Warum bekomme ich dieses spezielle Farbmuster, wenn ich rand () verwende?


170

Ich habe versucht, eine Bilddatei wie folgt zu erstellen:

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);
    }
}

Ich hatte erwartet, etwas Zufälliges zu bekommen (weißes Rauschen). Die Ausgabe ist jedoch interessant:

Kennen Sie den Grund dafür?


Bearbeiten

Nun ist klar, dass es nichts damit zu tun hat rand().

Versuchen Sie auch diesen Code:

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 - schöne Demo davon. Es ist 100% deterministisch
pm100


50
@ pm100: Wie die Antwort von bames53 so gut erklärt, würden Sie das gleiche Muster erhalten, selbst wenn Sie einen perfekten Zufallszahlengenerator verwenden würden.
TonyK

13
Entschuldigen Sie eine Frage: Da Sie die x- und y-Koordinaten zur Berechnung der Pixelwerte verwenden, warum sollten Sie erwarten, dass diese Werte unabhängig von den Koordinaten sind? Wenn das Bild zu gleichmäßig aussieht, brauchen Sie es doch, oder?
Thomas Padron-McCarthy

15
Lektionen gelernt: Zufällige Dinge mit Zufallszahlen zu tun, führt nicht zu zufälligen Ergebnissen :)
Hagen von Eitzen

Antworten:


355

Ich würde anfangs die gleiche Antwort haben wie alle anderen und dies auf die Probleme mit bringen rand(). Ich dachte jedoch besser darüber nach und analysierte stattdessen die Verteilung, die Ihre Mathematik tatsächlich erzeugt.

TL; DR: Das Muster, das Sie sehen, hat nichts mit dem zugrunde liegenden Zufallszahlengenerator zu tun und ist stattdessen einfach auf die Art und Weise zurückzuführen, wie Ihr Programm die Zahlen manipuliert.

Ich bleibe bei deiner blauen Funktion, da sie alle ähnlich sind.

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

Jeder Pixelwert wird von einem der drei Funktionen ausgewählt ist : (x + y) % rand(), (x - y) % rand()und rand();

Schauen wir uns die Bilder an, die von jedem dieser Bilder alleine erstellt wurden.

  • rand()

Dies ist, was Sie erwarten würden, nur Lärm. Nennen Sie dies "Bild C"

Geben Sie hier die Bildbeschreibung ein


  • (x + y) % rand()

Hier addieren Sie die Pixelkoordinaten und nehmen den Rest von der Division durch eine Zufallszahl. Wenn das Bild 1024 x 1024 ist, liegt die Summe im Bereich [0-2046]. Die Zufallszahl, nach der Sie tauchen, liegt im Bereich [0, RAND_MAX], wobei RAND_MAX mindestens 32.000 und auf einigen Systemen 2 Milliarden beträgt. Mit anderen Worten, es besteht bestenfalls eine 1: 16-Chance, dass der Rest nicht gerecht ist (x + y). Zum größten Teil erzeugt diese Funktion nur einen Gradienten mit zunehmendem Blau in Richtung + x + y.

Sie verwenden jedoch nur die niedrigsten 8 Bits, da Sie a zurückgeben uint8_t, sodass Sie Streifen mit Farbverläufen von 256 Pixel Breite haben.

Nennen Sie dies "Bild A"

Geben Sie hier die Bildbeschreibung ein


  • (x - y) % rand()

Hier machst du etwas ähnliches, aber mit Subtraktion. Solange x größer als y ist, haben Sie etwas Ähnliches wie das vorherige Bild. Aber wenn y größer ist, ist das Ergebnis eine sehr große Zahl, weil xund yohne Vorzeichen (negative Ergebnisse werden bis zum oberen Rand des Bereichs des vorzeichenlosen Typs verschoben), und dann % rand()tritt das ein und Sie erhalten tatsächlich Rauschen.

Nennen Sie dies "Bild B"

Geben Sie hier die Bildbeschreibung ein

Jedes Pixel in Ihrem endgültigen Bild wird mithilfe der Funktionen rand() % 2und aus einem dieser drei Bilder entnommen ((x * y % 1024) % rand()) % 2. Die erste davon kann als Auswahl mit einer Wahrscheinlichkeit von 50% gelesen werden (Ignorieren von Problemen mit rand()und ihren Bits niedriger Ordnung).

Hier ist eine Nahaufnahme, wo rand() % 2wahr ist (weiße Pixel), also ist Bild A ausgewählt.

Geben Sie hier die Bildbeschreibung ein

