如何为并行进程正确选择rng种子

时间:2012-10-08 09:50:39

标签: c++ c random parallel-processing

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

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

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

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

例如,我使用的是线号(thread_num),seed = time(NULL)*thread_num。然而,这意味着种子是相关的:它们是彼此的多个。这会对“伪随机”造成任何问题,还是与顺序种子一样好?

要求是它必须适用于Mac OS(我的电脑)和类似于OS Cent(集群)的Linux发行版(并且自然会给出独立的实现)。

9 个答案:

答案 0 :(得分:7)

常用的方案是使用“主”RNG为每个特定过程的RNG生成种子。

这种方案的优点是整个计算只由一个种子决定,你可以在某处记录,以便能够重放任何模拟(这可能对调试令人讨厌的错误很有用)。

答案 1 :(得分:5)

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

time(NULL)*thread_num*getpid()

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

答案 2 :(得分: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编写自己的实现应该不会太难。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

如果您使用的是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;
}

答案 5 :(得分:1)

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

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

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

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

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

答案 6 :(得分:1)

也许你可以尝试使用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和Assignable。复制或分配发电机   将复制其所有内部状态,因此原件和副本将   生成相同的随机数序列。

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

由于大多数生成器都有疯狂的长循环,你可以生成一个,将其复制为第一个生成器,生成带有原始的X数字,将其复制为第二个,生成带有原始的X数字,将其复制为第三个...... 如果您的用户在不到X时间内调用自己的生成器,则不会重叠。

答案 7 :(得分:1)

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

在这种情况下,你是正确的,怀疑给予不同的(相关的)种子并不保证你的任何东西,除非rng算法这样说。你基本上有两个解决方案:

简易版

使用单一种子随机数。然后以循环方式为每个过程提供随机数。

此解决方案速度很慢,但保证您为流程提供的数量正常。

您可以执行相同的操作,但一次生成所需的所有随机数,然后将此集拆分为与进程一样多的切片。

使用为此设计的RNG

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

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

检查他们的手册,以充分了解他们的工作,他们如何做,以及它是否真的是你需要的。

答案 8 :(得分:-1)

您可以查看std::random_device以生成非确定性随机数。