시드 기반의 랜덤 노이즈


16

현재 픽셀의 '좌표'를 기반으로 화면에서 무작위 노이즈를 생성 해야하는 프로그램을 개발 중입니다. 프로그램을 다시 시작할 때마다 좌표의 색상이 동일해야합니다. 그러나 Java의 util.Random을 사용하면 원하는 결과가 무작위가 아닙니다.

프린트 스크린

결합 된 좌표를 사용하면 (두 좌표에서 서로 옆에 형성된 하나의 정수에서와 같이) 각 좌표의 숫자가 다를 것이라고 생각했습니다. 그 숫자를 시드로 사용함으로써 각 좌표에 대해 해당 좌표의 RGB 값에 사용할 다른 난수를 얻을 것으로 예상했습니다.

이것은 내가 사용한 코드입니다.

public class Generate {

static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(Integer.valueOf(Integer.toString(x)+Integer.toString(y)));
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

Java의 임의 함수가 작동하는 방식으로 인해 프로그램이 작성하는 패턴입니까, 아니면 내가 잘못하고 다른 접근법을 시도해야합니까?

업데이트 : 이제 다음 코드를 사용하여 연결과 관련된 문제를 해결하려고했습니다.

public static int TileColor(int x, int y){  
            Randomy = new Random(y);
            Randomx = new Random(x);
            Random = new Random(Integer.valueOf(Integer.toString(Randomx.nextInt(1234))+Integer.toString(Randomy.nextInt(1234))));
            int b = 1 + Random.nextInt(100);
            int g = 1 + Random.nextInt(100);
            int r = 1 + Random.nextInt(100);
            int color = -Color.rgb888(r, g, b);
            return color;
}

어떻게 든 이것은 (내 의견으로는) 충분히 임의의 이미지를 제공했습니다.

야옹 이미지

그러나이 코드는 픽셀 당 세 번 시드되었습니다. 지금은 이것이 문제가되지 않지만 나중에 더 나은 성능이 필요한 경우를 대비하여이 코드를 변경하는 것이 좋습니다.


3
확실하지 자바의 임의하지만 난 확신에 대한 실질 임의하지 ... 읽기 en.wikipedia.org/wiki/Pseudorandom_number_generator 당신이 그 패턴을 참조 왜 당신은 이해할 수있을 겁니다.
Salketer

23
다른 답변에서 누락 된 중요한 점은 모든 픽셀에 대해 RNG를 다시 시드하지 마십시오. 그 종자 번을 그 기반으로 이미지의 모든 픽셀에 대한 연속적인 값을 생성합니다.
Konrad Rudolph

4
참고 : 의사 난수 수는 1 차원 으로 균일하게 분포 될 수 있지만 2 차원 이상을 사용하는 경우 실패합니다. 효과적으로 3D (r, g 및 b 및 3 개의 다른 좌표)로 점을 생성하므로 랜덤 생성기가 필요합니다. 생성하는 값이 균일하게 분포 될뿐만 아니라 생성하는 3 중항이 3D 공간에 균일하게 분포되도록합니다.
Bakuriu

6
@Bakuriu X, Y 및 Z가 독립적 인 균일 랜덤 변수이면 3D 공간에서 (X, Y, Z)가 균일하다고 확신합니다.
Jack M

2
Mersenne Twister 와 같은 다른 RNG를 사용하여 실험 할 수 있습니다.
Kevin

답변:


21

Java의 java.util.Random클래스는 일반적으로 게임 1 에 사용하기에 충분한 의사 난수 시퀀스를 제공합니다 . 그러나이 특성은 시드 기반의 여러 샘플 시퀀스에만 적용됩니다. 시드 값을 증분하여 RNG를 다시 초기화하고 각 시퀀스의 첫 번째 값만 보면 랜덤 성 특성이 거의 좋지 않습니다.

대신 할 수있는 일 :

