java - 클래스 - 자바 배열 랜덤 추출




Java에서 setSeed 이후의 첫 번째 난수는 항상 비슷합니다. (2)

이는 알려진 문제입니다. 비슷한 씨앗도 비슷한 값의 첫 번째 값을 생성합니다. 랜덤은 실제로 이렇게 사용하도록 설계되지 않았습니다. 좋은 씨앗을 가진 인스턴스를 만든 다음 적당한 크기의 "임의의"숫자 시퀀스를 생성해야합니다.

현재의 솔루션은 괜찮아 보입니다. 또한 문제를 해결하기 위해 설계된 해싱 / 믹싱 기능을 사용할 수도 있습니다 (그리고 옵션으로 출력을 시드로 사용). 예를 들어보십시오 : 2D 잡음 생성을위한 파라 메트릭 무작위 함수

컨텍스트를 제공하기 위해 필자는 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 는 벡터 수학을위한 자체 제작 클래스입니다.

그러나 실행하면 프로그램에서 매우 나쁜 잡음을 생성합니다.

일부 파고 nextDouble() 나는 각 벡터의 첫 번째 요소가 매우 유사하다는 것을 알았고 (즉, 각 setSeed() 호출 후에 첫 번째 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

여기에서 첫 번째 평균 차이는 나머지보다 현저히 작으며 씨앗이 많을수록 감소한 것으로 나타났습니다.

이와 같이 weight 벡터를 설정하기 전에 nextDouble() 간단한 더미 호출을 추가하여 Perlin 노이즈 구현을 수정할 수있었습니다.

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

를 야기하는:

nextDouble() (다른 유형의 임의성을 확인하지 못함 nextDouble() 에 대한 첫 번째 호출에서 이러한 나쁜 변형이 발생하고 / 또는이 문제점에 대해 사람들에게 경고하는 이유를 알고 싶습니다.

물론, 그것은 저를 위해서만 구현 오류가 될 수 있습니다, 나는 그것이 나에게 지적 되었다면 나는 위대 할 것입니다.


setSeed 를 루프 밖으로 이동하십시오. Java의 PRNG는 선형 합동 생성기이므로 순차 값을 시드에 배치하면 반복의 반복을 통해 상관 관계가있는 결과가 보장됩니다.

추가

나는 회의에 출입하기 전에 그 일을 망 쳤고, 이제 나는 위에서 말한 것을 설명 할 시간을 갖습니다.

Schrage의 휴대용 프라임 모듈러스 곱셈 선형 합자 생성기를 구현하는 루비 스크립트를 작성했습니다. 두 개의 LCG 복사본을 인스턴스화합니다 (둘 다 값 1로 시드 됨). 그러나 출력 루프의 각 반복에서 루프 인덱스를 기반으로 두 번째 복사본을 다시 시드했습니다. 코드는 다음과 같습니다.

# Implementation of a Linear Congruential Generator (LCG)
class LCG
  attr_reader :state
  M = (1 << 31) - 1    # Modulus = 2**31 - 1, which is prime

  # constructor requires setting a seed value to use as initial state
  def initialize(seed)
    reseed(seed)
  end

  # users can explicitly reset the seed.
  def reseed(seed)
    @state = seed.to_i
  end

  # Schrage's portable prime modulus multiplicative LCG
  def value
    @state = 16807 * @state % M
    # return the generated integer value AND its U(0,1) mapping as an array
    [@state, @state.to_f / M]
  end
end

if __FILE__ == $0
  # create two instances of LCG, both initially seeded with 1
  mylcg1 = LCG.new(1)
  mylcg2 = LCG.new(1)
  puts "   default progression     manual reseeding"
  10.times do |n|
    mylcg2.reseed(1 + n)  # explicitly reseed 2nd LCG based on loop index
    printf "%d %11d %f %11d %f\n", n, *mylcg1.value, *mylcg2.value
  end
end

여기에 출력 결과가 나와 있습니다.

   default progression     manual reseeding
0       16807 0.000008       16807 0.000008
1   282475249 0.131538       33614 0.000016
2  1622650073 0.755605       50421 0.000023
3   984943658 0.458650       67228 0.000031
4  1144108930 0.532767       84035 0.000039
5   470211272 0.218959      100842 0.000047
6   101027544 0.047045      117649 0.000055
7  1457850878 0.678865      134456 0.000063
8  1458777923 0.679296      151263 0.000070
9  2007237709 0.934693      168070 0.000078

열은 LCG에 의해 생성 된 기본 정수가 뒤 따르는 반복 번호이며 범위 (0,1)로 스케일 된 결과입니다. 왼쪽 열은 자체적으로 진행할 수있는 경우 LCG의 자연스러운 진행을 보여주고 오른쪽 세트는 각 반복을 다시 시드 할 때 발생하는 상황을 보여줍니다.





noise