setSeed之后的第一个随机数总是相似的

setSeed之后的第一个随机数总是相似的

为了给出一些上下文,我已经在Java中编写了一个基本的Perlin噪声实现,而在实现播种时,我遇到了一个我无法解释的错误。

为了产生相同的随机权重向量,无论哪一组坐标的噪声级别被查询,并以什么顺序生成一个新的种子( newSeed ),基于原始种子和权重向量的坐标,并将其用作通过运行以下权重向量的随机化的种子:

rnd.setSeed(newSeed);
weight = new NVector(2);
weight.setElement(0, rnd.nextDouble() * 2 - 1);
weight.setElement(1, rnd.nextDouble() * 2 - 1);
weight.normalize()

其中, NVector是矢量数学的自制类。

但是,当运行时,程序产生了非常糟糕的噪音: 非常糟糕的噪音,有垂直条纹

在一些挖掘之后,我发现每个向量的第一个元素非常相似(因此在每次setSeed()调用之后nextDouble()调用第一个nextDouble() ),导致向量网格中每个向量的第一个元素相似。

这可以通过运行来证明:

long seed = Long.valueOf(args[0]);
int loops = Integer.valueOf(args[1]);
double avgFirst = 0.0, avgSecond = 0.0, avgThird = 0.0;
double lastfirst = 0.0, lastSecond = 0.0, lastThird = 0.0;
for(int i = 0; i<loops; i++)
{
    ran.setSeed(seed + i);
    double first = ran.nextDouble();
    double second = ran.nextDouble();
    double third = ran.nextDouble();
    avgFirst += Math.abs(first - lastfirst);
    avgSecond += Math.abs(second - lastSecond);
    avgThird += Math.abs(third - lastThird);
    lastfirst = first;
    lastSecond = second;
    lastThird = third;
}
System.out.println("Average first difference.: " + avgFirst/loops);
System.out.println("Average second Difference: " + avgSecond/loops);
System.out.println("Average third Difference.: " + avgSecond/loops);

其中找到在通过程序参数指定的一个种子范围之后调用了setSeed()方法之后生成的第一个,第二个和第三个随机数之间的平均差异; 对我来说,这些结果归结为:

C:\java Test 462454356345 10000
Average first difference.: 7.44638117976783E-4
Average second Difference: 0.34131692827329957
Average third Difference.: 0.34131692827329957

C:\java Test 46245445 10000
Average first difference.: 0.0017196011123287126
Average second Difference: 0.3416750057190849
Average third Difference.: 0.3416750057190849

C:\java Test 1 10000
Average first difference.: 0.0021601598225344998
Average second Difference: 0.3409914232342002
Average third Difference.: 0.3409914232342002

在这里您可以看到,第一个平均差异远远小于其余的差异,似乎随着种子的增加而下降。

因此,通过在设置权重向量之前添加一个简单的虚拟调用nextDouble() ,我能够修复我的perlin噪声实现:

rnd.setSeed(newSeed);
rnd.nextDouble();
weight.setElement(0, rnd.nextDouble() * 2 - 1);
weight.setElement(1, rnd.nextDouble() * 2 - 1);

导致:

在此输入图像说明

我想知道为什么第一次调用nextDouble() (我没有检查其他类型的随机性)发生和/或提醒人们这个问题的这个坏的变化。

当然,这可能只是我的一个执行错误,如果我指出我将会很有帮助。

采纳答案:

Random类被设计为伪随机数的低开销来源。 但是,“低开销”实现的后果是数字流具有从完整的方面很好的属性...从统计的角度来看。 你遇到了一个缺点。 Random记录为线性同余发生器,这种发生器的属性是众所周知的。

有多种处理方式。 例如,如果你小心,你可以隐藏一些最明显的“差”特征。 (但是建议您进行一些统计测试,您看不到第二个图像中添加的噪点中的非随机性,但仍可能存在。)

或者,如果您想要保证良好的统计属性的伪随机数,那么您应该使用SecureRandom而不是Random 。 它具有明显更高的开销,但您可以放心,许多“聪明人”将花费大量时间进行算法的设计,测试和分析。

最后,创建一个使用替代算法生成数字的Random的子类是比较简单的; 见链接 。 问题是您必须选择(或设计)并实施适当的算法。


称这个“ 问题 ”是有争议的。 这是一个众所周知的和理解的LCG的属性,使用LCG是一个很好的工程选择。 人们想要低开销的PRNG,但低开销的PRNG具有差的性能。 TANSTAAFL。

当然,这不是Oracle会考虑在Random改变的。 实际上,在Random Class的javadoc中清楚地说明了不改变的原因。

“为了保证这个属性,特定的算法是为Random指定的.Java实现必须使用这里所示的所有算法为Random类,为了Java代码的绝对可移植性。

参考更多解答:setSeed之后的第一个随机数总是相似的,转载请保留setSeed之后的第一个随机数总是相似的

更多:noise