在上一篇文章(http://blog.csdn.net/xzjxylophone/article/details/6835332)中,我们主要完成了这个C#工程的重构和Rand10,Rand12的另一种实现。
这次我们来实现如果用Rand7来产生一个Rand11.
按照以前的思路计算Rand10的时候是分成2个集合(1,3,5,7,9)(2,4,6,810)
那么我们是否可以把11也分成2个集合(1,3,5,7,9,11)和(2,4,6,8,10);
按照10的思路,在Rand10.Next()函数中应该把代码:
if (n > 4) num *= 2; else num = num * 2 - 1;
if (n > 4) num = 2*num-1;//1,3,5,7,9,11 else num = 2 * Rand5;//此处的Rand5表示随即产生一个1-5的一个数
Rand5
public class Rand5 : Rand7Base { private Rand5() { _maxNum = 5; } public static Rand5 GetInstance() { if (rand == null || !(rand is Rand5)) { rand = new Rand5(); } return (Rand5)rand; } //获得随机数 override public int Next() { int num; //均匀产生1、 2 、3、4、5 while (true) { num = _rand7.Next(); if (num <= 5) return num; } } }按照前述的思路完成Rand11:
public class Rand11 : Rand7Base { private Rand11() { _maxNum = 11; } public static Rand11 GetInstance() { if (rand == null || !(rand is Rand11)) { rand = new Rand11(); } return (Rand11)rand; } //获得随机数 override public int Next() { int num; label: while (true) //代码块1 { num = _rand7.Next(); if (num <= 6) break; } //1<=num<=6 //1.3.5.7.9.11 num*2-1 //2.4.6.8.10 num*2 while (true) { int n = _rand7.Next(); if (n == 1) continue; if (n > 4) num = num * 2 - 1; // 代码块2 else { num = 2 * Rand5.GetInstance().Next();//随即获取1-5,保证num为(2,4,6,8,10) //代码块3 } break; } return num; } }
static void TestRand5() { Rand5 rand = Rand5.GetInstance(); TestRand(rand); } static void TestRand11() { Rand11 rand = Rand11.GetInstance(); TestRand(rand); }测试Rand5和Rand11得到的测试结果:
计算Rand5的概率如下 产生1的概率是:0.1998812 产生2的概率是:0.2001405 产生3的概率是:0.1998788 产生4的概率是:0.2001452 产生5的概率是:0.1999543 耗时: 1.35574716655683 秒 Average:0.2000000;Max:0.2001452;Min:0.1998788 Max:0.2001452, Max-Average:0.0001452, Bits:0.0726000% Min:0.1998788, Min-Average:-0.0001212, Bits:-0.0606000% 计算Rand11的概率如下 产生1的概率是:0.0833986 产生2的概率是:0.0998455 产生3的概率是:0.0832477 产生4的概率是:0.1000255 产生5的概率是:0.0832922 产生6的概率是:0.1000058 产生7的概率是:0.0833212 产生8的概率是:0.0999634 产生9的概率是:0.0834429 产生10的概率是:0.1001360 产生11的概率是:0.0833212 耗时: 2.95999916630916 秒 Average:0.0909091;Max:0.1001360;Min:0.0832477 Max:0.1001360, Max-Average:0.0092269, Bits:10.1496000% Min:0.0832477, Min-Average:-0.0076614, Bits:-8.4275300%Rand5是正确的。Rand11的误差范围居然在-8%----10%这里,说明Rand11是有问题。
重新观察Rand10和Rand11.在Rand10中虽然分配了2个集合,但是每个集合的元素都是相同的,但是在Rand11中2个集合,一个集合是6个元素,一个集合是5个元素,问题肯定就出现在这里。
注意到程序运行到代码块2和代码块3的概率都是一样的都为1/2
只不过概率1/2被6个元素给分了(1,3,5,7,9,11),而剩下的1/2被剩余的5个元素分了(2,4,6,8,10)
所以在输出的结果中可以看到P(1),P(3),P(5),P(7),P(9),P(11)的概率都约等于1/12而其他数的概率约等于1/10。
现在可以这样来考虑,为了让1-11这11个数都是等概率的得到,可以把概率1看成一块蛋糕分给11个人,这个该如何分了?
可以这样来分:先平均分成12份,每个人拿一份,最后剩下一份,然后把剩下的那一份再平均分12份,依次类推。
现在我们用数学知识来计算一个人(A)到底分配了多少蛋糕:
步骤1:第一次分12份,分给11人后,A得到了1/12,还剩下1/12
步骤2:把步骤1剩下的一份在分成12份,分给11人后, A得到了1/12 * 1/12,剩余1/12 * 1/12
步骤3:把步骤2剩余的一份在分成12份,分给11人后, A得到 1/12 * pow(1/12, 2) //pow(a,b)表示a的b次方
依次类推
可的A得到的蛋糕:P(A)=1/12 + 1/12 * 1/ 12 + 1/12 * pow(1/12,2) + ..... + 1/ 12 * pow(1/12, n)= (1/12) * (1- pow(1/12, 2)) / (1 - 1/12) //等比数列计算公式 = (1/12 ) / (11/12) = 1 / 11
结果为1/11是我们想要的结果,那么可以根据依然的想法来编写程序:
Rand11_2
public class Rand11_2 : Rand7Base { private Rand11_2() { _maxNum = 11; } public static Rand11_2 GetInstance() { if (rand == null || !(rand is Rand11_2)) { rand = new Rand11_2(); } return (Rand11_2)rand; } //获得随机数 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 = num * 2 - 1;//1,3,5,7,9,11 else { //当num = 6的时候表示需要继续的划分蛋糕 if (num == 6) { num = Next(); } else { num = 2 * num; } } break; } return num; } }
此次测试Rand11和Rand11_2,这样可以进行比较:
计算Rand11的概率如下 产生1的概率是:0.0831913 产生2的概率是:0.1001187 产生3的概率是:0.0833342 产生4的概率是:0.0999278 产生5的概率是:0.0834714 产生6的概率是:0.1000888 产生7的概率是:0.0833103 产生8的概率是:0.1001032 产生9的概率是:0.0832276 产生10的概率是:0.1000561 产生11的概率是:0.0831706 耗时: 3.28849131413559 秒 Average:0.0909091;Max:0.1001187;Min:0.0831706 Max:0.1001187, Max-Average:0.0092096, Bits:10.1305700% Min:0.0831706, Min-Average:-0.0077385, Bits:-8.5123400% 计算Rand11_2的概率如下 产生1的概率是:0.0908736 产生2的概率是:0.0908268 产生3的概率是:0.0908969 产生4的概率是:0.0909283 产生5的概率是:0.0909252 产生6的概率是:0.0908479 产生7的概率是:0.0908451 产生8的概率是:0.0909375 产生9的概率是:0.0909681 产生10的概率是:0.0910002 产生11的概率是:0.0909504 耗时: 2.48036752188162 秒 Average:0.0909091;Max:0.0910002;Min:0.0908268 Max:0.0910002, Max-Average:0.0000911, Bits:0.1002200% Min:0.0908268, Min-Average:-0.0000823, Bits:-0.0905200%
既然Rand11_2可以用这样的方法去解决了,那么Rand10或者Rand12能不能用类似的思路去解决这个问题了?
下一篇我们来尝试用这种思路实现Rand10.