为什么此代码会生成均匀分布的数字?我理解它有些困难。有人能解释一下吗感谢。
int RandomUniform(int n) {
int top = ((((RAND_MAX - n) + 1) / n) * n - 1) + n;
int r;
do {
r = rand();
} while (r > top);
return (r % n);
}
更新:我明白为什么rand()%n不会给你一个均匀分布的序列。我的问题是为什么
top = ((((RAND_MAX - n) + 1) / n) * n - 1) + n;
这里有什么问题?我认为一个简单的顶部= RAND_MAX / n * n就可以了。
答案 0 :(得分:10)
该函数假定rand()
均匀分布;这是否是一个有效的假设取决于rand()
的实现。
给定统一rand()
,我们可以通过计算[0,n)
得到rand()%n
范围内的随机数。但是,一般来说,这不会很均匀。例如,假设n
为3而RAND_MAX
为7:
rand() 0 1 2 3 4 5 6 7
rand() % n 0 1 2 0 1 2 0 1
我们可以看到0和1的概率为3/8,而2只出现2/8的概率:分布不均匀。
您的代码会丢弃rand()
的任何值,该值大于或等于它可以生成的n
的最大倍数。现在每个值的概率相等:
rand() 0 1 2 3 4 5 6 7
rand() % n 0 1 2 0 1 2 X X
所以0,1和2都有1/3的概率,只要我们不那么不幸,循环永远不会终止。
关于您的更新:
我认为一个简单的顶部= RAND_MAX / n * n就可以了。
如果RAND_MAX
是一个独占界限(比实际最大值多一个),那么这是正确的。由于它是一个包容性的界限,我们需要添加一个来获得独占界限;并且由于以下逻辑与包含边界的>
进行比较,因此在计算后再次减去一个:
int top = ((RAND_MAX + 1) / n) * n - 1;
但是,如果RAND_MAX
等于INT_MAX
,则计算会溢出;为避免这种情况,请在计算开始时减去n
,并在结尾处再次添加:
int top = (((RAND_MAX - n) + 1) / n) * n - 1 + n;
答案 1 :(得分:7)
基本问题是:假设您有一个随机数生成器my_rand()
,它生成0到6(含)的值,并且您希望生成0到5之间的值,包括0和5;如果您运行生成器并返回my_rand() % 6
,则无法获得统一分布。当my_rand()
返回0时,您得到0;当它返回1时,你得到1,等等,直到my_rand()
返回6;在这种情况下my_rand() % 6
为0.总的来说,my_rand() % 6
将返回0的频率是任何其他值的两倍。解决此问题的方法是不使用大于5的值,即代替my_rand() % 5
编写循环并丢弃my_rand()
中过大的值。这基本上就是问题中的代码所做的事情。我没有追踪它,但通常的实现是计算n
的最大倍数小于或等于RAND_MAX
,并且只要rand()
返回的值大于{{1}}多重,返回并获得新值。
答案 2 :(得分:2)
我没有追踪计算top的代码,但RAND_MAX
是rand()
可以返回的最大值; (RAND_MAX + 1) / n * n
会有更好的上限,但如果RAND_MAX
是INT_MAX
,则结果将无法预测。所以也许所有代码都试图避免溢出。