【问题标题】:Java Random, little change in seed causes only little change in outputJava 随机,种子的微小变化只会导致输出的微小变化
【发布时间】:2011-05-28 19:49:01
【问题描述】:

在用 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) 方法,或针对此问题进行其他修复吗?

感谢您的帮助!

【问题讨论】:

  • 你为什么在r3上打电话给nextInt()?那个RNG是从哪里来的?它与r有某种关系吗?
  • .. 为什么在每个值之后更改种子?如果 r3 only 是一个错字
  • @Kaj,说明输出是相似的。
  • 所以?那是错误的使用方式。正确使用它,它会按文档说明工作。
  • 对不起,我把代码复制错了。它应该是System.out.println(r.nextInt()); 另外,我每次都必须在我的应用程序中设置种子,因为动态地图生成器使用坐标来种子。从我的世界中无耻地窃取灵感:P

标签: java random seed


【解决方案1】:

您是否比较了从 100000 和 100001 中获得的前 20 个值的序列?

这些是种子 100000 和 100001 的前 20 个 nextInts。在第三列中是不同位的数量(2 之间的异或的位数)

最后一列应该保持在 16 左右

-1986972922 -1987357671 13
-1760380366 -604895790  16
-1057894078 -329706441  15
-363772240  -1218064509 15
1545317691  -300240831  14
271304166   -900428132  21
1208561582  273461468   16
-1257783052 1069490639  16
-1549884799 40157720    15
-1514737808 -1818800021 17
-1030569735 1859508545  15
1310070992  880402584   18
-1513092400 971613287   19
-1993219517 354161779   16
-10847170   -204018237  15
-965377044  1488135032  14
802471291   1094582308  22
-539776032  -1021376555 15
2088199751  2070302462  12
-1271582124 64627614    19

在 3-5 次迭代后不太相似

除了标准的 Random 之外,还实现了一个线性同余 RNG,它被认为不是现有最好的伪随机实现,但内存效率最高(2^48 期间只有一个 64 位字)

对于有兴趣的人来说,乘数是0x5deece66dL,c 是0xbL

【讨论】:

  • 只是为了让这里的每个人都清楚——在那种情况下,“不是最好的伪随机实现”只是意味着在 2^48 个不同的数字之后,我们将再次得到相同的数字。在其他方面——通常是更重要的方面——java Random 实现非常好。毕竟,如果你每次我们想要生成一个数字时将种子加一,我们会很长时间得到一个 2^64 的周期,但没有人会认为生成器更好;)
  • 其实LCG还有其他问题来自无所不知的wiki:en.wikipedia.org/wiki/…我说的是连续数字之间的相关性
【解决方案2】:

您的两个种子(PRNG 状态)仅在最低有效位上有所不同。考虑到 PRNG 通常只是进行一些异或运算并进行转换,这应该不足为奇。

无论如何,您都不应该像这样使用Random。 PRNG 的状态将在每个nextInt 方法上更新(状态/种子将更改大约 48 个可用位的 50%)。这就是你应该关心的全部。

【讨论】:

    【解决方案3】:

    据我了解,您需要一个依赖于某个计算种子的随机数序列,这样您就可以在给定相同种子的任何时候重新生成该序列。对吗?

    相似种子产生的随机数序列开始相似,但很快就发散了。如果您跳过前 k 个值,您可能会得到更符合您需要的结果。这里,k是一个数字,你必须根据你对序列不同和计算速度的需要来确定。

    【讨论】:

      【解决方案4】:

      java.security.SecureRandom 被引入来处理java.util.Random 中的问题,例如问题中描述的问题。 SecureRandom 没有表现出相同的可预测性(至少,它不那么明显)。您可以通过在代码中使用 SecureRandom 而不是 Random 来解决此问题,因为前者是后者的子类。

      有人可能想知道为什么 Sun 在发现此问题后不直接修复 Random。原因是向后兼容性——Random 的行为无法更改,因为它会破坏依赖于任何给定种子生成的特定伪随机序列的现有代码。

      【讨论】:

      • 但他没有使用“任何给定种子生成的序列”。他使用长度为 1 的序列,然后稍微更改种子。他在滥用 API。这在 Random 中没有表现出使用 SecureRandom 解决的问题。
      • @EJP 他的问题不是关于生成流的随机性,而是在用附近的种子重新播种后初始元素缺乏相同的随机性。这可能在生成初始种子时发生,例如,使用一天中的时间。这种可预测性不仅从伪随机的角度来看很差,而且可能对安全性有害。这个问题困扰着早期的 TCP/IP 堆栈。这就是为什么SecureRandom 在其名称中包含“安全”,以及为什么在以后的 JDK 实现中努力修复此行为的原因。
      猜你喜欢
      • 2016-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多