如何在C语言中以“相同的概率”从0到N-1获取一个随机数?
如何在C语言中以“相同的概率”从0到N-1获取一个随机数?
我知道这可能是一个“老问题”,但我想重点关注概率。
我的第一个问题是:
在C语言中,rand()
会给出一个从0
到RAND_MAX
的数字,这个区间中的每个数字被rand()
选择的概率相同吗?
第二个问题:
如果rand()
让从0
到RAND_MAX
的每个数字具有相同(或近似相同)的选择概率,那么当我想要从0到N-1(N-1 < RAND_MAX)获取一个随机数时,通常会这样做:
rand()%N
但是,如果RAND_MAX
不是N的倍数,从0到N-1选择的随机数的概率可能不同。
例如,假设RAND_MAX=150,N=100,当我执行rand()%100
时,从0到49的数字被选择的概率会比从50到99的数字更高,因为150不是100的倍数。
在C语言中,是否有一种算法或函数可以让每个随机数被选择的概率相同?
问题的原因:在C语言中,使用rand()
函数生成的随机数在0到RAND_MAX
之间,但是这些随机数并不是完全均匀分布的,可能存在一定的偏差。
解决方法:为了获得0到N-1之间的随机数,可以使用如下的方法:
1. 首先,判断N是否小于等于RAND_MAX
,如果不满足,需要使用其他的解决方案。
2. 计算rmax
的值,公式为:rmax = RAND_MAX - (RAND_MAX % N) - 1
。这个值表示了在生成随机数时需要舍弃的范围。
3. 使用一个循环,每次调用rand()
函数生成一个随机数,并判断这个随机数是否大于rmax
,如果大于,则需要重新生成随机数,直到生成的随机数小于等于rmax
为止。
4. 最后,将生成的随机数模上N,即可得到一个均匀分布在0到N-1之间的随机数。
示例:假设RAND_MAX
为32767,N
为100,那么rmax
的值将为32699。在32700到32767的范围内生成的随机数将会被舍弃,需要重新生成随机数,这样可以消除%N
操作带来的偏差。
需要注意的是,这种方法并不能解决rand()
函数本身的不足之处。C语言并没有规定rand()
函数的质量,只要求它生成的值在0到RAND_MAX
之间,而RAND_MAX
至少为32767。对于大于RAND_MAX
的N值,需要使用其他的解决方案。
问题的原因是rand()函数生成的随机数并不是真正的随机数,而是依赖于系统提供的随机数生成方式,因此无法确定结果的真正随机性。如果需要非常重要的随机性,最好使用涉及硬件的解决方案。另外,在使用模运算时,会导致结果偏向某些数值,从而引入偏差。解决这个问题的方法是将随机数转换为浮点数,通过除以随机生成器能提供的最大值,然后乘以你希望处理的数值范围的个数。如果你的范围不是从0开始的,还需要加上你期望的基准值。C语言的规范没有规定特定的实现方式,但是对rand/srand函数的约束几乎可以保证它是一个线性同余生成器(LCG)。但最重要的是,通过浮点数回到整数仍然会给出有偏差的结果,尽管不那么明显。