今天在博客园上看到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 }