rand()を使用するときにこの特定のカラーパターンが表示されるのはなぜですか?


170

私は次のように画像ファイルを作成しようとしました:

uint8_t raw_r[pixel_width][pixel_height];
uint8_t raw_g[pixel_width][pixel_height];
uint8_t raw_b[pixel_width][pixel_height];
uint8_t blue(uint32_t x, uint32_t y)
{
    return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
    return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
    return (rand()%2)? (y-x)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}

for (y=0; y<pixel_height; ++y)
{
    for (x=0; x<pixel_width; ++x)
    {
        raw_b[x][y]=blue(x, y);
        raw_g[x][y]=green(x, y);
        raw_r[x][y]=red(x, y);
    }
}

ランダム(ホワイトノイズ)になると思っていました。ただし、出力は興味深いものです。

理由を知っていますか?


編集する

これで、とは何の関係もないことは明らかですrand()

このコードも試してください:

for (x=0; x<pixel_width; ++x)
    for (y=0; y<pixel_height; ++y)
    {
        r[x][y] = (x+y);
        g[x][y] = (y-x);
        /* b[x][y] = rand()%2? x : y; */
    }


26
cos rand isnt rand-素晴らしいデモ。それは100%確定的です
pm100


50
@ pm100:bames53の答えが非常によく説明しているように、完全な乱数ジェネレーターを使用しても、同じパターンが得られます。
TonyK 2018

13
質問の言い訳:x座標とy座標を使用してピクセル値を計算しているので、これらの値が座標から独立していると思われるのはなぜですか。画像が均一にランダムに見えすぎる場合、それが必要ですよね?
Thomas Padron-McCarthy

15
学んだ教訓:乱数を使ってランダムなことをしてもランダムな結果は得られません:)
Hagen von Eitzen 2018

回答:


355

私は当初、他の誰もが持っていたのと同じ答えを出し、これをの問題までチョークで書きますrand()。しかし、私はそうする方が良いと考え、代わりにあなたの数学が実際に生成している分布を分析しました。

TL; DR:表示されるパターンは、基になる乱数ジェネレーターとは何の関係もありません。代わりに、プログラムが数値を操作する方法が原因です。

それらはすべて類似しているので、私はあなたの青い関数に固執します。

uint8_t blue(uint32_t x, uint32_t y) {
    return (rand() % 2)                  ? (x + y) % rand() :
           ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
                                           rand();
}

各画素値は、三つの機能の1つから選択される:(x + y) % rand()(x - y) % rand()、およびrand()

これらのそれぞれによって生成された画像だけを見てみましょう。

  • rand()

これはあなたが期待するものであり、単なるノイズです。これを「イメージC」と呼ぶ

ここに画像の説明を入力してください


  • (x + y) % rand()

ここでは、ピクセル座標を一緒に追加し、残りを乱数で除算しています。画像が1024x1024の場合、合計は[0-2046]の範囲になります。ダイビングに使用する乱数の範囲は[0、RAND_MAX]です。RAND_MAXは少なくとも32kで、一部のシステムでは20億です。言い換えれば、残りが16分の1である確率はせいぜい1倍です(x + y)。したがって、ほとんどの場合、この関数は+ x + y方向に向かって増加する青のグラデーションを生成します。

ただし、を返すため、最下位の8ビットのみを使用しているためuint8_t、幅256ピクセルのグラデーションのストライプが作成されます。

これを「イメージA」と呼ぶ

ここに画像の説明を入力してください


  • (x - y) % rand()

ここでは似たようなことをしますが、減算を行います。xがyよりも大きい限り、前の画像と同様のものが得られます。しかし、yがより大きい場合、結果は非常に大きな数にxなりyます。% rand()キックが発生して実際にノイズが発生するためです。

これを「イメージB」と呼びます

ここに画像の説明を入力してください

最終画像の各ピクセルは、関数rand() % 2とを使用して、これら3つの画像の1つから取得されます((x * y % 1024) % rand()) % 2。これらの最初のものは、50%の確率で選択するものとして読み取ることができます(rand()およびその下位ビットの問題は無視されます)。

以下rand() % 2はtrueの部分(白いピクセル)のクローズアップなので、イメージAが選択されています。

ここに画像の説明を入力してください

2番目の関数((x * y % 1024) % rand()) % 2にも、rand()通常(x * y % 1024)、分割するものよりも大きいという問題があり、最大で1023 です。この場合、(x*y%1024)%20と1が等しく生成されません。奇数に偶数を掛けると偶数になります。偶数に偶数を掛けたものも偶数です。奇数に奇数を掛けたものだけが奇数なので、%2、4分の3の偶数の値は0の4分の3の時間を生成します。

以下((x * y % 1024) % rand()) % 2は、画像Bを選択できるようにtrueになっている部分のクローズアップです。両方の座標が奇数の場所を正確に選択しています。

ここに画像の説明を入力してください

そして、画像Cを選択できる場所のクローズアップを以下に示します。

ここに画像の説明を入力してください

最後に、ここで画像Bが選択されている条件を組み合わせます。

ここに画像の説明を入力してください

そして、画像Cが選択されている場合:

ここに画像の説明を入力してください

結果の組み合わせは、次のように読み取ることができます。

50%の確率で、画像Aのピクセルを使用します。残りの時間は、画像Bと画像Cの間で選択します。両方の座標が奇数であるBと、どちらかが偶数であるCです。

最後に、3つの異なる色で同じことを行っていますが、方向が異なるため、パターンは各色で異なる方向を向いており、見ている交差ストリップまたはグリッドパターンを生成します。


45

コードで実行している計算の多くは、真にランダムな値にはなりません。表示されているこれらの鋭い線は、x座標とy座標の相対値が相互に交換される場所に対応しており、その場合、根本的に異なる式を使用しています。例えば、コンピューティングは、(x + y) % rand()一般的に、あなたが値をお返ししますx + yから、rand()意志(通常は)はるかに大きいよりも、かなりの数を返しますx + yその与えられたRAND_MAX通常かなり大きな数です。その意味では、物事を生成するために使用しているアルゴリズムがホワイトノイズの生成から離れて偏っているので、ホワイトノイズを取り戻すことを期待すべきではありません。ホワイトノイズが必要な場合は、各ピクセルをrand()。上記のような素敵なパターンが必要だが、あちこちに少しランダムな要素を加えた場合は、作成したコードを引き続き使用してください。

さらに、@ pm100がコメントで述べたように、rand関数は真の乱数を返さず、代わりに疑似乱数関数を使用して値を生成します。rand多くのシステムののデフォルトの実装では、線形合同ジェネレーターと呼ばれる一種の疑似乱数ジェネレーターを使用します。このジェネレーターは、短いバーストではランダムに見えるが実際には非ランダムである数値を生成します。たとえば、次のWikipediaのアニメーションは、線形合同ジェネレーターで選択された空間内のランダムな点が、固定数の超平面に落ちる様子を示しています。

画像

x、y、z座標をR、G、B座標に置き換えると、プログラムで生成される出力と非常によく似ています。これはおそらく、ここでの中心的な問題ではないのではないかと思います。これは、上記の他の側面がおそらくさらに顕著になるからです。

より高品質の乱数を探している場合は、より高品質の乱数ソースを使用する必要があります。Cでは、/dev/urandom/(Linuxのようなシステムで)からバイトを読み取ることを検討できます。これにより、かなり均一なランダム値が得られます。C ++の標準ライブラリには、適切な乱数生成プリミティブがいくつかあります(利用できる場合)。

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