Die zweite Funktion hat ((x * y % 1024) % rand()) % 2wieder das Problem, wo rand()normalerweise größer ist als das, was Sie teilen (x * y % 1024), was höchstens 1023 ist. Dann werden (x*y%1024)%20 und 1 nicht gleich oft erzeugt. Jede ungerade Zahl multipliziert mit einer geraden Zahl ist gerade. Jede gerade Zahl multipliziert mit einer geraden Zahl ist auch gerade. Nur eine ungerade Zahl multipliziert mit einer ungeraden Zahl ist ungerade, und so %2weiter erzeugen Werte, die gerade drei Viertel der Zeit sind, 0 drei Viertel der Zeit.

Hier ist eine Nahaufnahme, wo ((x * y % 1024) % rand()) % 2wahr ist, damit Bild B ausgewählt werden kann. Es wird genau ausgewählt, wo beide Koordinaten ungerade sind.

Geben Sie hier die Bildbeschreibung ein

Und hier ist eine Nahaufnahme, wo Bild C ausgewählt werden könnte:

Geben Sie hier die Bildbeschreibung ein

Kombinieren Sie abschließend die Bedingungen, unter denen Bild B ausgewählt ist:

Geben Sie hier die Bildbeschreibung ein

Und wo Bild C ausgewählt ist:

Geben Sie hier die Bildbeschreibung ein

Die resultierende Kombination kann wie folgt gelesen werden:

Verwenden Sie mit einer Wahrscheinlichkeit von 50% das Pixel aus Bild A. Der Rest der Zeit wählt zwischen Bild B und Bild C, B, wo beide Koordinaten ungerade sind, C, wo eines der beiden gerade ist.

Da Sie dasselbe für drei verschiedene Farben tun, aber mit unterschiedlichen Ausrichtungen, werden die Muster in jeder Farbe unterschiedlich ausgerichtet und erzeugen die Kreuzungsstreifen oder Gittermuster, die Sie sehen.


45

Viele der Berechnungen, die Sie in Ihrem Code durchführen, führen nicht zu wirklich zufälligen Werten. Diese scharfen Linien, die Sie sehen, entsprechen Stellen, an denen die relativen Werte Ihrer x- und y-Koordinaten miteinander handeln, und wenn dies geschieht, verwenden Sie grundlegend unterschiedliche Formeln. Zum Beispiel (x + y) % rand()gibt Ihnen das Rechnen im Allgemeinen den Wert zurück x + y, da rand()(normalerweise) eine Zahl zurückgegeben wird, die viel, viel größer ist als x + yangegeben, RAND_MAXwas normalerweise eine ziemlich große Zahl ist. In diesem Sinne sollten Sie nicht damit rechnen, weißes Rauschen zurückzugewinnen, da der Algorithmus, mit dem Sie Dinge erzeugen, nicht mehr weißes Rauschen erzeugt. Wenn Sie weißes Rauschen wünschen, stellen Sie einfach jedes Pixel auf einrand(). Wenn Sie ein schönes Muster wie das oben gezeigte möchten, aber mit ein wenig Zufälligkeit hier und da hineingeworfen werden, verwenden Sie weiterhin den Code, den Sie geschrieben haben.

Wie @ pm100 in den Kommentaren festgestellt hat, gibt die randFunktion keine echten Zufallszahlen zurück und verwendet stattdessen eine Pseudozufallsfunktion, um ihre Werte zu erzeugen. Die Standardimplementierung randauf vielen Systemen verwendet eine Art Pseudozufallszahlengenerator, der als linearer Kongruenzgenerator bezeichnet wird und Zahlen erzeugt, die in kurzen Bursts zufällig erscheinen können, in der Praxis jedoch entschieden nicht zufällig sind. Hier ist zum Beispiel eine Animation aus Wikipedia, die zeigt, wie zufällige Punkte im Raum, die mit einem linearen Kongruenzgenerator ausgewählt wurden, in eine feste Anzahl von Hyperebenen fallen:

Das Bild

Wenn Sie die x-, y- und z-Koordinaten durch die R-, G- und B-Koordinaten ersetzen, ähnelt dies bemerkenswert der von Ihrem Programm erzeugten Ausgabe. Ich vermute, dass dies hier wahrscheinlich nicht das Kernproblem ist, da der andere oben erwähnte Aspekt wahrscheinlich viel ausgeprägter sein wird.

Wenn Sie nach Zufallszahlen mit höherer Qualität suchen, müssen Sie eine Zufallsquelle mit höherer Qualität verwenden. In C könnten Sie in Betracht ziehen, Bytes von /dev/urandom/(auf einem Linux-ähnlichen System) zu lesen , was ziemlich gleichmäßig zufällige Werte ergibt. C ++ hat jetzt eine Reihe guter Grundelemente zur Generierung von Zufallszahlen in seinen Standardbibliotheken, sofern diese für Sie verfügbar sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.