播种许多PRNG,然后再次播种,什么是优质的方法?

时间:2016-04-29 20:00:15

标签: c random

我有很多粒子并行跟随随机过程。对于每个粒子,都有一个与之相关的PRNG。 模拟必须经过多次重复才能获得平均结果。对于每次重复,应在模拟开始之前为每个粒子选择独有的PRNG种子。

我第一次得到seed = time(NULL)作为particle1的种子。其余的我只做particle2 = seed + 1particle3 = seed + 2等等,所以所有粒子最终都有不同的种子。

每次重复时,计划是为从time(NULL)获得的初始种子添加偏移量,例如seed = seed + all_particles_offset; 然后使用前面描述的方法为每个粒子分配不同的种子。 我的问题是,这种方法是否会带来可接受的随机性质量?我不关心安全性,只关注并行运行的随机数的质量以及该过程不时重新播种的事实。

顺便说一下,使用的PRNG是PCG。

2 个答案:

答案 0 :(得分:2)

时间不是好种子,因为它太可预测了。如果您经常需要种子,它也有可能被重复使用。为避免这种情况,使用其他方法(如进程和/或线程ID)来排除时间是正常的。

更好的方法是从您的操作系统的随机源(如/dev/random或非阻止版本/dev/urandom)获取种子。这将使用环境噪音和其他来源为您提供良好的随机种子。

您可以将它们组合在一起,这就是Ruby's Random#new_seed所做的。

    /* This gets a seed from /dev/urandom */
    fill_random_bytes(seed, sizeof(*seed));

    /* This further permutes the seed with the time and pid */
    gettimeofday(&tv, 0);
    seed[0] ^= tv.tv_usec;
    seed[1] ^= (uint32_t)tv.tv_sec;
#if SIZEOF_TIME_T > SIZEOF_INT
    seed[0] ^= (uint32_t)((time_t)tv.tv_sec >> SIZEOF_INT * CHAR_BIT);
#endif
    seed[2] ^= getpid() ^ (n++ << 16);
    seed[3] ^= (uint32_t)(VALUE)&seed;
#if SIZEOF_VOIDP > SIZEOF_INT
    seed[2] ^= (uint32_t)((VALUE)&seed >> SIZEOF_INT * CHAR_BIT);
#endif

您可以从random.c中获取随机种子代码。

为了避免必须从/dev/random进行多次读取,这可能会耗尽熵,您可以改为@indiv suggested in the comments:播种PRNG以生成更多种子。好的一面是你只需要一个好种子。缺点是可能让你容易受到PRNG的瑕疵。

这实际上是/dev/urandom所做的,它使用/dev/random真正的随机性来播种自己的PRNG,以确保始终存在随机数。你可以从/dev/urandom读取你的种子。好处是这增加了另一个PRNG,可以通过使用相同PRNG算法的输出播种你的PRNG来消除任何缺陷......或者它可能会使它们变得更糟。缺点是你不知道/dev/urandom如何产生这些种子,它会随着平台而变化。

这比使用timeseed +1seed + 2更好......

答案 1 :(得分:1)

Cristobal,PCG或类似的PRNG(使用快速对数跳过),你不需要处理多个种子。一颗种子就足够了。在所有线程/工作人员开始之前,你就会为PRNG播种一次。

您可以设置每个事件需要多少RNG呼叫 - N_part。您可以设置每个线程的事件数量 - N_evt。

然后在你开始工作之后,你必须做每个工人

pcg rng(seed);
rng.skip_ahead(worker_id*N_part*N_evt); // whatever it is called in PCG

for(int k = 0; k != N_evt; ++k) {
    int used_rngs = simulate_particle();
    // overlap check
    rng.skip_ahead(N_part-used_rngs); // move to the next particle
}

因此,你有完全可控的模拟,你知道如果你重叠,每个粒子都从相同的rng状态开始,所以它完全可以重复调试等