【问题标题】:How to properly choose rng seed for parallel processes如何为并行进程正确选择 rng 种子
【发布时间】:2012-09-28 13:21:46
【问题描述】:

我目前正在开发一个 C/C++ 项目,我正在使用随机数生成器(gslboost)。整个想法可以简化为不平凡的stochastic process,它接收种子并返回结果。我正在计算该过程的不同实现的平均值。

因此,种子很重要:过程必须使用不同的种子,否则会使平均值产生偏差。

到目前为止,我使用time(NULL) 提供种子。但是,如果两个进程在同一秒开始,则种子是相同的。发生这种情况是因为我正在使用并行化(使用 openMP)。

所以,我的问题是:如何在提供独立种子的 C/C++ 上实现“种子提供者”?

例如,我虽然在使用线程号(thread_num),seed = time(NULL)*thread_num。然而,这意味着种子是相关的:它们是彼此的倍数。这对“伪随机”有什么问题吗?还是和顺序种子一样好?

要求它必须同时在 Mac OS(我的电脑)和类似于 OS Cent(集群)的 Linux 发行版上运行(并且自然会提供独立的实现)。

【问题讨论】:

  • “这是否对“伪随机”事物造成任何问题,或者与种子顺序一样好?这取决于您的 RNG,但如果它有效,那应该不是问题。毕竟你得到的是伪随机数。
  • 啊,它必须是 (thread_num+1),因为第一个线程始终为 0,并使种子始终为 0。
  • 可能重复的问题:stackoverflow.com/questions/7617587/…
  • 我想你不是在使代码瘫痪,而是在并行化,不:)

标签: c++ c random parallel-processing


【解决方案1】:

为此,一种常用的方案是使用“主”RNG 为每个特定于进程的 RNG 生成种子。

这种方案的优点是整个计算仅由一个种子决定,您可以将其记录在某个地方以便能够重播任何模拟(这可能对调试讨厌的错误很有用)。

