11

我只是想知道在程序开始时只播种一次随机数生成器是否足够。我编写使用随机数的函数。我从不在函数中播种 rand() 生成器,而是在主条目上保留调用 srand() 。例如,我的程序可能如下所示:

void func1()
{
    std::cout << "This is func1 " << std::rand() << std::endl;
}

void func2()
{
    std::cout << "This is func2 " << std::rand() << std::endl;
}

int main()
{
    std::srand(std::time(NULL));
    func1();
    func2();
    return 0;
}

通过这样做,我可以轻松地从主条目中关闭播种。它在调试程序时很有用 - 每次我运行程序而不播种时结果都保持不变。有时,如果由于某个随机数而出现问题,如果要生成一组不同的随机数,它可能会消失,所以我更喜欢这种简单的机制来关闭播种。

但是,我注意到在 C++11 的新随机实用程序集中,随机数生成器必须在使用前进行实例化。(例如 default_random_engine)。并且每次生成器都必须单独播种。我想知道是否真的鼓励在需要新生成器时重新植入生成器。我知道我可以创建一个全局随机生成器并像以前一样只播种一次,但我根本不喜欢使用全局变量的想法。否则,如果我创建一个本地随机数生成器,我就会失去全局关闭种子以进行调试或任何目的的能力。

我很高兴学习 C++11 中的新功能,但有时它只是非常令人困惑。如果我对新的随机生成器有任何问题,谁能告诉我?或者 C++11 中的最佳实践是什么?

4

2 回答 2

18

这肯定取决于您正在开发的系统的总体目标,但通常您只需要在系统初始化时为需要随机数生成器 (RNG) 的任何系统播种一次。

在游戏开发中,每个系统(AI、程序内容生成器、粒子等)都有一个单独的 RNG 来隔离该系统以进行调试和维护是很常见的。

如果您存储种子,您还可以用很少的数据重放逻辑(只需要每帧的增量输入)。这不仅可以让您更轻松地调试应用程序,还可以让您支持用户的录制和回放功能。显然,如果您要创建重播模式,则需要在每个重播会话之前为系统提供记录的种子。

还有其他系统,例如密码学,您可能希望定期建立一个新种子。您可能希望随着时间的推移使种子过期,或者需要在每次与客户端应用程序握手时生成一个新种子。但是,您可能希望使用 Cryptography 库,因为它们可能比您自己推出的库更强大。

于 2013-03-03T06:34:47.320 回答
7

随机数生成器 (RNG) 需要实例化的原因之一是它可以保持其内部状态,这样在多线程应用程序中,当多个线程使用同一个 RNG 进程时,您不会引入不确定性- 全局状态。假设您有两个线程,每个线程都处理问题的独立部分 - 如果状态(种子)对于 RNG 实例是私有的,那么当您使用已知值为每个线程的 RNG 播种时,您可以获得确定性。另一方面,如果 RNG 将状态保存在全局变量中,那么每个线程观察到的随机数序列取决于它们对 RNG 的调用的交错——您现在已经引入了非确定性。

用于在多线程应用程序中分配工作的一种编程模式是线程池。如果在池中排队等待工作线程执行的工作项需要一个 RNG,并且您希望每次运行都具有确定性,那么您希望每个线程在从队列

  • 这可以作为工作项初始化的一部分来完成
  • 种子可能涉及作业参数的哈希函数
  • 如果您对它的编码方式很小心,那么无论工作线程的数量或它们从队列中拉出作业的顺序如何,您都可以拥有伪随机性和确定性。

这是一个处理此问题的 SO 问题:Deterministic random number generator bolted to instance (thread independent)

在单线程应用程序中,没有必要重新播种 RNG,实际上这是不可取的,因为这样做会缩短它开始重复之前的周期。@MatthewSanders 指出的例外是密码学 - 在这种情况下,您需要最大熵(最小确定性),因为随机数被用作私钥。

于 2013-03-03T08:00:24.087 回答