今天在博客园上看到hellogiser写的随机数的生成问题,链接如下:
http://www.cnblogs.com/hellogiser/p/random-generator-with-equal-or-unequal-probability.html
说一下自己的简单理解
1.等概率生成
(1)rand5生成rand3。rand5()等概率生成[0,5)之间的整数,设计一个算法生成[0,3)之间的整数。
1 int rand3() 2 { 3 int x; 4 do 5 { 6 x=rand5(); 7 } 8 while(x>=3) 9 return x; 10 }
没什么好说的。rand5()等概率生成0,1,2,3,4,那么0,1,2自然也是等概率的。关于这个,我最开始的想法是3个rand5相加,然后对3取余。这个方法是不行的,3个random5相加产生的[0,15)之间整数并不是等概率的,所以对3取余生成的随机数也不是等概率的。这个题如果改为rand100生成rand3,上面的方法效率就非常低下,可以改为
1 int rand3() 2 { 3 int x; 4 do 5 { 6 x=rand100(); 7 } 8 while(x>=99) 9 return x%3;
(2)rand3生成rand5,ran5生成rand7等
1 int rand5() 2 { 3 int x; 4 do 5 { 6 x=rand3()*3+rand3(); 7 } 8 while(x>=5) 9 return x; 10 }
先说一下为什么rand3*3+rand3();这一步其实是要先产生[0,9)的随机数,之后就变成了(1)的形式。上面说过,多个rand3相加并不能产生等概率的随机数。这个方法很有意思,先将rand3*3,产生等概率的0,3,6,而0与3之间,3与6之间正好相差3,正好是一个rand3()的范围,所以rand3*3+rand3正好把0与3之间的部分给填上了,而且由于rand3产生的是等概率的,所以生成的也是等概率的,于是就变成了[0,9)之间的等概率随机数,接下来方法同(1)。而乘数3必须是rand3()的上界,这样正好可以填充完整。同理,rand5生成rand7,代码如下。
1 int rand7() 2 { 3 int x; 4 do 5 { 6 x=rand5()*5+rand5(); 7 } 8 while(x>=21) 9 return x%7; 10 }
为什么改为x>=21呢,同样是为了效率问题。
2.不等概率问题
(1)产生如下随机数。0出现1次,1出现2次,2出现3次……n-1出现n次。
采用两个rand(n)相加,还记得我上面说过的吧,产生的不是等概率的随机数。如rand3()+rand3(),结果为0,只有一种可能:两次rand3()都产生0。结果为1,有两种可能:第一个rand3()产生0,第二个产生1,或者反之。结果为2,结果有三种可能:一个rand3产生0,第二次rand3产生2,或者反之,还有一种可能,两次都产生1。依次类推,产生满足题意的随机数。代码如下
1 int random(int size) 2 { 3 int m,n; 4 do 5 { 6 m=rand(size); 7 n=rand(size); 8 } 9 while(m+n>=size) 10 return m+n; 11 }
(2)rand()以不等概率产生01,怎么等概率产生[0,n)的整数。
首先设计一个算法等概率产生01,代码如下
1 int rand1() 2 { 3 int m,n; 4 while(1) 5 { 6 m=rand(); 7 n=rand(); 8 if(m==0&&n==1) 9 { 10 return 1; 11 } 12 else if(m==1&&n==0) 13 { 14 return 0; 15 } 16 } 17 }
假设rand以概率p产生0,以概率1-p产生1,在以上代码中,m==0&&n==1的概率为p(1-p),m==1&&n==0的概率也是p(1-p),所以产生的01是等概率的。
然后由此设计算法产生[0,n)的随机数。记住多个rand1相加是行不通的.从二进制出发。产生01字符串作为结果的二进制。代码如下:
1 int rand(int size) 2 { 3 while(1) 4 { 5 int result=0; 6 for(int i=0;i<k;i++) 7 { 8 result|=(rand1()<<i); 9 } 10 if(result<n) 11 { 12 return result; 13 } 14 } 15 }