【讨论】:

    【解决方案2】:

    我们在 Beowulf 计算网格上遇到了类似的问题,我们使用的解决方案是将进程的 pid 合并到 RNG 种子中,如下所示:

    time(NULL)*thread_num*getpid()
    

    当然,您可以只从 /dev/urandom 或 /dev/random 中读取一个整数。

    【讨论】:

      【解决方案3】:

      当遇到这个问题时,我经常使用来自 Boost.Uuid 的seed_rng。它使用timeclock 和来自/dev/urandom 的随机数据来计算种子。你可以像这样使用它

      #include <boost/uuid/seed_rng.hpp>
      #include <iostream>
      
      int main() {
        int seed = boost::uuids::detail::seed_rng()();
        std::cout << seed << std::endl;
      }
      

      请注意,seed_rng 来自 detail 命名空间,因此它可以在不另行通知的情况下消失。在这种情况下,基于seed_rng 编写自己的实现应该不会太难。

      【讨论】:

        【解决方案4】:

        Mac OS 也是 Unix,所以它可能有 /dev/random。如果是这样,那就是 获得种子的最佳解决方案。否则,if 生成器是 好,取time( NULL ) 一次,然后为种子增加它 每个生成器,应该给出相当好的结果。

        【讨论】:

          【解决方案5】:

          如果您在 x86 上并且不介意使代码不可移植,那么您可以读取时间戳计数器 (TSC),它是一个 64 位计数器,以 CPU(最大)时钟速率(大约 3 GHz)并将其用作种子。

          #include <stdint.h>
          static inline uint64_t rdtsc()
          {
              uint64_t tsc;
              asm volatile
              ( 
                  "rdtsc\n\t"
                  "shl\t$32,%%rdx\n\t"       // rdx = TSC[ 63 : 32 ] : 0x00000000
                  "add\t%%rdx,%%rax\n\t"     // rax = TSC[ 63 :  0 ]
                  : "=a" (tsc) : : "%rdx"
              );
              return tsc;
          }
          

          【讨论】:

          • 内联汇编在 x64 MSVC 上不可用吗?
          • @NoSenseEtAl,我不了解 MSVC,它可以在 Mac OS X 和 Linux 上运行,两者都支持 gcc - 我应该更具体一些,谢谢。
          【解决方案6】:

          当比较相同的伪随机数生成器产生的两个无限时间序列与不同的种子时,我们可以看到它们相同地延迟了一些时间 tau。通常这个时间尺度比你的问题要大得多,以确保两个随机游走是不相关的。

          如果您的随机过程处于高维相空间中,我认为一个好的建议可能是:

          seed = MAXIMUM_INTEGER/NUMBER_OF_PARALLEL_RW*thread_num + time(NULL)
          

          请注意,使用方案并不能保证时间 tau 很大!

          如果您对系统时间尺度有所了解,则可以多次调用随机数生成器,以生成在某个时间间隔内等距的种子。

          【讨论】:

            【解决方案7】:

            也许你可以试试 C++11 的 std::chrono 高分辨率时钟:

            类 std::chrono::high_resolution_clock 表示具有 系统上可用的最小刻度周期。它可能是一个别名 std::chrono::system_clock 或 std::chrono::steady_clock,或第三个, 独立时钟。

            http://en.cppreference.com/w/cpp/chrono/high_resolution_clock

            但是我不确定 srand(0) 有什么问题; srand(1), srand(2).... 但我对 rand 的了解非常基础。 :/

            出于疯狂的安全考虑:

            请注意,下面描述的所有伪随机数生成器都是 CopyConstructible 和可分配。复制或分配发生器 将复制其所有内部状态,因此原始和副本将 生成相同的随机数序列。

            http://www.boost.org/doc/libs/1_51_0/doc/html/boost_random/reference.html#boost_random.reference.generators

            由于大多数生成器都有疯狂的长周期,您可以生成一个,将其复制为第一个生成器,使用原始生成 X 个数字,将其复制为第二个,使用原始生成 X 个数字,将其复制为第三个... 如果您的用户调用自己的生成器少于 X 次,它们将不会重叠。

            【讨论】:

              【解决方案8】:

              按照我理解您的问题的方式,您有多个进程使用相同的伪随机数生成算法,并且您希望每个随机数“流”(在每个进程中)彼此独立。我说的对吗?

              在这种情况下,您猜对了,除非 rng 算法这么说,否则提供不同(相关)种子并不能保证任何东西。你基本上有两种解决方案:

              简单版

              使用单一来源的随机数和单一种子。然后以循环方式向每个进程提供随机数。

              此解决方案速度较慢,但​​可以保证您提供给流程的数字是可以的。

              您可以做同样的事情,但一次生成您需要的所有随机数,然后将此集合分成与您的进程一样多的切片。

              使用为此设计的 RNG

              您可以在论文和网络上找到几种专门设计用于从单个初始状态提供独立随机数流的算法。它们很复杂,但大多数都提供源代码。这个想法通常是将 RNG 空间(您可以从初始状态获得的值)“拆分”成各种块,如上所示。它们更快,因为使用的算法可以轻松计算如果您跳过给定数量的值,RNG 的状态会是什么。

              这些生成器通常被称为“并行随机数生成器”。 最受欢迎的可能是这两个:

              查看他们的手册以充分了解他们的工作、他们的工作方式以及是否真的是您需要的。

              【讨论】:

              • @J.C.Leitão,关于循环策略,我最近了解到有些人担心随机流的子集可能不像原始流那样随机。这是提到here。循环策略意味着每个线程都有一个子集。我认为担心的是数字序列可能存在未被注意到的周期性。 (有一个关于旧的 Unix RNG 的故事,它在奇数和偶数之间交替。这是一个不应该发生的极端情况,但也许一些更微妙的周期性可能会发生。)
              猜你喜欢
              • 2020-04-25
              • 2012-01-13
              • 1970-01-01
              • 2019-06-05
              • 2016-07-18
              • 1970-01-01
              • 2022-11-12
              • 2014-02-11
              • 2023-02-03
              相关资源
              最近更新 更多