我有一个伪随机数生成器的实现,特别是 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;
我最初没有发现这一点,当我发现时,我想知道在计算的不同阶段溢出是否会导致不同的结果。我相信答案是否定的——这两种计算总是给出相同的答案。