2011-05-28 71 views
7

在Java中制作映射生成器时,我发现它们的随机数生成器存在一个相当不安的问题,即当两个RNG具有非常相似的种子(小的整数不同)他们的第一个输出值将变得非常相似!Java随机,种子变化不大导致输出变化很小

示例代码:

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)方法重新定义,或对此问题的其他修复?

谢谢你的帮助!

+4

你为什么在'r3'上调用'nextInt()'? RNG从哪里来?它与'r'有什么关系? – 2011-05-28 19:51:42

+1

..为什么在每个值后改变种子?如果r3只是输入错误 – Kaj 2011-05-28 19:54:51

+0

@Kaj,以说明输出是相似的。 – aioobe 2011-05-28 20:04:33

回答

8

你比较了从100000和100001得到的前20个值的顺序吗?

这些是种子100000和100001的前20个nextInts。与在第三列中不同的位的量

(2之间的异或的比特计数)这个最后一列应后3-5次迭代保持大约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 

不那么类似他

除了标准的随机实现这被称为一个线性同余RNG应不存在第i最好伪随机实现,但最有效的使用存储器(一段2^48只有一个64位字)

nterested乘法器是0x5deece66dL,c是0xbL

+0

为了让这里的每个人都清楚 - 在这种情况下“不是最好的伪随机实现”仅仅意味着在2^48个不同的数字后,我们将再次获得相同的数字。在其他 - 通常更重要的方面 - java随机实现相当不错。毕竟,如果每次我们想要生成一个数字,我们就会逐渐增加一个种子,我们会平均得到2^64的时间长,但没有人会认为生成器更好;) – Voo 2011-05-28 20:51:26

+1

实际上还有其他问题与来自全知的wiki的LCG:http://en.wikipedia.org/wiki/Linear_congruential_generator#Advantages_and_disadvantages_of_LCGs我在谈论连续数字之间的关联 – 2011-05-28 20:59:04

2

你的两个种子(PRNG状态)只有最低有效位不同。考虑到PRNG通常只是做一些异想天开的事情,所以这不应该太令人惊讶。

不管怎样,你不应该这样使用Random。 PRNG的状态将在每次nextInt方法被更新(状态/种子将由48可用位的约50%变化)。这就是你应该关心的一切。

1

据我所知,你需要一个随机数序列,它依赖于一些计算出来的种子,这样你可以在任何时候重新生成序列。是对的吗?

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

0

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

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

+1

但他没有使用'任何给定种子生成的序列'。他使用长度为1的序列,然后稍微改变种子。他滥用API。这在Random中没有出现问题,这是通过使用SecureRandom解决的。 – EJP 2011-05-29 01:04:36

+1

@EJP他的问题不是关于生成的流的随机性,而是在用附近的种子重新播种之后缺少初始元素。这可能会在初始种子生成时发生,比如使用一天中的时间。这种可预见性不仅从伪随机的角度来看很差,而且可能对安全有害。这个问题折磨了早期的TCP/IP协议栈。这就是为什么'SecureRandom'在其名称中具有“安全”的原因,以及为什么要在后来的JDK实现中解决这个问题。 – WReach 2011-05-29 01:21:14