我应该使用" rand%N"或" rand()/(RAND_MAX / N + 1)"?

时间:2014-12-30 13:49:01

标签: c algorithm random numbers

我正在阅读C FAQ并在question中发现它建议我使用rand() / (RAND_MAX / N + 1)而不是更流行的rand() % N方式。

原因是当N为低数时rand() % N只会使用rand()中的几位。

我在Windows和Linux上测试了N2的不同方法,但未发现差异。

#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)

3 个答案:

答案 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