我正在使用 Random 类来生成数字。
class Random {
UInt128 seed;
public Random(UInt128 seed) {
this.seed = seed;
}
public int Next(int maxValue) {
NextSeed();
return (int)((seed * seed * 158 + 52185) % (UInt128)maxValue);
}
public void NextSeed() {
seed = (seed * seed + 1856237) % UInt128.MaxValue;
}
}
然后通过循环for (int i = 0; i < 16; i++)我生成这些随机值并且它们是不同的。然而,当再次调用这个循环时,会生成同一系列的数字,尽管粒度应该不同,但我没有重新定义它并使用一个对象。
以下是具有相同值的行的输出示例:
7 1 7 1 7 1 7 1 7 1 7 1 7 1 7 1
7 1 7 1 7 1 7 1 7 1 7 1 7 1 7 1
7 1 7 1 7 1 7 1 7 1 7 1 7 1 7 1
7 1 7 1 7 1 7 1 7 1 7 1 7 1 7 1
... // и т.д.
PS我需要使用我自己的课程。是的,我知道代码很糟糕,而且转换为 UInt128 看起来不太好。
测试代码:
Random random = new(27652582738); // сид задан для примера
for (int j = 0; j < 32; j++) {
for (int i = 0; i < 16; i++) {
Console.Write(random.Next(16).ToString() + " ");
}
Console.Write("\n");
}
这段代码有两个问题:
例如,这里是没有溢出的相同代码
我们得到以下结论
这已经更像是一场意外了。我们看到,首先,所有数字都是奇数,其次,序列中只涉及 3 个不同的数字。
为什么奇怪:
这里有一个公式
seed * seed * 158,其中 158 是偶数,这意味着该乘法的结果将始终是偶数。因此,如果向其添加奇数52185,则输出将是奇数。因此,输出不能包含偶数。为什么只有3个数字:
通过取余数,您可以忽略所有高于左侧余数值最接近的 2 次方的位。也就是说,你只需丢弃这个公式可以给一边的所有熵。
等等。所选择的公式根本不会给您预期的结果;事实上,它不是 PRCH。
对 128 位种子的要求非常奇怪,而且还不完全清楚原因。我想修补一个好的 PRNG,这里是一个
System.Random基于Xoshiro256算法的实现。还有一种替代方案 - 较旧的 PRCH 算法“Mersenne Twister”,这是实现。正如您所看到的,具有良好分布的 PRNG 略多于 2 行,并进行乘法和取余。了解如何将数字减少到用户指定的限制而不取余数的基本实践。换句话说,你的问题的答案是采用一个好的算法并修改它以满足你的需要,以便它吃掉 128 位种子,仅此而已。或者搜索已经支持开箱即用的 128 位种子的实现。
因此,我试图摆脱因取余而丢弃的位,并将 158 替换为素数 157,将 52185 替换为最接近的素数 52189。
得到输出
您是否同意这要好得多?但这个代码仍然不符合 GSPC 的资格。