Java随机,种子的微小变化仅导致输出的微小变化
在用 Java 制作地图生成器时,我发现随机数生成器存在一个相当令人不安的问题,具体来说,当两个 RNG 具有非常相似的种子(小整数不同)时,它们的第一个输出值将变得非常相似!
示例代码:
Random r = new Random();
long n = 100000; //Choose any number
r.setSeed(n);
System.out.println(r.nextInt());
r.setSeed(n+1);
System.out.println(r.nextInt());
这几乎打破了我对原始 Java RNG 的信心,因为我使用坐标来生成地图生成器。 有人可以建议重新定义 Random.next(int bits)
方法,或者对此问题进行其他修复吗?
感谢您的帮助!
While making a map generator in Java I found a rather unnerving problem with their random number generator, to specify, when two RNGs have very similar seeds (differing in small integers) their first output value will become very similar!
Example code:
Random r = new Random();
long n = 100000; //Choose any number
r.setSeed(n);
System.out.println(r.nextInt());
r.setSeed(n+1);
System.out.println(r.nextInt());
This pretty much broke my faith in the original Java RNG, since I use coordinates to seed a map generator.
Could someone suggest either a redefinition for the Random.next(int bits)
method, or some other fix for this problem?
Thank you for your help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您是否比较了从 100000 和 100001 获得的前 20 个值的序列?
这些分别是种子 100000 和 100001 的前 20 个 nextInts。在第三列中,不同位的数量(2之间的异或的位数)
最后一列应该保持在16左右,
在3-5次迭代后不那么相似,
除了标准随机数之外,他还实现了一个线性同余RNG,已知它不是 现有的最好的伪随机实现,但内存效率最高(在
2^48
周期内只有一个 64 位字)对于感兴趣的乘法器来说,
0x5deece66dL
且 c 为0xbL
did you compare the sequence of the first ~20 values you get from 100000 and 100001?
these are the first 20 nextInts of seeds 100000 and 100001 resp. with in the third column the amount of different bits (bitcount of the xor between the 2)
this last column should remain around 16
not so similar after 3-5 iterations he
besides the standard Random implements a linear congruential RNG which is known not to be the best pseudo-random implementation in existence but the most efficient with memory (only one 64bit word for a period of
2^48
)for the interested the multiplier is
0x5deece66dL
and c is0xbL
您的两个种子(PRNG 状态)仅在最低有效位上有所不同。考虑到 PRNG 通常只是进行一些异或运算和移位,这应该不会太令人惊讶。
无论如何,你不应该像这样使用
Random
。 PRNG 的状态将根据每个nextInt
方法进行更新(状态/种子将更改 48 个可用位的大约 50%)。这就是你应该关心的。Your two seeds (PRNG states) differ only by the least significant bit. Considering that PRNGs usually just do some xor-ing and shifting this shouldn't be too surprising.
You shouldn't use
Random
like this anyway. The state of the PRNG will be updated (state / seed will change by about 50 % of the 48 available bits) upon eachnextInt
method. That's all you should care about.据我了解,您需要一个依赖于某些计算种子的随机数序列,这样您就可以在给定相同种子时随时重新生成该序列。是这样吗?
相似种子生成的随机数序列一开始很相似,但很快就会发散。如果您跳过前 k 值,您可能会得到更适合您需求的结果。这里,k是一个你必须根据你对序列的不同性和计算速度的需要来确定的数字。
As I understand, you want a sequence of random numbers that depends on some computed seed, such that you can re-generate the sequence any time when given the same seed. Is that right?
The random number sequence generated by similar seeds starts similar, but soon diverges. You might get results that better fit your need, if you just skip over the first k values. Here, k is a number you have to determine, according to your need of dissimilarity of the sequence and speed of computation.
引入 java.security.SecureRandom 来处理存在
java.util.Random
中的问题,例如问题中描述的问题。SecureRandom
没有表现出相同的可预测性(至少,它没有那么明显)。您可以通过在代码中使用SecureRandom
而不是Random
来解决该问题,因为前者是后者的子类。人们可能想知道为什么 Sun 在发现这个问题后不直接修复
Random
。原因是向后兼容性——Random
的行为无法更改,因为它会破坏依赖于任何给定种子生成的特定伪随机序列的现有代码。java.security.SecureRandom was introduced to deal with problems in
java.util.Random
such as the one described in the question.SecureRandom
does not exhibit the same predictability (at least, it is not as blatantly obvious). You can fix the problem by usingSecureRandom
in your code instead ofRandom
as the former is a subclass of the latter.One might wonder why Sun didn't just fix
Random
after this problem was discovered. The reason is backward compatibility -- the behaviour ofRandom
could not be changed because it would break existing code that depended upon the particular pseudo-random sequence generated by any given seed.