问题
我打算为Linux编写一个C++ 11应用程序,该应用程序基于大约一百万个伪随机32位数字进行一些数值模拟(而不是加密)。为了加快速度,我想使用台式机CPU的所有内核在并行线程中执行仿真。我想将boost提供的Mersenne Twister mt19937 用作PRNG,并且我想出于性能原因,每个线程应该有一个这样的PRNG。现在,我不确定如何播种它们,以避免在多个线程中生成相同的随机数子序列。
备择方案
到目前为止,我已经想到了以下替代方案:

  • 独立于/dev/urandom为每个线程播种PRNG。
    我有点担心系统熵池耗尽的情况,因为我不知道系统内部PRNG的工作方式。由于/dev/urandom使用的是Mersenne Twister本身,我是否偶然得到了连续的种子,它们准确地标识了Mersenne Twister的连续状态?可能与我对下一点的担忧密切相关。
  • /dev/urandom播种一个PRNG,并从第一个PRNG播种。
    基本上也有相同的问题:使用一个PRNG播种使用相同算法的另一个PRNG是好是坏?或者换句话说,从mt19937读取625个32位整数是否直接对应于mt19937生成器在生成过程中的任何时刻的内部状态?
  • 首先使用非Merenne信息为他人播种。
    由于使用相同的算法来生成随机数并生成初始种子似乎有点不好,所以我考虑引入一些不依赖于Mersenne Twister算法的元素。例如,我可以将线程ID异或到初始种子 vector 的每个元素中。这会使事情变得更好吗?
  • 在线程之间共享一个PRNG。
    这样可以确保只有一个序列具有Mersenne Twister的所有已知和理想特性。但是控制访问该生成器所需的锁定开销确实让我有些担心。由于我没有发现相反的证据,因此我假设我作为库用户将负责阻止并发访问PRNG。
  • 预先生成所有随机数。
    这将使一个线程预先生成所有必需的1M随机数,以供以后的不同线程使用。与整个应用程序相比,4M的内存需求将很小。这种方法让我最担心的是,随机数的生成本身并不是并发的。整个方法的伸缩性也不太好。

  • 问题
    您会建议采用哪种方法?为什么?还是您有其他建议?
    您知道我的哪些担忧是正当的,哪些仅是由于我对事情的实际运行缺乏洞察力所致?

    最佳答案

    我会用一个实例来播种其他实例。我敢肯定,您可以轻松安全地完成此操作。

  • 即使状态空间中的微小变化也会导致下游的较大变化-如果您可以确保它们的起始空间不完全相同(并且没有相同的状态前缀),那么我也不必担心产生相同的数字。例如,仅使用值1,2,3来播种三个线程就可以了-您甚至不需要播种整个空间。另一个优点:通过使用可明确预测的种子,您可以轻松地否认正在挑选任何运行的想法(假设您正在尝试演示某些东西)。
  • 进行播种很简单,这意味着所得的“子代”是高度不相关的。只是以广度优先的方式进行迭代;也就是说,如果您要播种N x 623个int值,请不要顺序播种623个值,而是选择第一个N并进行分发,然后选择下一个N,依此类推。即使播种机和子级之间存在某种相关性,几乎没有任何 child -这就是您所关心的。
  • 我希望有一种算法,该算法尽可能允许确定性执行,因此依赖urandom并不有吸引力。这使调试更加容易。
  • 最后,显然-测试。这些PRNG相当强大,但是一定要注意结果,并根据模拟的内容进行一些相关性测试。大多数问题应该是显而易见的-要么您播种不佳,并且存在明显的重复子序列,您播种得好,然后质量由PRNG限制所决定。
  • 对于最终执行,在完成测试后,您可以使用urandom播种623个状态值中的第一个,以使您省心和/或线程ID。
  • 10-07 19:06