先举一个之前看过的例子:(引自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的倍数,我们从下面的代码中来分析如何解决上面两个问题:
- int rand7() {
- while (1) {
- int rand = 5 * (rand5() -1) ;
- rand = rand + rand5() -1;
- if (rand < 22 && ran > 0) {
- return rand%7 + 1 ;
- }
- }
- }
第三行代码产生一个均匀分布的 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好, 再者就是不是线程安全。