【问题标题】:Is rand() really that bad?rand() 真的那么糟糕吗?
【发布时间】:2023-07-19 10:44:01
【问题描述】:

General purpose random number generation 的启发,我决定执行自己的测试,看看 rand() 出了什么问题。使用这个程序:

srand(time(0));
for (int i = 0; i < 1000000; ++i)
{
    std::cout << rand() % 1000 << " ";
}

我使用以下命令在 Octave 中加载它:

S = load("test.txt")
hist(S)

得到了这个结果:

对我来说,结果似乎很统一。我预计结果会更加偏斜。我做错了测试吗?

【问题讨论】:

  • rand 主要是不好的,因为:RAND_MAX 是实现定义的,例如在 Visual Studio 上它只是 2^16; rand 是全球性的,这意味着如果您以外的其他人调用 srand 它可能会搞砸您的代码。如果您有 C++11 编译器,请考虑使用其 RNG 之一
  • 根据实现的不同,rand() 可以在其低位上具有低熵。如果你做了rand() % 4,在某些实现中这是非常不统一的。这就是为什么在这种情况下通常建议(如果你使用rand())写rand() / (RAND_MAX / 4)
  • rand() 真的那么糟糕吗? 是的!
  • 虽然看起来是均匀的,但对于除 2 的幂之外的任何区间宽度,您都不会得到真正的均匀性。数学很简单..
  • @Creris 在 MSVC RAND_MAX 上是 (2^15-1),而不是 2^16。因此,您甚至无法通过 2 个 rand() 调用获得 32 位 int。当您必须运行 2 次调用以获取随机 int 时,在 Linux 中也是如此

标签: c++ c++11 random


【解决方案1】:

您问题中的测试并未真正测试随机性。它所做的只是确保数字均匀分布。这是一个必要条件,但不是充分条件:随机数生成器在许多其他方面可能存在缺陷。

例如,如果我给你一个循环返回数字 0、1、2、...、999 的函数,它也会通过你的测试。然而,它显然无法对随机性进行任何合理的定义。

要了解如何在实践中测试随机数生成器,请查看

有关rand() 的具体讨论,请查看rand() Considered Harmful

【讨论】:

    【解决方案2】:

    您没有考虑的一个重点是生成的随机序列的可预测性。 当使用 time() 作为随机种子时,如果攻击者或多或少知道种子是何时生成的,他可以很容易地重现你的整个随机序列。

    这就是为什么需要真正的随机源,假设您将这些随机数用于任何与安全相关的事情。

    当安全性真的很重要时,您还希望从真正的随机源中获取每个数字,而完全不依赖 PRNG。更慢但更安全。

    【讨论】:

      【解决方案3】:

      这取决于您的目的,提供的rand() 易于手头使用,具有可接受的分发可能性,它不是为加密目的而设计的,也不是为物理模拟目的而设计的。如果您想要一个加密级别或进行物理模拟,这不是一个好的选择,您可能必须获得一个特殊的实现。
      计算机程序还没有产生真正的随机性。

      【讨论】: