我发现了一些奇怪的使用Java的Random类的随机数的产生。 基本上,如果创建使用接近的种子(例如在1和1000之间),由每个发电机产生将几乎相同的第一值的多个随机的对象,但接下来的值看起来很好(ⅰ没有搜索另外的)。
下面是与从0到9的种子两个第一生成双:
- 0 0.730967787376657 0.24053641567148587
- 1 0.7308781907032909 0.41008081149220166
- 2 0.7311469360199058 0.9014476240300544
- 3 0.731057369148862 0.07099203475193139
- 4 0.7306094602878371 0.9187140138555101
- 5 0.730519863614471 0.08825840967622589
- 6 0.7307886238322471 0.5796252073129174
- 7 0.7306990420600421 0.7491696031336331
- 8 0.7302511331990172 0.5968915822372118
- 9 0.7301615514268123 0.7664359929590888
而从991到1000:
- 991 0.7142160704801332 0.9453385235522973
- 992 0.7109015598097105 0.21848118381994108
- 993 0.7108119780375055 0.38802559454181795
- 994 0.7110807233541204 0.8793923921785096
- 995 0.7109911564830766 0.048936787999225295
- 996 0.7105432327208906 0.896658767102804
- 997 0.7104536509486856 0.0662031629235198
- 998 0.7107223962653005 0.5575699754613725
- 999 0.7106328293942568 0.7271143712820883
- 1000 0.7101849056320707 0.574836350385667
这里是表示与种子生成从0到100000的第一个值的图。
首先随机双基于该种子产生的:
我搜索了这方面的信息,但我没有看到任何东西指的是这种精确的问题。 我知道,有与个LCG算法很多问题,但我不知道这一个,我想知道,这是一个已知的问题。
而且,你知道,如果这个问题只对第一个值(或前几个值),或者如果它是更普遍的使用密切种子应避免?
谢谢。
你会通过下载和阅读可以提供最好的服务Random
来源,以及对伪随机生成一些文件,但这里有一些源的相关部分。 首先,有控制算法三个恒参数:
private final static long multiplier = 0x5DEECE66DL;
private final static long addend = 0xBL;
private final static long mask = (1L << 48) - 1;
乘法器工程以大约2 ^ 34和变化,掩模2 ^ 48 - 1,并且加数是相当接近为0这一分析。
当你创建一个随机配有种子,构造函数调用setSeed
:
synchronized public void setSeed(long seed) {
seed = (seed ^ multiplier) & mask;
this.seed.set(seed);
haveNextNextGaussian = false;
}
你提供的种子非常接近零,时设置由支配所以初始种子值multiplier
时,两者或运算在一起。 在所有测试情况下具有接近零的种子,在内部使用的种子是大致2 ^ 34; 但它很容易地看到,即使你提供了非常大的种子数,类似的用户提供的种子将产生类似的内部种子。
最后一块为next(int)
方法,这实际上产生基于当前种子所请求长度的随机整数,然后更新所述种子:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
这就是所谓的“线性同余”伪随机生成器,这意味着其由一个常数乘法器当前种子相乘,然后加上一个常数加数(然后掩蔽采取低48位,在这种情况下)产生的每个连续种子。 发电机的质量是通过乘法器和加数的选择确定,但是从所有这样的发电机的输出中可以很容易地预测基于当前输入和具有一组周期它自身重复之前(因此建议不要在敏感使用它们应用程序)。
你看到相似的初始输出的原因nextDouble
给出类似种子的是,因为下一个整数的计算只涉及一个乘法和加法,下一个整数的大小没有太大的影响在低位的差异。 下一个双计算涉及计算基于种子大整数和由另一个(恒定)大的整数除以它,并且将结果的大小主要是受整数的大小。
下一个种子的重复计算将会放大的种子,因为由常数倍增重复乘法的较低位的差异,因为48位掩码抛出每次最高位,直到最后你看到的是什么样子的甚至蔓延。
我不会把这种现象称之为一个“问题”。
而且,你知道,如果这个问题只对第一个值(或前几个值),或者如果它是更普遍的使用密切种子应避免?
连续的号码之间的关联模式是与非加密的PRNG一个普遍的问题,而这仅仅是一个表现。 的相关性(严格自相关)是在算法(多个)所依据的数学固有的。 如果你想了解的是,你应该通过阅读计算机编程第3章的Knuth的艺术的部分开始。
如果您需要非预测性,你应该使用(真)随机种子进行Random
......或让系统选择一个“相当随意”一个给你; 例如,使用无参数的构造函数。 或者更好的是,使用真正的随机数源或加密质量PRNG,而不是Random
。
作为记录:
- 的Javadoc(Java 7中)没有规定如何随机()种子本身。
- 实行
Random()
在Java 7中为Linux,从纳秒时钟播种,用“唯一标志”序列进行异或运算。 在“唯一标志”序列是LCG,它使用不同的乘数,并且其状态是静态的。 这是为了避免种子的自相关...
这是一个伪随机种子一个相当典型的行为 - 他们不需要提供完全不同的随机序列,它们只提供了保证,你可以再次获得,如果你使用相同的种子相同的序列。
行为发生,因为PRNG的数学形式的 - 在Java一个使用线性同余发生器,所以你刚刚看到的结果,通过一轮线性同余发生器运行的种子。 这是不够的,完全混合了所有的位模式,因此你看到类似的种子相似的结果。
最好的策略可能只是使用非常不同的种子 - 一个办法是通过哈希您当前使用的种子值来获得这些。
通过使的随机种子(例如,使用上System.currentTimeMillis的()或System.nanoTime()的种子生成一些数学函数),您可以得到更好的随机结果。 也可以看这里了解更多信息