随机数、随机函数、大数随机及等概率探讨

openkk 12年前
   <p>        近日在做一个入职练习中,我遇到了随机数的问题,将分析过程做些整理。</p>    <p><strong>        本文主要讨论大范围内随机数的产生办法,讨论在随机范围内的等概率问题。</strong></p>    <p><strong>        一、要求</strong></p>    <p><strong>        1、产生一个比较大的随机数。</strong></p>    <p><strong>        2、产生的随机数在随机范围内等概率。</strong></p>    <p style="text-align:center;"><img title="随机数、随机函数、大数随机及等概率探讨" border="0" alt="随机数、随机函数、大数随机及等概率探讨" src="https://simg.open-open.com/show/37dfb008e6e9cafe0179beb6110caf26.jpg" width="400" height="321" /></p>    <p>        <strong>二、知识背景</strong></p>    <p>        我们知道在C语言中有 rand ()函数可以提供随机数,rand ()函数的范围为 0 到 32727。我们假定认为 rand ()产生的随机数在 0 到 32727 范围内是等概率的。如果我们需要得到一个小范围内的随机数,比如 0 到 55 之间的随机数,那我们可以采用 rand ()%55。但是对于我们要得到一个更大范围内的随机数,rand ()便满足不了我们的要求。</p>    <p>        <strong>三、探讨过程</strong></p>    <p>        <strong>1、两个 rand 相乘</strong></p>    <p>        假设我们要产生一个 10 亿内的随机数,想到 rand ()可以产生 0 到 32727,那么我们可以采用 rand ()*rand (),刚好能达到 10 亿的范围。</p>    <p>        可是我们不难发现 rand ()*rand ()会有问题,最大的问题是在规定范围内产生的随机数概率不等,比如一个大于 32727 的素数,就永远产生不了。而对于很多合数,出现的频率会非常高。</p>    <p>        <strong>2、按位组合</strong></p>    <p>        首先我们找到上限数字的位数,然后对每一位产生一个 0 到 9 的随机数,并将产生的一系列 0 到 9 的数字组合起来。假设我们要产生一个 10 亿内的随机数,也就是我们需要产生 0 到 999999999 之间的随机数,我们首先求得 999999999 的位数是 9 位,然后我们产生 9 个数字,并将他们组合成一个 9 位数。比如:872345671,023478652。</p>    <p>        看上去没有什么问题,我们很好地解决了一个特别的随即范围,即 10 亿内。假如我们现在要产生一个 60000 内的随机数,也就是需要产生一个 0 到 59999 之间的数。如果我们按照上述办法,如果产生的数字大于 59999,同时也是 5 位数,比如 97863,我们该怎么办?</p>    <p>        <strong>3、求余法</strong></p>    <p>        我们最先想到的是,如果产生的数字(98763)对范围(60000)求余,对一个数字求余,所得到的结果肯定是落在该数字的范围内。</p>    <p>        不难发现,我们这里同样有概率问题。对于 40000 到 60000 之间的数字,出现的概率为1/100000,对于 0 到 40000 之间的数字,出现的概率为2/100000,因此概率不等。</p>    <p>        <strong>4、逐位检验法</strong></p>    <p>        我们将上限数字的逐位取出来,我们逐个产生 0 到该数字的随机数。对于产生 0 到 59999 只的随机数,我们先取第一位:5,我们产生一个 0 到 5 之间的随机数,第二位:9,我们产生 0 到 9 之间的随机数,最终组合出的 5 位则是 0 到 59999 之间。</p>    <p>        我们发现,这也只能解决特殊的数字范围。如果我们要产生一个 0 到 51782 之间的随数,这个方法就失效了。比如 33216 这个数字就产生不了,因为 33216 第二位 3 比范围(51782)第二位 1 大,永远产生不了。</p>    <p>        <strong>5、丢弃法</strong></p>    <p>        同样地,我们首先依然采用组合法产生一个规定位数的数据,如果我们发现我们产生的数字在我们的范围之外,那我们选择丢弃该数据,继续产生随机数,一直到我们产生我们在范围内的随机数。不难证明,丢弃一个不正确的数字本身并不影响产生正确数字的概率。</p>    <p>        因此,<strong>采用组法+丢弃法能满足我们的要求</strong>。</p>    <p>        这里只讨论了随机数的上线,对于随机数的下限同理。</p>    <p style="text-align:center;"><img title="随机数、随机函数、大数随机及等概率探讨" border="0" alt="随机数、随机函数、大数随机及等概率探讨" src="https://simg.open-open.com/show/5cb646e415c65a16cac638cea10323bc.jpg" width="400" height="359" /></p>    <p>        <strong>四、源码实现(C语言)</strong></p>   <pre class="brush:cpp; toolbar: true; auto-links: false;"><strong>//产生一个 0 到 9 的随机数  static __inline int min_rand ()  {  return rand ()%10;  }    /*************************************************************/  /* 函数作用:产生一个 range 范围内的随机数 */  /* 参数1,range:取随机数的范围 */  /* 返回:返回取得的数据 */  /*************************************************************/  int my_rand (const int range)  {  short bit = 0; //纪录位数  int tempt = range;  int rand_data = 0;    while ( tempt > 0 )  {  bit++;  tempt = tempt/10;  }    while (bit–)  rand_data = 10*rand_data + min_rand ();//组合随机数    if (rand_data >= range)  return my_rand (range);//产生随机数不符合范围,继续    return rand_data;  } </strong></pre>    <p></p>    <div id="come_from">    来自:     <a id="link_source2" href="/misc/goto?guid=4958340622892139798" target="_blank">hp.dewen.org</a>    </div>