我有很多粒子并行跟随随机过程。对于每个粒子,都有一个与之相关的PRNG。 模拟必须经过多次重复才能获得平均结果。对于每次重复,应在模拟开始之前为每个粒子选择独有的PRNG种子。
我第一次得到seed = time(NULL)
作为particle1的种子。其余的我只做particle2 = seed + 1
,particle3 = seed + 2
等等,所以所有粒子最终都有不同的种子。
每次重复时,计划是为从time(NULL)
获得的初始种子添加偏移量,例如seed = seed + all_particles_offset;
然后使用前面描述的方法为每个粒子分配不同的种子。
我的问题是,这种方法是否会带来可接受的随机性质量?我不关心安全性,只关注并行运行的随机数的质量以及该过程不时重新播种的事实。
顺便说一下,使用的PRNG是PCG。
答案 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
如何产生这些种子,它会随着平台而变化。
这比使用time
或seed +1
,seed + 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状态开始,所以它完全可以重复调试等