  • 한 번에 전체 픽셀 덩어리를 생성하려면 동일한 시드를 사용하십시오. 예를 들어, 픽셀 425 : 487의 색상 값이 필요한 경우 좌표 400 : 400을 RNG에 공급하고 10000 개의 임의 색상을 생성하고 인덱스 2587 (25 * 100 + 87)에서 색상을 사용하십시오. 이러한 방식으로 생성 된 청크는 해당 청크의 모든 단일 픽셀에 대해 10000 개의 임의 색상을 재생성하지 않도록 캐시되어야합니다.
  • 난수 생성기를 사용하는 대신 메시지 요약 기능을 사용하십시오. 을 하여 좌표 쌍을 색상 값으로 변환하십시오. 대부분의 MDF의 결과는 대부분의 무작위성 테스트를 수행 할만큼 예측할 수 없습니다. 출력은 일반적으로 RGB 값에 필요한 24 비트보다 크지 만 잘리는 것은 문제가되지 않습니다.

    성능을 향상시키기 위해 메시지 요약 생성과 청크를 결합 할 수 있습니다. 다이제스트 함수의 한 출력의 전체 길이를 사용하기에 충분히 큰 작은 픽셀 덩어리를 생성합니다.

아무도 다음 숫자를 예측할 수없는 것이 절대적으로 필요한 경우 1 느리지 만 예측하기 어려운 것을 사용하십시오.java.security.SecureRandom


13

좌표는 프로그램을 다시 시작할 때마다 같은 색이어야합니다.

이 경우 Perlin 노이즈 또는 심플 렉스 노이즈 와 같은 결정 성 노이즈 함수를 사용하려고합니다. .

( 예쁜 사진과 함께 Perlin 노이즈에 대한 자세한 내용은이 질문을 참조하십시오. )

대부분의 경우 내장 random()또는 유사한 기능을 사용하면 시계를 입력 또는 다른 의사 난수 값으로 사용할 수 있으므로 프로그램을 실행할 때마다 다른 값을 제공합니다.

또 다른 옵션은 오프라인에서 "노이즈 맵"을 한 번 생성 한 다음 나중에 난수 소스로 사용하는 것입니다.

구현에서 x 및의 문자열 표현을 연결합니다 y. 도메인 전체에서 고유하지 않으므로 좋지 않습니다. 예를 들어

x    y   concatenated
40   20  4020
402   0  4020
10   10  1010
101   0  1010
12   34  1234
123   4  1234
1   234  1234

행운을 빕니다!


1
연결된 숫자에 대한 좋은 지적. 그러나 프로그램을 여러 번 실행하면 프로그램은 항상 동일한 결과를 제공합니다. 나는 perlin / simplex noise도 고려했으며, 그것을 조사하고 더 잘 작동하는지 확인할 수 있습니다. 그러나 Java가 왜 패턴을 작성하는지 아직 확실하지 않습니다. 연결 문제가 완전히 해결하지 못하는 것 같습니다.
잠자리

1
픽셀을 생성하기 전에 상수 시드 값으로 랜덤을 시드하는 것만으로는 충분하지 않습니까?
Jack M

1
@JackM 전적으로 PRNG 알고리즘을 사용합니다.
3Dave

4
"한 번은 각 값을 다음 값의 시드로 사용하는 rand () 구현을 보았습니다." 대부분의 비 암호화 의사 난수 생성기가 작동하는 방식이 아닙니까? 이전 난수 (또는 상태)를 입력으로 사용하여 다음 난수 / 상태를 생성합니다.
JAB

2
@DavidLively 사실상 모든 PRNG는 내부 상태가 생성하는 수의 범위 (예 : 메르 센 트위스터)보다 크지 않는 한 거의 모든 PRNG는 이와 동등한 작업을 수행합니다.
Konrad Rudolph

9

정확히 무엇을하고 있는지 보자.

