本文基于V站上一个大佬的博文,在此之上的一些启发与思考。
在此先对原作者致敬。
前置
基本上的编程语言的rand函数的实现原理是,通过一个算法,得出一个序列,影响序列生成的因素压缩成若干个变量。
换句话说,若作为影响因素的参数不变,rand所得的随机数是固定的。
为了达到我们 “ 随机 ” 的目的,我们将当前时间作为参数,使得序列有一定的随机性。
关于rand函数的实现,wiki上有详细介绍,传送门
错误的使用
假设我们现在的rand函数是完全意义上的随机。我现在要求一个 [ 0,N ) 的随机数。
一般人包括我自己都是这样使用的
srand(time(NULL));
int r = rand()%N;
但实际上存在一个疏忽的地方,因为存在取模,所以 [ 0, RAND_MAX %N ) 被随机到的概率会比 [ RAND_MAX%N , N )多一次。
而我们严格意义上的随机数是均匀分布的,也就是说这是不被我们所允许的。
尝试解决
其实解决方法很简单,问题就只是出在 [ RAND_MAX – RAND_MAX %N , RAND_MAX ] 这一段影响区间,所以我们只要把它去掉就好了。
而将之去掉的可以尝试
- 更改RAND_MAX,使得其为 N 的整数倍
- 多随机几次,当随机结果不在这个区间的就输出我们的结果。
第一种方法我尝试了一下,貌似不行……,应该是我不会,因为RAND_MAX 被定义在 stdlib.h 头文件中,头文件不允许修改,在当前文件中宏定义显然不能将之覆盖,说加 extern 的麻烦去把 C 学好再说实际上我也试了一下 ,gcc编译选项? 貌似只能添加不能指定吧……
总之第一种方法显示了我对语言了解的短板,我个人解决不了。
对于第二种方法,显然是很好实现的。
srand(time(NULL));
unsigned long long r , limit = RAND_MAX - RAND_MAX %N ;
do {
r = rand();
} while(r>=limit);
return r / (RAND_MAX / N);
结尾
虽然说这么多,然而实际上并没多大意义,因为rand本身的误差就挺大的……
另一方面,RAND_MAX 本身在我们平时的使用就挺大的,而RAND_MAX越大,区间长度越小,误差就越小。稍微想一下就是对的所以在我们平时的应用上就没有什么感觉。
如果你有一个悠闲的下午去实现了一个rand,可以试试另 RAND_MAX = 3 , 而 N = 2 ,统计一下 0,1的数量,就更显然了。