我有一个循环,我在某些点添加噪音;这些随后被用作一些统计测试的基础。
涉及的数据集非常大,所以我想使用openMP将其并行化以加快速度。当我想拥有多个PRNG时,问题出现了。我有自己的PRNG类基于NR的模数方法(我认为是rand4),但我不确定如何正确地播种PRNG以确保适当的熵
Normalliy我会做这样的事情
prng.initTimer();
但是如果我有一个prng数组,每个工作线程一个,那么我不能简单地在每个实例上调用initTimer - 定时器可能不会改变,并且定时器关闭可能会引入相关性。
我需要防止自然关联,而不是针对恶意攻击者(这是实验性数据),所以我需要一种安全的方式来播种rng数组。
我想过简单地使用
prng[0].initTimer()
for(int i=1; i<numRNGs; i++)
prng[i].init(prng[0].getRandNum());
然后调用我的循环,但不确定这是否会在模数方法中引入相关性。
答案 0 :(得分:2)
种子PRNG不需要创建独立的流。您应该仅播种第一个实例(称为引用)并通过快速转发引用实例来初始化其余实例。这只有在您知道每个线程将使用多少随机数并且快速转发算法可用时才有效。
我对你的rand4了解不多(谷歌搜索它,但没有具体的内容出现),但是你不应该认为只通过播种就可以创建独立的流。你可能想要使用不同的(更好的)PRNG。看看WELL。它速度快,具有良好的统计特性,并由知名专家开发。 WELL 512和1024是最快的PRNG之一,并且都有很长的时间段。您可以使用不同的种子初始化多个WELL实例,以便创建独立的流。由于周期很长,PRNG几乎没有机会产生重叠的随机数流。
如果频繁拨打您的PRNG,请注意虚假共享。这篇Herb Sutter's文章解释了错误共享如何破坏多核性能。将多个PRNG打包成连续数组几乎是错误共享的完美配方。为了避免错误共享,可以在PRNG之间添加填充或在堆/免费存储上分配PRNG。在后一种情况下,应使用某种对齐的分配器单独分配每个RNG。您的编译器应提供对齐的malloc的版本。检查文档(嗯,谷歌搜索实际上比阅读手册更快)。 Visual C ++有_aligned_malloc
,GCC有memalign
和posix_memalign
。 aliment值必须是CPU的缓存行大小的倍数。通常的做法是沿128字节边界对齐。对于便携式解决方案,您可以使用TBB的缓存对齐分配器。
答案 1 :(得分:1)
我认为这取决于您的PRNG的属性。通常的PRNG弱点是较低位的较低熵和较低的n
值的较低的熵。所以我认为你应该检查你的PRNG是否存在这些弱点并相应地更改你的代码。
也许某些diehard tests提供了有用的信息,但您也可以自己检查第一个n
值及其统计属性,例如求和方差,并将它们与预期值进行比较。
例如,播种PRNG并总结PRNG的模数为11的前100个值,重复此次R次。如果总和与预期的总和(5 * 100 * R)非常不同,那么您的PRNG会遇到上述一个或两个弱点。
对PRNG一无所知,我觉得使用这样的东西更安全:
prng[0].initTimer();
// Throw the first 100 values away
for(int i=1; i < 100; i++)
prng[0].getRandNum();
// Use only higher bits for seed values (assuming 32 bit size)
for(int i=1; i<numRNGs; i++)
prng[i].init(((prng[0].getRandNum() >> 16) << 16)
+ (prng[0].getRandNum() >> 16));
但当然,这些都是关于PRNG的猜测。有了理想的PRNG,您的方法应该可以正常工作。
答案 2 :(得分:0)
如果使用来自相同类型的PRNG的一系列数字为PRNG播种,它们将产生相同的数字序列,彼此相互偏移。如果您希望它们产生不同的数字,您需要使用来自不同PRNG的一系列伪随机数为它们播种。
或者,如果您使用的是具有/dev/random
的类似unix的系统,则只需从该设备读取即可获得一系列随机数作为种子。