Im Internet gibt es viele Beiträge und Diskussionen zum Thema rand() und mt_rand(). Beides sind Funktionen die PHP bereitstellt um Zufallszahlen zu erzeugen.
mt_rand() arbeitet nach dem Prinzip des Mersenne-Twisters der „bessere Zufallszahlen“ erzeugen soll als die bisherige Implementation der rand()-Funktion. Es kursieren auch noch Angaben, dass dieser Algorithmus laut PHP-Manual bis zu vier mal schneller sein soll als rand().
In diesem Artikel beleuchte ich mit unterschiedlichen Methoden die Facetten der beiden Algorithmen. Es handelt sich dabei um eine dynamisch erzeugte Seite, sollte sich die Implementation ändern, so kann es sein, dass komplett andere Fakten vorliegen als zum Zeitpunkt als dieser Artikel geschrieben wurde.
Die Ladezeit der Seite wird im Normalfall etwas länger dauern als bei normalen Seiten.
Zunächst ein Auszug aus dem PHP-Manual:
mt_rand — Erzeugt „bessere“ Zufallszahlen
Viele Zufallszahlengeneratoren, die auf älteren libc-Versionen basieren, haben seltsame oder doch zumindest unerwartete Verhaltensweisen und sind zudem recht langsam. Standardmäßig verwendet PHP den libc-Zufallszahlengenerator mit der Funktion rand(). Die Funktion mt_rand() kann jedoch als vollwertiger Ersatz verwendet werden. Sie verwendet einen Zufallszahlengenerator mit den bekannten Charakteristika der » Mersenne Twister, die Zufallszahlen viermal schneller generiert als der durchschnittliche libc-rand()-Aufruf.Wenn die Funktion ohne Angabe von min und/oder max aufgerufen, gibt mt_rand() eine Pseudozufallszahl zwischen 0 und mt_getrandmax() zurück. Benötigen Sie zum Beispiel eine Zufallszahl zwischen 5 und 15 (inklusive), verwenden Sie mt_rand(5, 15).
Und für rand():
rand — Erzeugt einen zufälligen Integerwert
Liefert eine Pseudozufallszahl zwischen min und max (inklusive), oder zwischen 0 und get_randmax() falls keine Parameter angegeben wurden. Wenn Sie z.B. einen Zufallswert zwischen 5 und 15 benötigen so wäre der Aufruf dafür rand(5, 15).Hinweis: Auf manchen Plattformen (Windows z.B.) ist get_randmax() nur 32768. Wenn sie einen größeren Wertebereich benötigen sollten, so können Sie entweder einen größeren max-Wert übergeben oder besser die mt_rand()-Funktion anstelle von rand() einsetzen.
Hinweis: Seit PHP 4.2.0 besteht keine Notwendigkeit mehr, den Zufallsgenerator für Zahlen mit srand() oder mt_srand() zu füttern, das geschieht nun automatisch.
Wie man sieht, steht hier nichts mehr von einem Geschwindigkeitsvorteil. Einzig das „bessere Zufallszahlen“ scheint auf einen Unterschied zwischen den beiden Generatoren hinzuweisen. Auch scheint bei mt_rand() keine Begrenzung auf einen Wert von 32768 (2^15) vorzuliegen.
Im folgenden soll ein wenig dargestellt werden, ob Unterschiede beispielsweise in der Zufälligkeit vorliegen oder ob eine Variante einen deutlichen Geschwindigkeitsvorteil aufweisen kann.
Eine interessante Variante die Zufälligkeit visuell darzustellen fand ich auf der Seite Dog-Net.org auf der Seite kann man sehen, dass die Verwendung von rand() im Gegensatz zu mt_rand() ein deutliches Muster in einem Bild erzeugt. Demnach können keine wirklich zufälligen Zufallszahlen vorliegen.
Ich habe diese Variante auf meinen Blog portiert, kann aber keine nachvollziehbaren Muster erkennen. Falls jemand doch mal eines sieht, das Bild gleich irgendwohin abspeichern, es wird bei jedem Aufruf der Seite neu erzeugt. Falls jemand Interesse an meinem PHP-Code hat kann ich ihn gerne zukommen lassen.
Mit mt_rand():
Mit rand():
Nun nachdem dieser Test keine augenscheinlichen Schwächen mehr aufweist, noch ein Geschwindigkeitstest und eine kleine Auswertung der erzeugten Nummern:
Bei der Ausführungsgeschwindigkeit konnte ich Unregelmäßigkeiten feststellen, manchmal liegt mt_rand() vorn, manchmal rand(). Ich vermute, dass durch die relativ lange Ausführzeit ein Taskswitch vorgenommen wird und so teilweise Zeit zur Erzeugung gerechnet wird, welche effektiv gar nicht in diesem Skript verbracht wird. Daher eine zweite Variante:
15000 mal 20 Zufallszahlen generiert in 8.7511539459229 Millisekunden mit rand(); 15000 mal 20 Zufallszahlen generiert in 8.5628032684326 Millisekunden mit mt_rand();
Ich würde eher mt_rand() benutzen, weil hier auf jeden Fall keine Beschränkung auf dem Maximalwert liegt. Ansonsten scheint es zumindest in der PHP version 5.2.6 keine wesentlichen Unterschiede zwischen den beiden Funktionen geben.Natürlich lässt sich auch bei dieser Variante nicht ausschließen, dass ein Taskswitch auftritt, auch sind Zweifel an der Genauigkeit der Messung durchaus gerechtfertigt, da zumindest unter Windows eine Zeitmessung unter einer Millisekunde nicht ohne weiteres möglich ist, und ich gewisse Zweifel hege, dass die Zeitmessung in PHP diese Genauigkeit aufweist.
Aber es sieht meistens so aus, als würden beide Varianten in Sachen Geschwindigkeit und auch der Zufälligkeit gleich auf liegen.
Die Diagramme werden übrigens mit HTML-Graphs erzeugt (die Navigation befindet sich am unteren Rand der Webseite).
Hi,
so richtig zufälli ist das Ganze leider nicht. Ich geb grad aus ein paar Ergebnissen zufällig welche aus. Es fällt auf dass da immer x mal dieselbe zahl kommt. Zahlen gehen zB 0-4, Zeile 2 von 0-8 usw jetzt hab ich frad 25 x ne Null bekommen – das wäre Im Lotte sicher ein 8er mit Zusatzzahl;-) 10exp14 …
nächstes Ergebnis 22 von 25 Reihen die Zahl 1.
Das ist leider ziemlich unbrauchbar.
Hmm so analysiert habe ich das bisher noch nicht, ist sicher mal einen Versuch wert was da bei mir so rauskommt – mal schauen wann ich dazu komme. Danke fürs berichten. Auf was für einem System hast du das getestet? Zufallsfunktionen sind durchaus auch vom verwendetem System abhängig.
Ich hab das auf nem Linux-Server ausprobiert.
Mittlerweile schaut’s besser aus – ich hatte am Anfang mt_srand(time()) – das hab ich rausgenommen, aber das darf ja eigentlich nicht geschadet haben. Wenn mir das Ergebnis nicht passt würfel ich halt nochmal;-)
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
Solang du mt_srand() nur einmal pro Funktionsblock aufrufst sollte es in Ordnung sein. Das Problem von time() ist, dass es nur alle Sekunde einen neuen Wert kriegt, wenn du also mehrmals hintereinander mt_srand(time()) aufrufst, seedest du den Zufallsgenerator mehrmals mit der gleichen Zahl, und weil es halt nur ein Pseudozufallszahlengenerator ist, erzeugt er dir dann immer die gleiche Kette an Zufallszahlen. Deswegen gehen die meisten ja dazu über microtime() zu verwenden, weil sich das öfter ändert, aber auch hier gibt es nur 1 Million unterschiedliche Werte. Was im Endeffekt dann zu nur 1 Millionen unterschiedlichen Zufallszahlenketten führen würde.
Wobei sich das Verhalten von den PHP-Versionen wohl auch in der Hinsicht deutlich verändert hat, mit 5.2.1:
Die Mersenne-Twister-Implementation in PHP verwendet jetzt einen neuen Seeding-Algorithmus von Richard Wagner. Identische Seeds erzeugen nicht länger die selbe Sequenz von Werten, wie es in früheren Versionen der Fall war. Dieses Verhalten wird nicht als noch einmal wechselnd erwartet, aber es ist trotzdem nicht sicher, darauf bedingungslos zu vertrauen.
Das halte ich für höchst eigenartig – Pseudozufallszahlengeneratoren sollten mit der gleichen Seed auch die gleiche Kette an Zufallszahlen produzieren.
Mal schauen ob es da irgendwo was dazu gibt.
Guter Artikel, habe auf meinem Blog einen Artikel (http://blog.matthias-isler.ch/2013/07/echte-zufallszahlen-in-php-oder-wie-man-im-onlinecasino-gewinnt/) veröffentlicht wie man auch mit PHP echte Zufallszahlen generieren kann.