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; */
}
Antworten:
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.
Jeder Pixelwert wird von einem der drei Funktionen ausgewählt ist :
(x + y) % rand()
,(x - y) % rand()
undrand()
;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"
(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"
(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
x
undy
ohne 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"
Jedes Pixel in Ihrem endgültigen Bild wird mithilfe der Funktionen
rand() % 2
und 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 mitrand()
und ihren Bits niedriger Ordnung).Hier ist eine Nahaufnahme, wo
rand() % 2
wahr ist (weiße Pixel), also ist Bild A ausgewählt.Die zweite Funktion hat
((x * y % 1024) % rand()) % 2
wieder das Problem, worand()
normalerweise größer ist als das, was Sie teilen(x * y % 1024)
, was höchstens 1023 ist. Dann werden(x*y%1024)%2
0 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%2
weiter erzeugen Werte, die gerade drei Viertel der Zeit sind, 0 drei Viertel der Zeit.Hier ist eine Nahaufnahme, wo
((x * y % 1024) % rand()) % 2
wahr ist, damit Bild B ausgewählt werden kann. Es wird genau ausgewählt, wo beide Koordinaten ungerade sind.Und hier ist eine Nahaufnahme, wo Bild C ausgewählt werden könnte:
Kombinieren Sie abschließend die Bedingungen, unter denen Bild B ausgewählt ist:
Und wo Bild C ausgewählt ist:
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.
quelle
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ückx + y
, darand()
(normalerweise) eine Zahl zurückgegeben wird, die viel, viel größer ist alsx + y
angegeben,RAND_MAX
was 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
rand
Funktion keine echten Zufallszahlen zurück und verwendet stattdessen eine Pseudozufallsfunktion, um ihre Werte zu erzeugen. Die Standardimplementierungrand
auf 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: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.quelle