Ich habe diesen Pseudozufallszahlengenerator zur Verwendung in Shadern gesehen, auf die hier und da im Internet Bezug genommen wird :
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
Es wird verschiedentlich als "kanonisch" oder "ein Einzeiler, den ich irgendwo im Internet gefunden habe" bezeichnet.
Was ist der Ursprung dieser Funktion? Sind die konstanten Werte so willkürlich, wie sie scheinen, oder gibt es Kunst in ihrer Auswahl? Gibt es eine Diskussion über die Vorzüge dieser Funktion?
BEARBEITEN: Der älteste Verweis auf diese Funktion, auf den ich gestoßen bin, ist dieses Archiv vom Februar '08 , wobei die ursprüngliche Seite jetzt aus dem Web entfernt wurde. Aber es gibt dort nicht mehr Diskussionen darüber als anderswo.
Antworten:
Sehr interessante Frage!
Ich versuche dies herauszufinden, während ich die Antwort eingebe :) Zuerst eine einfache Möglichkeit, damit zu spielen: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898 +% 2B + y * 78,233% 29 + * + 43758,5453% 2C1% 29x% 3D0..2% 2C + y% 3D0..2% 29
Überlegen wir uns dann, was wir hier versuchen: Für zwei Eingabekoordinaten x, y geben wir eine "Zufallszahl" zurück. Dies ist jedoch keine Zufallszahl. Es ist jedes Mal dasselbe, wenn wir dasselbe x, y eingeben. Es ist eine Hash-Funktion!
Das erste, was die Funktion tut, ist, von 2d auf 1d zu gehen. Das ist an sich nicht interessant, aber die Zahlen werden so gewählt, dass sie sich normalerweise nicht wiederholen. Außerdem haben wir dort eine Gleitkommaaddition. Es wird noch ein paar Bits von y oder x geben, aber die Zahlen könnten einfach richtig gewählt werden, damit eine Mischung entsteht.
Dann probieren wir eine Black-Box-Funktion sin () aus. Dies hängt stark von der Implementierung ab!
Zuletzt wird der Fehler in der sin () - Implementierung durch Multiplizieren und Nehmen des Bruchs verstärkt.
Ich denke nicht, dass dies im allgemeinen Fall eine gute Hash-Funktion ist. Die sin () ist numerisch eine Black Box auf der GPU. Es sollte möglich sein, eine viel bessere zu konstruieren, indem fast jede Hash-Funktion übernommen und konvertiert wird. Der schwierige Teil besteht darin, die typische Ganzzahloperation, die beim CPU-Hashing verwendet wird, in Float- (Halb- oder 32-Bit-) oder Festkommaoperationen umzuwandeln, dies sollte jedoch möglich sein.
Das eigentliche Problem bei dieser Hash-Funktion ist wiederum, dass sin () eine Black Box ist.
quelle
Der Ursprung ist wahrscheinlich das Papier: "Über die Erzeugung von Zufallszahlen mit Hilfe von y = [(a + x) sin (bx)] mod 1", WJJ Rey, 22. Europäisches Statistiktreffen und 7. Vilnius-Konferenz über Wahrscheinlichkeitstheorie und Mathematische Statistik, August 1998
BEARBEITEN: Da ich keine Kopie dieses Dokuments finden kann und die Referenz "TestU01" möglicherweise nicht klar ist, ist hier das Schema wie in TestU01 in Pseudo-C beschrieben:
wobei der einzige empfohlene konstante Wert der B1 ist.
Beachten Sie, dass dies für einen Stream ist. Die Konvertierung in einen 1D-Hash 'n' wird zum ganzzahligen Raster. Ich vermute also, dass jemand dies gesehen und 't' in eine einfache Funktion f (x, y) umgewandelt hat. Die Verwendung der obigen Originalkonstanten würde ergeben:
quelle
fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * (co.xy + vec2(43758.5453, SOMENUMBER))
, um der Funktion zu entsprechen, um die es in dem Papier geht.a
undb
) offen, wurde aber möglicherweise in dem von Ihnen zitierten Papier verwendet.Die konstanten Werte sind willkürlich, insbesondere weil sie sehr groß sind und ein paar Dezimalstellen von den Primzahlen entfernt sind.
Ein Modul über 1 eines Sinus mit hoher Amplitude multipliziert mit 4000 ist eine periodische Funktion. Es ist wie eine Jalousie oder ein Wellblech, das sehr klein gemacht wird, weil es mit 4000 multipliziert und vom Punktprodukt in einem Winkel gedreht wird.
Da die Funktion 2-D ist, bewirkt das Punktprodukt, dass die periodische Funktion relativ zur X- und Y-Achse schräg gedreht wird. Im Verhältnis 13/79 ungefähr. Es ist ineffizient, Sie können das gleiche erreichen, indem Sie Sinus von (13x + 79y) machen. Dies wird auch das gleiche erreichen, was ich denke, mit weniger Mathematik.
Wenn Sie die Periode der Funktion sowohl in X als auch in Y finden, können Sie sie abtasten, sodass sie wieder wie eine einfache Sinuswelle aussieht.
Hier ist ein Bild davon in der Grafik gezoomt
Ich kenne den Ursprung nicht, aber er ähnelt vielen anderen. Wenn Sie ihn in regelmäßigen Abständen in Grafiken verwenden, erzeugt er tendenziell Moiré-Muster und Sie können sehen, dass er irgendwann wieder auftritt.
quelle
(13x + 79y)
dadot(XY, AB)
genau das tun wird, was Sie beschreiben, da es das Punktprodukt ist, dasx,y dot 13, 79 = (13x + 79y)
Vielleicht ist es eine nicht wiederkehrende chaotische Abbildung, dann könnte sie viele Dinge erklären, kann aber auch nur eine willkürliche Manipulation mit großen Zahlen sein.
BEARBEITEN: Grundsätzlich ist die Funktion Bruch (sin (x) * 43758.5453) eine einfache Hash-ähnliche Funktion, die sin (x) liefert eine glatte Sin-Interpolation zwischen -1 bis 1, also ist sin (x) * 43758.5453 eine Interpolation von - 43758.5453 bis 43758.5453. Dies ist ein ziemlich großer Bereich, so dass selbst kleine Schritte in x einen großen Ergebnisschritt und eine wirklich große Variation des Bruchteils liefern. Der "Bruch" wird benötigt, um Werte im Bereich von -0,99 ... bis 0,999 ... zu erhalten. Wenn wir nun so etwas wie eine Hash-Funktion haben, sollten wir aus dem Vektor eine Funktion für den Produktions-Hash erstellen. Der einfachste Weg ist, "Hash" separat für x jede y-Komponente des Eingabevektors aufzurufen. Aber dann werden wir einige symmetrische Werte haben. Wir sollten also einen Wert aus dem Vektor erhalten. Der Ansatz besteht darin, einen zufälligen Vektor zu finden und ein "Punkt" -Produkt für diesen Vektor zu finden. Los geht's: Bruch (sin (Punkt (co.xy, vec2 (12,9898,78,233)) * 43758,5453); Entsprechend dem ausgewählten Vektor sollte seine Länge lang genug sein, um mehrere Peroide der "sin" -Funktion zu haben, nachdem das "dot" -Produkt berechnet wurde.
quelle