【发布时间】:2012-09-24 21:46:54
【问题描述】:
我有一个伪随机数生成器的实现,特别是 George Marsaglia 的 XOR-Shift RNG。我的实现在这里:
事实证明,第一个随机样本与种子非常密切相关,如果您看一下 Reinitialise(int seed) 方法,这一点相当明显。这是不好的。我建议的解决方案是将种子的部分混合如下:
_x = (uint)( (seed * 2147483647)
^ ((seed << 16 | seed >> 48) * 28111)
^ ((seed << 32 | seed >> 32) * 69001)
^ ((seed << 48 | seed >> 16) * 45083));
因此,我通过将种子的位乘以四个素数并异或返回以形成 _x,从而显着削弱了任何相关性。我还在乘法之前旋转种子的位,以确保不同大小的位在 32 位值的整个值范围内混合。
四向轮换似乎在什么都不做和所有可能的轮换之间取得了很好的平衡 (32)。质数是“空中的手指” - 足够的大小和位结构可以将这些位混在一起并将它们“传播”到完整的 32 位上,而不管起始种子如何。
我应该使用更大的素数吗?是否有解决这个问题的标准方法,也许有更正式的基础?我正在尝试以最小的 CPU 开销来做到这一点。
谢谢
=== 更新 ===
我决定使用一些素数,设置位更好地分布在所有 32 位中。结果是我可以省略移位,因为乘法可以达到相同的效果(在 32 位的整个范围内散列位),所以我只需将四个乘积相加即可得到最终的种子......
_x = (uint)( (seed * 1431655781)
+ (seed * 1183186591)
+ (seed * 622729787)
+ (seed * 338294347));
我可能会用更少的素数/乘法来逃脱。两个似乎太少了(我仍然可以在第一个样本中看到模式),三个看起来还不错,所以为了安全起见,我做了四个。
=== 更新 2 ===
仅供参考,以上简化为功能等效:
_x = seed * 3575866506U;
我最初没有发现这一点,当我发现时,我想知道在计算的不同阶段溢出是否会导致不同的结果。我相信答案是否定的——这两种计算总是给出相同的答案。
【问题讨论】:
-
如果您在一个时钟周期内初始化多个 rng,那么使用时钟也是不好的(这是我有时遇到的问题)。
-
你能把第一个值扔掉,或者可能是时钟随机确定的前n个值吗?
-
见乔治·马萨利亚。 (2003 年)。 Xorshift RNG。 jstatsoft.org/v08/i14/paper
-
如果您正在寻找 32 位哈希函数,您可能需要查看这篇文章:32-bit checksum algorithm better quality than CRC32
-
您的(滞后斐波那契混合)xorshift 生成器使用三元组 8、11、19,它没有最大周期(您可能打算使用 9、11、19?)。此外,移位异或操作似乎有点奇怪。 t(a.k.a.x) 与自身的两个移位值进行异或,并与w和移位值w进行异或。您通常应该使用相应临时值的移位值对一个值进行三次异或。我不确定您所做的是否等效。
标签: c# bit-manipulation prng