您的位置:首页 > 职场人生

一道面试题引发的有关随机数的思考(2)

2011-09-29 21:59 711 查看
上一篇文章(/article/9566997.html)我们已经按照面试题的要求完成了Rand10的功能。

在文章的结尾处,猜测了有可能有其他的方法去实现Rand10.

那么这篇文章就来说说如何去实现10=5*2(5个集合,每个集合有2个元素)和12=2*6(2个集合,每个集合有6个元素)

既然我们知道有代码重构这个概念,我想有必要代码重构下!Let's Go

首先一个所有随机数的抽象类

Rand

//抽象类
//随即数范围是[1, MaxNum]
//注:[a,b],表示包括a也包括b
//(a,b],表示不包括a但是包括b
//数学中闭合的关系
public abstract class Rand
{
//获取下一个随机数
public abstract int Next();
//随机数类的实例
protected static Rand rand;
//指明的最大数
protected int _maxNum;
public int MaxNum
{
get
{
return _maxNum;
}
}
}
Rand7:

//1~7的随机数产生类
public class Rand7 : Rand
{
private readonly Random _random = new Random();
private Rand7()
{
_maxNum = 7;
}
public static Rand7 GetInstance()
{
if (rand == null || !(rand is Rand7))
{
rand = new Rand7();
}
return (Rand7)rand;
}
//获得随机数
override public int Next()
{
return _random.Next(1, 8);
}
}


以Rand7为基础计算其他数的随机数的一个基类:
Rand7Base:

//该类的作用表示其他随机数是以Rand7为基础
public abstract class Rand7Base : Rand
{
protected Rand7 _rand7 = Rand7.GetInstance();
}


把上一篇文章Rand10和Rand12类重构如下:(注意基类)

//1~10的随机数产生类
public class Rand10 : Rand7Base
{
private Rand10()
{
_maxNum = 10;
}
public static Rand10 GetInstance()
{
if (rand == null || !(rand is Rand10))
{
rand = new Rand10();
}
return (Rand10)rand;
}
//2个集合,每个集合5个元素
//(1,3,5,7,9)(2,4,6,8,10)
override public int Next()
{
int num;
//均匀产生1、 2 、3、4、5
while (true)
{
num = _rand7.Next();
if (num <= 5)
break;
}

while (true)
{
int n = _rand7.Next();
if (n == 1)
continue;
//n大于4的数字有5、6、7,因为是由Rand7产生的,所以概率均匀
if (n > 4)
//因为num只可取值1、2、3、4、5并且取值概率均匀,num*2可得2、4、6、8、10也概率均匀
num *= 2;
//n小于4的数字有1、2、3,因为是由Rand7产生的,所以概率均匀
else
//因为num只可取值1、2、3、4、5并且取值概率均匀,num*2-1可得1、3、5、7、9也概率均匀
num = num * 2 - 1;
break;
}
return num;
}
}

public class Rand12 : Rand7Base
{
private Rand12()
{
_maxNum = 12;
}
public static Rand12 GetInstance()
{
if (rand == null || !(rand is Rand12))
{
rand = new Rand12();
}
return (Rand12)rand;
}
//获得随机数
//3个集合,每个集合有4个数据
//(3,6,9,12)(2,5,8,11)(1,4,7,10)
override public int Next()
{
//3*4
int num;
while (true)
{
num = _rand7.Next();
if (num <= 4)
break;
}

while (true)
{
int n = _rand7.Next();
if (n == 1)
continue;
if (n > 5)
num *= 3;
else if (n > 3)
num = num * 3 - 1;
else
num = num * 3 - 2;
break;
}
return num;
}
}


测试类中:

