随机函数之等概率问题
编写一个函数,通过返回一个范围1至6的随机整数来模拟掷骰子,这6个值出现的概率应该相等。这个问题大多人做的时候,可能这样做:
程序代码:
#include<stdio.h> #include<stdlib.h> #include<time.h> int fun_throw(void) { //local declarations static int is_seeded=0; int value; //statements if(!is_seeded) { is_seeded=1; srand(time(NULL)); } //if value=rand(); return value%6+1; } //fun_throw int main() { //local declarations int num; int i; //statements printf("请输入要投掷的次数:"); scanf("%d",&num); printf("\n"); for(i=0;i<num;i++) printf("%6d\n",fun_throw()); return 0; } //main
其实这是错的。
随机数函数返回的最大值为32 767(这里就以它为例,标准C规定它至少为32 767),也就是说返回的数在0~32 767之间,总的个数为sum=32768.
为了得到1~6之间的随机整数,我们可以将一个随机数对6取模,得到0~5之间的整数,再将这个值加1便可以了。
在0~32 765之间对6取模得到各个值(0~5)的概率是相等的,每个数字有32 766/6=5461种情况,各个数字发生都是等概率的。但是32 766、32 767对6取模后得到的是0和1 ,加1后得到是1和2.
value=rand()%6+1;
由上面可知,在整个过程中,1和2出现的次数比其他四个数字(3,4,5,6)多了一次,即概率大。
数字1,2在整个过程中出现的概率为5462/32 768,而3,4,5,6出现的概率为5461/32 768.
虽然这个差别很小,但却是不容忽视的。因为这里产生的随机数范围很小而已,我们再看下面的:
如果需要函数产生1~30 000之间的随机数,同样我们可以通过:
value=rand()%30 000+1;
来做。
通过上面的分析我们可知,函数产生的随机数在0~29 999之间通过取模加1得到的是在1~30 000之间的数,它们都均出现一次,概率相等。
但是在后面的30 000~32 767之间,通过取模再加1得到的数在1~2768之间。
也就是说,如果我们利用这个函数试图产生一个范围在1~30 000之间的随机数时,前2768个值出现的概率是后面那些值出现概率的两倍。
前2768个值(1~2768)的概率相等,为:2/32 768.
后面值(2769~30 000)的概率为:1/32 768.
这种误差就大了。
正确的代码为:
程序代码:
#include<stdio.h> #include<stdlib.h> #include<time.h> #define MAX (int) ((((long)RAND_MAX+1)/6)*6-1) int fun_throw(void) {//local declarations static int is_seeded=0; int value; //statements if(!is_seeded) { is_seeded=1; srand(time(NULL)); } //if do { value=rand(); } while(value>MAX); return value%6+1; } //fun_throw int main() { //local declarations int num; int i; //statements printf("请输入要投掷的次数:"); scanf("%d",&num); printf("\n"); for(i=0;i<num;i++) printf("%6d\n",fun_throw()); return 0; } //main