  • 모든 픽셀을 하나씩 반복합니다
  • 각 픽셀에 대해 좌표의 연결을 시드로 사용합니다.
  • 그런 다음 주어진 씨앗에서 새로운 무작위를 시작하고 3 개의 숫자를 꺼냅니다.

이 모든 것이 괜찮은 것처럼 들리지만 다음과 같은 이유로 패턴이 수신됩니다.

1,11의 픽셀과 11,1의 픽셀에는 모두 111이라는 숫자가 뿌려 지므로 동일한 색상을 가지게됩니다.

또한 항상 같은 방식으로 순환하는 한, 하나의 생성기 만 사용할 수 있으며 각 픽셀마다 하나씩 사용할 필요는 없습니다. 전체 이미지 중 하나가 수행됩니다! 의사 난수 (pseudo-randomness) 때문에 여전히 어떤 종류의 패턴이있을 것입니다. @David_Lively는 일부 노이즈 알고리즘을 사용하는 것이 옳습니다.보다 무작위로 보일 것입니다.


문제는 이미지의 시야가 양의 좌표로 이동할 수 있어야한다는 것입니다. 이 방법이 완전히 작동하지 않습니다 그래서
잠자리

4
실제로“이 모든 것” 옳은 것은 아닙니다 . 시드 자체가 암호화 RNG에서 나온 것이 아니라면 모든 픽셀에 결정 론적 RNG를 시드하는 것은 끔찍한 전략입니다.
Konrad Rudolph

이 맥락에서 숫자를 연결하는 올바른 방법을 포함시킬 수 있습니다. 즉. (x + y * width)
Taemyr 2013 년

1

색상 생성기를 만든 다음 타일의 색상을 생성하십시오. 한 번만 시드! 적어도 타일 당 그 이상을 시드 할 필요는 없습니다.

public class RandomColorGenerator {
  private final int minValue;
  private final int range;
  private final Random random;
  public RandomColorGenerator(int minValue, int maxValue, Random random) {
    if (minValue > maxValue || (long)maxValue - (long)minValue > (long)Integer.MAX_VALUE) {
      throw new IllegalArgumentException();
    }
    this.minValue = minValue;
    this.range = maxValue - minValue + 1;
    this.random = Objects.requireNonNull(random);
  }

  public int nextColor() {
    int r = minValue + random.nextInt(range);
    int g = minValue + random.nextInt(range);
    int b = minValue + random.nextInt(range);
    return -Color.rgb888(r, g, b);
  }
}

public class Tile {
  private final int[][] colors;
  public Tile(int width, int height, RandomColorGenerator colorGenerator) {
    this.colors = new int[width][height];
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        this.colors[x][y] = colorGenerator.nextColor();
      }
    }
  }

  public int getColor(int x, int y) {
    return colors[x][y];
  }
}

사용법은 다음과 같습니다.

RandomColorGenerator generator = new RandomColorGenerator(1, 100, new Random(0xcafebabe));
Tile tile = new Tile(300, 200, generator);
...
// getting the color for x, y:
tile.getColor(x, y);

결과가 마음에 들지 않으면 Random씨앗을 바꾸십시오 . 또한 모든 클라이언트가 동일한 이미지를 갖도록 시드 및 크기 만 저장 / 통신해야합니다.


1

랜덤을 사용하는 대신 MD5와 같은 해시 다이제스트 사용을 고려하십시오. 특정 입력을 기반으로 '무작위'값을 예측하기 어렵지만 항상 동일한 입력에 대해 동일한 값을 제공합니다.

예:

public static int TileColor(int x, int y){
        final MessageDigest md = MessageDigest.getInstance("MD5");
        final ByteBuffer b = ByteBuffer.allocate(8);
        b.putInt(x).putInt(y);
        final byte[] digest = md.digest(b.array());
        return -Color.rgb888(digest[0], digest[1], digest[2]);
}

참고 : Color.rgb888 (..)의 출처를 모르므로 허용되는 범위가 무엇인지 알 수 없습니다. 0-255는 정상입니다.

고려해야 할 개선 사항 :

  • MessageDigest 및 ByteBuffer 변수를 클래스 외부에서 작성하여 성능을 향상 시키십시오. 이를 수행하기 위해 ByteBuffer를 재설정해야하며 메소드는 더 이상 스레드로부터 안전하지 않습니다.
  • 다이제스트 배열에는 바이트 값 0-255가 포함됩니다. 다른 범위를 원하면 그에 대한 수학을 수행해야합니다.
  • 다른 '무작위'결과를 원하는 경우 일종의 '씨앗'을 추가 할 수 있습니다. 예를 들어 ByteBuffer.allocate (12)로 변경하고 .putInt (seed)를 추가하십시오.

