gpt4 book ai didi

java - Java中setSeed之后的第一个随机数总是相似的

转载 作者:IT老高 更新时间:2023-10-28 20:50:05 25 4
gpt4 key购买 nike

为了提供一些背景信息,我一直在用 Java 编写一个基本的 Perlin 噪声实现,在实现播种时,我遇到了一个我无法解释的错误。

为了无论查询哪组坐标的噪声水平以及以什么顺序每次都为同一个种子生成相同的随机权重 vector ,我生成了一个新种子(newSeed),基于原始种子和权重 vector 坐标的组合,并通过运行将其作为权重 vector 随机化的种子:

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

其中NVector是一个自制的 vector 数学类。

但是,在运行时,程序会产生非常糟糕的噪音: Very bad noise, with vertical streaks

经过一番挖掘,我发现每个 vector 的第一个元素非常相似(因此每个 setSeed() 调用之后的第一个 nextDouble() 调用)导致在 vector 网格中每个 vector 的第一个元素是相似的。

这可以通过运行来证明:

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

在这里您可以看到第一个平均差异明显小于其他平均差异,并且似乎随着种子的增加而减小。

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

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

导致:

enter image description here

我想知道为什么在第一次调用 nextDouble() 时会出现这种糟糕的变化(我没有检查其他类型的随机性)和/或提醒人们注意这个问题。

当然,这可能只是代表我的一个实现错误,如果有人向我指出,我将非常感激。

最佳答案

Random 类被设计为伪随机数的低开销源。但是“低开销”实现的结果是数字流具有远非完美的属性......从统计角度来看。您遇到了其中一种缺陷。 Random 被记录为线性同余生成器,并且此类生成器的属性众所周知。

有多种方法可以解决此问题。例如,如果你小心,你可以隐藏一些最明显的“差”特征。 (但建议您进行一些统计测试。您看不到添加到第二张图像的噪声的非随机性,但它仍然可能存在。)

或者,如果您想要保证良好统计特性的伪随机数,那么您应该使用 SecureRandom 而不是 Random。它的开销显着增加,但您可以放心,许多“聪明人”会花费大量时间在算法的设计、测试和分析上。

最后,创建一个 Random 的子类相对简单,该子类使用另一种算法生成数字;见 link .问题是您必须选择(或设计)并实现适当的算法。


将此称为“问题”值得商榷。这是 LCG 的众所周知和理解的特性,使用 LCG 是一种有意识的工程选择。人们想要低开销的 PRNG,但低开销的 PRNG 的属性很差。坦斯塔夫。

当然,Oracle 不会考虑在 Random 中进行更改。确实,不改变的原因在javadoc 中有明确说明。对于 Random 类。

"In order to guarantee this property, particular algorithms are specified for the class Random. Java implementations must use all the algorithms shown here for the class Random, for the sake of absolute portability of Java code."

关于java - Java中setSeed之后的第一个随机数总是相似的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27760450/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com