シードに基づくランダムノイズ


16

私は現在、ピクセルの「座標」に基づいて画面上にランダムなノイズを生成するプログラムに取り組んでいます。プログラムを再起動するたびに、座標は同じ色になるはずです。ただし、Javaのutil.Randomを使用すると、私が望む結果は私が望むほどランダムではありません。

プリントスクリーン

結合された座標を使用すると(互いに隣接する両方の座標から形成される1つの整数のように)、各座標は異なる数になると思いました。その番号をシードとして使用することにより、各座標に異なる乱数を取得して、その座標の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回再シードされます。現時点ではこれは問題ではありませんが、後でより良いパフォーマンスが必要になった場合に備えて、このコードを変更することを検討します。


3
Javaのランダムについて はわかりませんが、実際のランダムではないことは確かです...読むen.wikipedia.org/wiki/Pseudorandom_number_generatorこれらのパターンが表示される理由は理解できます。
Salketer

23
他の答えから欠けている重要なもの:すべてのピクセルにRNGを再シードしないでください。それを一度シードし、それに基づいて画像内のすべてのピクセルの連続値を生成します。
コンラッドルドルフ

4
注:擬似乱数の生成は、1次元で一様に分布する場合がありますが、複数の次元を使用すると失敗します... 3D(r、g、b、および3つの異なる座標)でポイントを効果的に生成しているため、ランダムジェネレーターが必要です生成する値が均一に分布するだけでなく、生成するトリプレットも3D空間に均一に分布することを保証します。
バクリウ

6
@Bakuriu X、Y、Zが独立した均一なランダム変数である場合、(X、Y、Z)は3D空間で均一であると確信しています。
ジャックM

2
Mersenne Twisterなど、さまざまなRNGを使用して実験できます。
ケビン

回答:


21

Javaのjava.util.Randomクラスは通常、ゲームでの使用に十分な擬似乱数のシーケンスを提供します1。ただし、その特性は、シードに基づく複数のサンプルのシーケンスにのみ適用されます。シード値を増分してRNGを再初期化し、各シーケンスの最初の値のみを見ると、ランダム性の特性はほとんど同じではありません。

代わりにできること:

  • 同じシードを使用して、一度にピクセルのチャンク全体を生成します。たとえば、ピクセル425:487のカラー値が必要な場合、座標400:400をRNGに送り、10000のランダムカラーを生成し、インデックス2587(25 * 100 + 87)のカラーを使用します。その方法で生成されたチャンクは、そのチャンクの各ピクセルごとに10000のランダムな色を再生成しないようにキャッシュする必要があります。
  • 乱数ジェネレーターを使用する代わりに、メッセージダイジェスト関数を使用して、座標ペアを色の値に変換します。ほとんどのMDFの出力は、ほとんどのランダム性のテストを満たすのに十分なほど予測不可能です。通常、出力はRGB値に必要な24ビットを超えますが、通常、それらを切り捨てても問題はありません。

    パフォーマンスを向上させるために、メッセージダイジェスト生成とチャンクを組み合わせることができます。ダイジェスト関数の1つの出力の全長を使用するのに十分な大きさの小さなピクセルの塊を生成します。

1 次の数字を誰も予測できないことが絶対に不可欠な場合は、より遅いが予測不可能な数字を使用するjava.security.SecureRandom


13

座標は、プログラムを再起動する全員と同じ色にする必要があります

その場合、Perlin noisesimplex noiseなどの決定論的なノイズ関数を使用する必要があります。

Perlinノイズの詳細については、この写真を参照してください。きれいな写真もあります。

ほとんどの場合、組み込みrandom()関数または類似の関数を使用すると、プログラムを実行するたびに異なる値が得られます。入力またはその他の擬似ランダム値としてクロックを使用する可能性があるためです。

もう1つのオプションは、「ノイズマップ」を一度オフラインで生成し、それを後で乱数ソースとして使用することです。

実装では、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ノイズも考慮しました。それを調べて、それがより良く機能するかどうか確かめるでしょう。しかし、連結問題が完全にそれを解決するようには見えないため、Javaがパターンを作成する理由はまだわかりません
トンボ

1
ピクセルを生成する前に、ランダムに一定のシード値をランダムにシードするだけでは不十分ですか?
ジャックM

1
@JackMプレイ中のPRNGアルゴリズムに完全に依存します。
3Dave

4
「かつて、次の値のシードとして各値を使用するrand()実装を見ました。」それは、ほとんどの非暗号化擬似乱数ジェネレーターがどのように機能するのですか?前の乱数(または状態)を入力として使用して、次の乱数/状態を生成します。
JAB

2
@DavidLively 実質的にすべての PRNGは、内部状態が生成する数値の範囲(メルセンヌツイスターなど)より大きくない限り、これまたは同等のことを行います。その場合でも、乱数のシーケンスはもちろんシードによって完全に決定されます。
コンラッドルドルフ

9

あなたが何をしているか正確に見てみましょう:

  • すべてのピクセルを1つずつループします
  • 各ピクセルに対して、その座標の連結をシードとして使用します
  • 次に、指定されたシードから新しいランダムを開始し、3つの数字を取り出します

これはすべて大丈夫ですが、次の理由でパターンを受け取っています:

1,11のピクセルと11,1のピクセルには、両方とも111がシードされているため、確実に同じ色になります。

また、常に同じ方法で循環する限り、1つのジェネレーターのみを使用でき、ピクセルごとに1つ使用する必要はありません。画像全体に1つで十分です!疑似ランダム性のため、何らかのパターンが依然として存在します。@David_Livelyは、ノイズアルゴリズムを使用するのに適しています。これにより、ランダムに見えるようになります。


問題は、画像のビューが(正の座標に)移動できる必要があることです。したがって、このアプローチは完全には機能しません
トンボ

4
実際、「すべてこれ」大丈夫とは思えません。シード自体が暗号化RNGからのものでない限り、すべてのピクセルに決定論的なRNGを再シードすることはひどい戦略です(さらに、配布とは無関係の理由で不安定な戦略です)。
コンラッドルドルフ

このコンテキストで数字を連結する正しい方法を含めることができます。すなわち。(x + y * width)
Taemyr

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などのアルゴリズムに基づいていることです。MD5は暗号的に安全(つまり、本当に、本当にランダム)ですが、非常に遅いです。ランダムピクセルが必要になるたびに暗号化ハッシュ関数を使用すると、非常に深刻なパフォーマンスの問題が発生します。

ただし、目的にも十分にランダムでありながら高速な値を生成できる非暗号化ハッシュ関数があります。私がいつも連絡しているのはmurmurhashです。私はJavaユーザーではありませんが、少なくとも1つの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十分にランダムです。あなたは2つの主な理由でそれを間違って使用しています。

  • 繰り返し再播種するようには設計されていません。ランダムプロパティは、乱数の単一のシーケンスに対してのみ有効です。
  • 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

これが、私が思いついた1行の静的シェーダー関数-ポルターガイスト(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を使用して任意のブラウザーに簡単に含めることができます。

楽しみのために、私はすべてのデバイスの品質とパフォーマンスでポルターガイストに勝つために誰にも挑戦を投げかけます。騒々しい幽霊のルール!無敗!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.