我正在阅读C FAQ并在question中发现它建议我使用rand() / (RAND_MAX / N + 1)
而不是更流行的rand() % N
方式。
原因是当N
为低数时rand() % N
只会使用rand()
中的几位。
我在Windows和Linux上测试了N
为2
的不同方法,但未发现差异。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 2
int main(void)
{
srand(0);
printf("rand() %% N:\n");
for (int i = 0; i < 40; ++i) {
printf("%d ", rand() % N);
}
putchar('\n');
srand(0);
printf("rand() / (RAND_MAX / N + 1):\n");
for (int i = 0; i < 40; ++i) {
printf("%d ", rand() / (RAND_MAX / N + 1));
}
putchar('\n');
return 0;
}
输出是这个(在我的gnu / linux机器上):
rand() % N:
1 0 1 1 1 1 0 0 1 1 0 1 0 1 1 0 0 0 0 0 1 0 1 1 0 0 0 1 1 1 1 0 0 0 1 1 1 0 1 0
rand() / (RAND_MAX / N + 1):
1 0 1 1 1 0 0 1 0 1 0 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 1
这两种选择对我来说都是完全随机的。甚至看起来第二种方法比rand % N
更差。
我应该使用rand() % N
还是rand() / (RAND_MAX / N + 1)
?
答案 0 :(得分:6)
如果N
是2的幂,则使用余数技术通常是安全的(RAND_MAX
通常是2减1的幂,因此整个范围具有两个幂的幂)。更一般地说,N
必须划分rand()
的范围,以避免偏见。
否则,无论rand()
的质量如何,您都会遇到this problem。简而言之,问题在于你将该范围切割成长度为N
的多个“部分”,如果N
没有划分范围,那么最后一部分将不会完整。因此,从该部分“切断”的数字不太可能发生,因为它们可以从中生成更少的“部分”。
不幸的是rand() / (RAND_MAX / N + 1)
也被打破了(几乎以同样的方式),所以真正的答案是:不要使用其中任何一个。
如上所述的问题非常重要,除非Y除以X,否则无法在Y结果上均匀分布X个不同的值。您可以通过拒绝一部分随机样本来修复它,使Y除以新X
答案 1 :(得分:4)
rand() % n
存在另一个问题,即它引入了模偏差。
为简单起见,让我们假装RAND_MAX
为7,n为6.您希望数字0,1,2,3,4,5以相同的概率出现在随机流中。然而,0和1将出现1/4的时间而其他数字仅出现1/8的时间,因为6和7分别具有余数0和1。您应该使用其他方法,但要小心,因为截断分数可能会引入类似的问题。
如果您有arc4random(),则可以使用arc4random_uniform()
来实现无偏见的分发,而不必小心。
答案 2 :(得分:0)
On avr-gcc:
I was using rand() & 0xFF
to get random number from 0 to 255 and the results were not good. Turned out, that using lower bits is not very reliable method, often the same values. Could be similar with modulo.
rand() / (RAND_MAX / N + 1)
worked much better for me