【问题标题】:Should I use "rand % N" or "rand() / (RAND_MAX / N + 1)"?我应该使用“rand % N”还是“rand() / (RAND_MAX / N + 1)”?
【发布时间】:2015-02-26 16:32:08
【问题描述】:

我正在阅读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)

【问题讨论】:

  • 这不是您在这种情况下注意到差异的方式。你的错误是这样的:“这两种选择对我来说都是完全随机的”。您需要一种分析方法。可以这样想:真正衡量这一点的唯一方法是获取无限长序列的一些统计数据。
  • 这是#define N 2 的一个糟糕示例,因为RAND_MAX 是一个奇数,而rand() % N 将返回0 和1 的相等分布,因为伪随机生成器的限制。这里的两种方法绝对没有区别,只是在伪随机结果的顺序上。但是(通常)N 越大,模偏差越显着。

标签: c algorithm random numbers


【解决方案1】:

如果N 是2 的幂,则使用余数技术通常是安全的(RAND_MAX 通常是2 的幂负1,因此整个范围的长度为2 的幂)。更一般地,N 必须划分rand() 的范围以避免偏差。

否则,不管rand() 的质量如何,你都会遇到this problem。简而言之,问题在于您将该范围切割成多个“部分”,每个长度为N,如果N 不划分范围,那么最后一部分将不完整。因此,从该部分“截断”的数字不太可能出现,因为它们可以生成的“部分”少了一个。

不幸的是,rand() / (RAND_MAX / N + 1) 也坏了(以几乎相同的方式),所以真正的答案是:不要使用它们中的任何一个。

上面概述的问题非常基础,除非 Y 除以 X,否则无法在 Y 结果上均匀分配 X .

【讨论】:

  • 考虑在此处添加问题的简短摘要,因为链接可能会失效。
【解决方案2】:

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() 来实现无偏分布,而不必小心。

【讨论】:

  • @David Eisenstat 随机数不是均匀分布,但一个非常大的样本应该在统计上接近它。
  • 是的。截断分数可能会引入类似的问题。但这分布得更均匀。第一种方法偏向于较小的数字,而第二种方法偏向于均匀分布在该范围内的数字,这对于某些应用程序来说可能是更好的行为。
【解决方案3】:

在 avr-gcc 上:

我使用rand() &amp; 0xFF 来获取 0 到 255 之间的随机数,结果并不好。事实证明,使用低位不是很可靠的方法,通常是相同的值。可能与模数类似。

rand() / (RAND_MAX / N + 1) 对我来说效果更好

【讨论】:

猜你喜欢
  • 2015-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
  • 2019-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多