为什么rand()在使用1和UINT_MAX进行种子设定时会产生相同的值

Why does rand() produce the same value when seeded with 1 and UINT_MAX?

本文关键字:定时 种子 MAX rand UINT 为什么      更新时间:2023-10-16

下面是一些代码:

#include <iostream>
int main() {
    srand(1);
    std::cout << rand() << "n";
    srand(UINT_MAX);
    std::cout << rand() << "n";
}

这会产生以下输出:

16807
16807

为什么这两种种子会产生相同的结果?它们在连续的rand()调用中产生的整个值序列也是相同的。可能值的范围太大,这不可能是纯粹的巧合。是吗

  • rand()实现的意外(如果是,我很好奇这可能是什么)
  • 按设计(如果是,为什么?)

(可能相关:种子10、100、1000、10000和100000分别产生168070、1680700、16807000、168070000和1680700000。)

一个非常简单可用的随机数生成器是Lehmer数生成器。这个RNG可能是最简单的软件实现,它仍然可用,所以它可能具有最多的随机性问题,也最容易分析。

数字16807(又名7的五次方)与Lehmer RNG有关,因为它在1988年被用于最早的实现之一——显然今天仍在使用!

第N个随机数的公式为(使用^求幂):

R(n) = (seed * (16807 ^ n)) mod (2 ^ 31 - 1)

如果设置种子=1,n=1:

R(1) = 16807 mod (2 ^ 31 - 1) = 16807

如果设置seed=2 ^ 32 - 1:

R(1) =
    (2 ^ 32 - 1) * 16807 ≡ (expressing 2^32 = 2^31 * 2)
    ((2 ^ 31 - 1) * 2 + 1) * 16807 ≡ (distributive law)
    (2 ^ 31 - 1) * 2 * 16807 + 1 * 16807 ≡ (modulo 2^31-1)
    16807

这里,随机序列中第一个数的相等性是因为Lehmer RNG中的模数几乎是2的幂(2^31-1),而您的种子也几乎是2(2^32-1)的幂。

对于seed=2^31也会发生同样的情况。

tl;dr:众所周知rand()有那么糟糕。

实际值由实现定义。我在我的平台上获得以下值:

seed:          1 : 41
seed: 4294967295 : 35
seed:         10 : 71
seed:        100 : 365
seed:       1000 : 3304
seed:      10000 : 32694

对于匆匆忙忙的普通用户来说,CCD_ 9看起来有些随意。它不适合携带其他物品。

实现通常使用低质量的生成器(最常见的是常数不好的线性同余)。

所需的数字范围是0…32767,虽然实现可能,但它们通常不会超过这个范围,因此您可以预期许多种子会产生相同的值。

对于C++modern,请参阅<random>以获得可靠的选项。

这取决于随机数生成器的实现。请参阅C';s rand()?用于常见的实现。

通常,可能的种子值的空间比UINT_MAX短得多。可能是1和CCD_ 12被映射到相同的内部种子。

rand()通常使用线性同余生成器,然后第一个生成的随机数像一样依赖

first_random_number  = (seed * const + another_const)  % third_constant

在种子上。这就解释了你发现的依赖性。

我看不出有什么好的理由可以将您观察到的不幸关联设计到您正在使用的rand的实现中。正如您所建议的,这很可能是实现的意外。也就是说,我也认为这是一个巧合,你可以产生与这些输入的相关性。另一个实施可能会有其他一些令人遗憾的投入。

如果是的话,我很好奇那可能是什么

如果您的实现是开源的,那么您可以通过阅读源代码来找到答案。如果它是专有的,你仍然可以在一些文档中找到算法的提及,或者如果你是客户,你可以询问实现者。

如上所述,如果seed设置为1,生成器将重新初始化为其初始值,并产生与任何对rand或srand的调用前相同的值

还要注意,使用相同种子的两个不同初始化将在后续对rand的调用中生成相同的连续结果