[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
static extern bool QueryPerformanceCounter(ref long count);
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
static extern bool QueryPerformanceFrequency(ref long count);
//测试随即数产生的概率
static void SubTestRand(Rand rand)
{
long total = 10000000;

long count = 0;
long count1 = 0;
long freq = 0;
double result = 0;
QueryPerformanceFrequency(ref freq);
QueryPerformanceCounter(ref count);

int[] numArray = new int[rand.MaxNum];
double[] doubleResult = new double[rand.MaxNum];
for (long i = 0; i < total; i++)
{
int randomNumber = rand.Next();
numArray[randomNumber - 1]++;
}

//打印产生各数的概率
for (int i = 0; i < numArray.Length; i++)
{
double res = (Double)numArray[i] / total;
doubleResult[i] = res;
string eachResult = string.Format("产生{0}的概率是:{1:0.0000000}", i + 1, res);
Console.WriteLine(eachResult);
}

QueryPerformanceCounter(ref count1);
count = count1 - count;
result = (double)(count) / (double)freq;
string spendTime = string.Format("耗时: {0} 秒", result);
Console.WriteLine(spendTime);

double average = (double)1 / doubleResult.Length;
double max = doubleResult[0];
double min = doubleResult[0];
for (int i = 0; i < doubleResult.Length; i++)
{
max = max > doubleResult[i] ? max : doubleResult[i];
min = min < doubleResult[i] ? min : doubleResult[i];
}
string totalMes = string.Format("Average:{0:0.0000000};Max:{1:0.0000000};Min:{2:0.0000000}", average, max, min);
string maxMes = string.Format("Max:{0:0.0000000}, Max-Average:{1:0.0000000}, Bits:{2:0.0000000}%",
max, max - average, (max - average) / average * 100);
string minMes = string.Format("Min:{0:0.0000000}, Min-Average:{1:0.0000000}, Bits:{2:0.0000000}%",
min, min - average, (min - average) / average * 100);

Console.WriteLine(totalMes);
Console.WriteLine(maxMes);
Console.WriteLine(minMes);
}
static void TestRand(Rand rand)
{
Console.WriteLine(string.Format("计算Rand{0}的概率如下", rand.MaxNum));
SubTestRand(rand);
}


如果要测试Rand10和Rand12可以添加如下函数:

static void TestRand10()
{
Rand10 rand = Rand10.GetInstance();
TestRand(rand);
}
static void TestRand12()
{
Rand12 rand = Rand12.GetInstance();
TestRand(rand);
}


在主函数中可以调用:

static void Main(string[] args)
{
TestRand10();
TestRand10();
Console.ReadLine();
}


可以通过注释主函数中的代码来测试任何一个。

以上这么多内容只是说了重构,现在我们来实现10=5*2(5个集合,每个集合有2个元素)和12=2*6(2个集合,每个集合有6个元素)。

实现10=5*2, 假设类名为:Rand10_2(表示Rand10的第二种实现方案)

public class Rand10_2:Rand7Base
{
private Rand10_2()
{
_maxNum = 10;
}
public static Rand10_2 GetInstance()
{
if (rand == null || !(rand is Rand10_2))
{
rand = new Rand10_2();
}
return (Rand10_2)rand;
}
//5个集合,每个集合2个元素
//(1,6)(2,7)(3,8)(4,9)(5,10)
override public int Next()
{
int num;
//均匀产生1、 2 、3、4、5
while (true)
{
num = _rand7.Next();
if (num <= 2)
break;
}

while (true)
{
int n = _rand7.Next();
if (n > 5)
continue;
//n是从1到5
//num刚开始只是1或者2
//所以要-1
num = 5 * num - n + 1;
break;
}
return num;
}
}
在测试类添加如下函数:

//此函数的作用是表示随机数的其他方法
static void TestRand(Rand rand, string message)
{
Console.WriteLine(string.Format("计算{0}的概率如下", message));
SubTestRand(rand);
}
static void TestRand10_2()
{
Rand10_2 rand = Rand10_2.GetInstance();
TestRand(rand, "Rand10_2");
}


同样的实现:12=2*6(2个集合,每个集合有6个元素),Rand12_2

public class Rand12_2 : Rand7Base
{
private Rand12_2()
{
_maxNum = 12;
}
public static Rand12_2 GetInstance()
{
if (rand == null || !(rand is Rand12_2))
{
rand = new Rand12_2();
}
return (Rand12_2)rand;
}
//2个集合,每个集合有6个数据
//(2,4,6,8,10,12)(1,3,5,7,9,11)
override public int Next()
{
int num;
while (true)
{
num = _rand7.Next();
if (num <= 6)
break;
}

while (true)
{
int n = _rand7.Next();
if (n == 1)
continue;
if (n > 4)
num *= 2;
else
num = num * 2 - 1;
break;
}
return num;
}
}


在测试类添加测试代码:

static void TestRand12_2()
{
Rand12_2 rand = Rand12_2.GetInstance();
TestRand(rand, "Rand12_2");
}


更新测试类的主函数

static void Main(string[] args)
{
TestRand10();
TestRand10_2();
TestRand12();
TestRand12_2();
Console.ReadLine();
}


运行,得到的结果是:

计算Rand10的概率如下
产生1的概率是:0.0999950
产生2的概率是:0.1000683
产生3的概率是:0.1000904
产生4的概率是:0.0998266
产生5的概率是:0.1000829
产生6的概率是:0.0999189
产生7的概率是:0.1000285
产生8的概率是:0.0999484
产生9的概率是:0.0999287
产生10的概率是:0.1001123
耗时: 2.23924715284627 秒
Average:0.1000000;Max:0.1001123;Min:0.0998266
Max:0.1001123, Max-Average:0.0001123, Bits:0.1123000%
Min:0.0998266, Min-Average:-0.0001734, Bits:-0.1734000%
计算Rand10_2的概率如下
产生1的概率是:0.1001656
产生2的概率是:0.0998927
产生3的概率是:0.1000315
产生4的概率是:0.1000447
产生5的概率是:0.1000213
产生6的概率是:0.0998959
产生7的概率是:0.0999171
产生8的概率是:0.0998640
产生9的概率是:0.1000109
产生10的概率是:0.1001563
耗时: 4.63440941163983 秒
Average:0.1000000;Max:0.1001656;Min:0.0998640
Max:0.1001656, Max-Average:0.0001656, Bits:0.1656000%
Min:0.0998640, Min-Average:-0.0001360, Bits:-0.1360000%
计算Rand12的概率如下
产生1的概率是:0.0832689
产生2的概率是:0.0833657
产生3的概率是:0.0832885
产生4的概率是:0.0833306
产生5的概率是:0.0833956
产生6的概率是:0.0832436
产生7的概率是:0.0833872
产生8的概率是:0.0833305
产生9的概率是:0.0832966
产生10的概率是:0.0834083
产生11的概率是:0.0834503
产生12的概率是:0.0832342
耗时: 2.61100502823934 秒
Average:0.0833333;Max:0.0834503;Min:0.0832342
Max:0.0834503, Max-Average:0.0001170, Bits:0.1403600%
Min:0.0832342, Min-Average:-0.0000991, Bits:-0.1189600%
计算Rand12_2的概率如下
产生1的概率是:0.0834729
产生2的概率是:0.0832840
产生3的概率是:0.0833406
产生4的概率是:0.0834923
产生5的概率是:0.0833294
产生6的概率是:0.0830786
产生7的概率是:0.0834936
产生8的概率是:0.0833099
产生9的概率是:0.0834100
产生10的概率是:0.0832989
产生11的概率是:0.0832233
产生12的概率是:0.0832665
耗时: 2.46493227776721 秒
Average:0.0833333;Max:0.0834936;Min:0.0830786
Max:0.0834936, Max-Average:0.0001603, Bits:0.1923200%
Min:0.0830786, Min-Average:-0.0002547, Bits:-0.3056800%


以上可以看出误差范围都是在-0.2%到0.2%之间。

我想这个随机数是可以接受了。

既然10和12都可以通过7来获得,那么11该如何通过7来获得了? 下一篇文章我们来介绍如何通过Rand7来得到Rand11.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: