rand * () 之间相互生成总结

先举一个之前看过的例子:(引自http://gaofen100.iteye.com/blog/1294993

给一个方法,比如 rand5(), 它能够等概率生成 1-5 之间的整数。 所谓等概率就是1,2,3,4,5 生产的概率均为 0.2 。现在利用rand5(), 构造一个能够等概率生成 1- 7 的方法?


思路:很多人的第一反应是return rand5()+rand5()%3; 这个实现确实能产生1-7之间的随机数,但是问题在于分布是不均匀的。rand5()%3产生0的概率是1/5,而产生1和2的概率都是2/5。所以这个实现产生6和7的概率要大于产生5的概率。 

  这里有两个特别重要的点,一是 如同上面思路,如果 rand5() + rand5(), 我们能够产生一个均匀分布的1 - 10 吗? 答案是否定的。比如对于 6来讲(4+2, 2+4, 3+3),它被生成的生成的概率比1 (1+0,0+1)要大.

  第二个点就是我们不可能用rand5()直接产生 1- 7 的数,不管你用加减乘除都不行。

所以,我们要构造一个更大的范围,使得范围里每一个值被生成的概率是一样的,而且这个范围是7的倍数,我们从下面的代码中来分析如何解决上面两个问题:

 

  1. int rand7() {  
  2.         while (1) {  
  3.                 int rand = 5 * (rand5() -1) ;  
  4.                 rand = rand + rand5() -1;  
  5.                 if (rand < 22 && ran > 0) {  
  6.                              return rand%7 + 1 ;  
  7.                 }  
  8.          }  
  9. }  

第三行代码产生一个均匀分布的 0, 5, 10, 15, 20的数。

第四行代码产生一个均匀分布的 0, 1, 2, 3, 4 的数。

相加以后,会产生一个 0到24的数,而且每个数(除0外)生成的概率是一样的。我们只取 1 - 21 这一段,和7 取余以后+1就能得到完全均匀分布的1-7的随机数了。

后来看到博客园另外一篇文章介绍的极其详细,果断学习之http://www.cppblog.com/hardtry/archive/2011/10/08/157556.html


学习之余,顺便探讨下rand()函数本身

函数rand()是真正的随机数生成器,而srand()会设置供rand()使用的随机数种子。函数rand()会返回一个处于0和你所指定的数值(缺省为1)之间的分数。如果你在第一次调用rand()之前没有调用srand(),那么系统会为你自动调用srand()。而使用同种子相同的数调用 srand()会导致相同的随机数序列被生成。

srand((unsigned)time(NULL))则使用系统定时/计数器的值做为随机种子,所以,在相同的平台环境下,显示的随机数会是伪随机数,即每次运行显示的结果会有不同

要注意的是所谓的“伪随机数”指的并不是假的随机数。其实绝对的随机数只是一种理想状态的随机数,计算机只能生成相对的随机数即伪随机数。计算机生 成的伪随机数既是随机的又是有规律的 —— 一部份遵守一定的规律,一部份则不遵守任何规律。比如“世上没有两片形状完全相同的树叶”,这正点到了事物的特性 —— 规律性;但是每种树的叶子都有近似的形状,这正是事物的共性 —— 规律性。从这个角度讲,我们就可以接受这样的事实了:计算机只能产生伪随机数而不是绝对的随机数。

下面是C库函数里面的rand()代码

 1 【C语言库函数rand()源代码】
 2 【本程序在Dev C 4.9.9.2 下编译通过】
 3 /*
 4    这两个函数是C库中产生随机数的程序。你需要先使用srand()函数赋随机数种子值。然后再使用 rand()函数来产生随机数。但是产生随机数的算法较简单,srandom()和random()函数是对这两个函数的改良,用法也很类似。
 5 */
 6 #define RANDOM_MAX 0x7FFFFFFF
 7  
 8 static long my_do_rand(unsigned long *value)
 9 {
10    /*
11       这个算法保证所产生的值不会超过(2^31 - 1)这里(2^31 - 1)就是 0x7FFFFFFF。而 0x7FFFFFFF等于127773 * (7^5) 2836,7^5 = 16807。整个算法是通过:t = (7^5 * t) mod (2^31 - 1)这个公式来计算随机值,并且把这次得到的值得到并且计算,作为下次计算的随机种子值。
12    */
13    long quotient, remainder, t;
14  
15    quotient = *value / 127773L;
16    remainder = *value % 127773L;
17    t = 16807L * remainder - 2836L * quotient;
18    if (t <= 0)
19       t = 0x7FFFFFFFL;
20    return ((*value = t) % ((unsigned long)RANDOM_MAX 1));
21 }
22 static unsigned long next = 1;
23 int my_rand(void)
24 {
25    return my_do_rand(&next);
26 }
27  
28 void my_srand(unsigned int seed)
29 {
30    next = seed;
31 }
32  
33 #include <time.h>
34 int main()
35 {
36    int i;  
37    my_srand((unsigned)(time(NULL)));
38    for(i=0;i<100;i )
39    {
40       if(i % 10 == 0)
41          printf("\n");
42       printf("%d\t",my_rand()%99 1);
43    }
44    system("pause");
45    return 0;
46 }
47  

rand()和random()的区别:

int rand(void):返回 0 ------RAND_MAX 之间的一个 int 类型整数,该函数为非线程安全函数。并且生成随机数的性能不是很好,已经不推荐使用

long int random(void):返回 0 ------- RAND_MAX 之间的一个 long 类型整数,该函数会产生一个非常大的随机值,最大为 16*((2**31)-1)。random 函数使用非线性反馈随机数发生器生成默认大小为31个长整数表所返回的连续伪随机数。

 

如果你使用 srandom 种植种子, 则你应该使用 random 返回随机数, 如果你使用 srand 种植种子, 则你应该使用rand返回随机数。

不过srand和rand官方已经不推荐使用。原因是产生随机数的性能不是很好, 另外是随机数的随机性没有random好, 再者就是不是线程安全。

 

posted @ 2012-04-24 15:51  zzuxiaolei  阅读(2146)  评论(1编辑  收藏  举报