1

다른 사람들은 당신이 원하는 행동을 얻는 한 가지 방법은 "메시지 다이제스트 함수"라고하는 해시 함수를 사용하는 것이라고 지적했습니다. 문제는 이것들이 종종 암호화 적으로 안전하지만 (실제로, 실제로는 무작위 임) MD5와 같은 알고리즘을 기반으로한다는 점입니다. 임의의 픽셀이 필요할 때마다 암호화 해시 기능을 사용하면 성능이 매우 저하 될 수 있습니다.

그러나, 목적에 맞게 임의의 값을 생성 할 수있는 비 암호화 해시 함수도 있습니다. 내가 일반적으로 도달하는 것은 murmurhash 입니다. Java 사용자는 아니지만 사용 가능한 Java 구현이 하나 이상있는 것 같습니다 . 각 픽셀을 한 번에 생성하고 텍스처에 저장하는 대신 좌표에서 생성해야하는 경우,이를 수행하는 것이 좋습니다.


1

나는 2000 이상으로 소수를 사용할 것입니다 (최대 일반적인 해상도
).

public class Generate {

    static Random Random;

    public static int TileColor(int x, int y){          
        Random = new Random(x + 2213 * y);
        int b = 1 + Random.nextInt(50);
        int g = 1 + Random.nextInt(50);
        int r = 1 + Random.nextInt(50);
        int color = -Color.rgb888(r, g, b);
        return color;
    }
}

0

Random충분히 무작위입니다. 두 가지 주요 이유로 잘못 사용하고 있습니다.

  • 반복적으로 다시 시드하도록 설계되지 않았습니다. 난수 속성은 단일 난수 시퀀스에만 적용됩니다.
  • Integer.valueOf(Integer.toString(x)+Integer.toString(y))시드하는 픽셀 사이 에는 큰 상관 관계 가 있습니다.

/programming/9624963/java-simplest-integer- 의 답변에서 해시 함수 (Integer.getHashCode를 사용하지 않음)를 선택할 수있는 다음 코드의 변형을 사용합니다. 해시시

public static int TileColor(int x, int y) {
    return hash(x ^ hash(y));
}

해시 함수가 될 수있는 곳


0

시스템의 현재 시계 시간을 다음과 같은 시드로 사용해 볼 수 있습니다.

Random random = new Random(System.currentTimeMillis())

잘하면 그것은 더 임의의 값을 생성합니다.


그러나 그것이 항상 댐 좌표에 대한 댐 값을 생성하지는 않습니다.
잠자리

0

여기에 내가 한 줄 정적 쉐이더 기능이 있습니다-poltergeist (Noisy Ghost).

2D 좌표와 시드를 사용하고 요청에 따라 모노톤으로 렌더링합니다. 화면 해상도에 관계없이 실시간 fps로 실행됩니다. 이것이 바로 GPU입니다.

// poltergeist (noisy ghost) pseudo-random noise generator function
// dominic.cerisano@standard3d.com 03/24/2015

precision highp float;

float poltergeist(in vec2 coordinate, in float seed) 
{
    return fract(sin(dot(coordinate*seed, vec2(12.9898, 78.233)))*43758.5453); 
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{   
    fragColor = vec4(poltergeist(fragCoord, iGlobalTime)); 
}

GL을 지원하는 모든 장치 (모바일)의 해상도, 텍스처, 화면과 거의 비슷합니다.

지금 여기서 실행되는 것을보십시오!

https://www.shadertoy.com/view/ltB3zD

표준 opengl을 사용하는 Java 프로그램 또는 표준 webgl을 사용하는 모든 브라우저에이 셰이더를 쉽게 포함시킬 수 있습니다.

재미를 위해 모든 기기에서 Poltergeist의 품질과 성능을 이길 수있는 건틀릿을 버립니다. 시끄러운 유령 규칙! 무패!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.