Чтобы дать некоторый контекст, я написал базовую реализацию шума Perlin в Java, и когда дело дошло до внедрения посева, я столкнулся с ошибкой, которую я не мог объяснить.
Чтобы каждый раз генерировать одни и те же случайные весовые векторы для одного и того же семени независимо от того, какой уровень шума будет задан, и в каком порядке я создал новое семя (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
- самодельный класс для векторной математики.
Однако при запуске программа генерирует очень плохой шум:
После некоторого копания я обнаружил, что первый элемент каждого вектора очень схож (и, следовательно, первый вызов nextDouble()
после каждого вызова setSeed()
), в результате чего первый элемент каждого вектора в векторной сетке является схожим.
Это можно доказать, выполнив:
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()
(я не проверял другие типы случайности) и/или предупреждать людей об этой проблеме.
Конечно, это может быть просто ошибка реализации от моего имени, и я был бы умен, если бы это было указано мне.