Nachdem mich René in den Kommentaren zu meinem Artikel über die Generierung von unterschiedlichen Zufallszahlen in PHP darauf hingewiesen hat, dass es sinnvoller sei, die PHP-Funktion mt_rand() anstelle von rand() zur Erstellung von Zufallszahlen zu nutzen, habe ich beschlossen ein wenig nachzuforschen und einen Artikel darüber zu schreiben. Aber was ist mt_rand() eigentlich?
Mit mt_rand() kann man genauso wie mit rand() Zufallszahlen generieren. Der Aufruf der Funktion erfolgt genauso, wie mit der normalen rand() Funktion. Zusätzlich heißt es in der PHP-Doku, würde mt_rand() Zufallszahlen bis zu 4-mal schneller generieren und diese auch wesentlich willkürlicher Auswählen. Kurz: Die von rand() erstellten Zufallszahlen seien nicht wirklich zufällig.
Mir war die ganze “Problematik” bis dato nicht bekannt und schon nach kurzem googeln musste ich feststellen, dass René mit seiner Meinung nicht alleine ist. Allerdings muss man auch dazu sagen, dass die meisten Beiträge, die beklagen, dass rand() nicht sauber arbeitet schon “etwas älter” sind.
Oft zitiert zur Unterstützung der Argumente ist ein Artikel von Dog.net Development, der die angeblichen Nachteile von rand() im Gegenteil zu mt_rand() visualisiert. Die oben schon genannte Behauptung, rand() würde keine korrekten Zufallszahlen erstellen, belegt der Autor mit einem kurzen aber effektiven PHP-Script. Ich habe euch zur Anschauung mal eben ein ähnliches Script geschrieben.
<?php header("Content-type: image/png"); $img = imagecreatetruecolor(256, 256) or die("Fehler beim Erstellen der Grafik!"); $farbeWeiss = imagecolorallocate($img, 255, 255, 255); for ($x=0; $x<256; $x++) { for ($y=0; $y<256; $y++) { if (mt_rand(0,1) === 1) { imagesetpixel($img, $x, $y, $farbeWeiss); } } } imagepng($img); imagedestroy($img); ?>
Das Script erstellt eine 256×256 Pixel große Grafik. Wobei für jeden Pixel eine Zufallszahl zwischen 0 und 1 generiert wird. Bei einer 1 wird der Pixel weiß, bei einer 0 bleibt der Pixel schwarz.
dog.net führte diese Funktion einmal mit mt_rand() und einmal mit rand() aus und erhielt folgende zwei Grafiken (zum Vergrößern bitte anklicken):
Hier wird ersichtlich was viele an der rand() Funktion anprangern. Auf dem Bild, das mit der rand() Funktion erstellt wurde ist ganz klar ein Muster erkennbar. Das hieße, dass die Zufallszahlen von rand() nicht wirklich zufällig sind. Aber…
..an diesem Punkt möchte ich meine Beobachtungen entgegenstellen. Ich sagte vorhin schon, dass die meisten anprangernden Artikel schon etwas älter seien. Es ist also genug Zeit vergangen die rand() Funktion zu verbessern. Deshalb habe ich es mir nicht nehmen lassen, das Script von dog.net nachzubauen und selber mal zu schauen, was passiert. Und siehe da…
(mit rand() erstelltes Bild)
(mit mt_rand() erstelltes Bild)
…wie durch ein Wunder scheinen beide Funktionen, rand() sowie mt_rand() hervorragend zu funktionieren.
Nun bleibt nur noch zu überprüfen, ob mt_rand() denn wenigstens schneller ist als rand(), wie es behauptet wurde. Hierzu habe ich ein weiteres kleines Script geschrieben.
<?php $start = microtime(true); for ($i=0; $i<1000000; $i++) { $zzahl = mt_rand(0,1); } $ende = microtime(true); $zeitmtrand= $ende - $start; $start = microtime(true); for ($i=0; $i<1000000; $i++) { $zzahl = rand(0,1); } $ende = microtime(true); $zeitrand= $ende - $start; echo "Laufzeit mt_rand(): ".$zeitmtrand." Sekunden!"; echo "Laufzeit rand(): ".$zeitrand." Sekunden!"; ?>
Und an dieser Stelle, folgt dann auch gleich die zweite Überraschung. Das Script lieferte folgendes Ergebnis zurück:
Laufzeit mt_rand(): 1.033802986145 Sekunden! Laufzeit rand(): 1.1012179851532 Sekunden!
Nach der vierfachen Geschwindigkeit sieht das für mich aber nicht aus. In meinen Augen gibt es von daher keinen Grund, von der rand() Funktion auf die mt_rand() Funktion umzusteigen, denn auch der Maximalwert für die größte zu erstellende Zahl mit rand(), der standardmäßig kleiner eingestellt ist, kann in der Größe angehoben werden, sofern dies nötig ist.
Wie seht ihr das? Konnte ich euch überzeugen?
Viele Grüße,
Raffi
Not a game machine. Maybe in the future it’ll do even better in those areas, but for now it’s a fantastic way to organize and listen to your music and videos, and is without peer in that regard. The iPod’s strengths are its web browsing and apps. If those sound more compelling, perhaps it is your best choice.
Mir ist bei beiden Funktionen aufgefallen, dass die Verteilung der Zufallszahlen nicht so ist, wie sie sein sollte. So gibt es beim Eintragen in Diagrammen einige Auffälligkeiten, die ich unter http://www.noob-blog.tk/2013/08/05/php-zufallszahlen-doch-nicht-so-zufallig/ zusammengefasst habe.
Keine von beiden! PHP kann keine wirklich zufälligen Zahlen generieren.
Siehe hier: http://blog.matthias-isler.ch/2013/07/echte-zufallszahlen-in-php-oder-wie-man-im-onlinecasino-gewinnt/
Hallo Denis,
danke für deinen Kommentar. Der Artikel ist wirklich gut. Sicherlich handelt es sich bei beiden Funktionen um Pseudo-Zufallszahlen. Aber darum ging auch nicht. Im speziellen wollte ich Zeigen, dass das ursprüngliche “massive” Problem mit rand() nicht mehr existiert.
Mhh, ich geb mal auch meinen Senf dazu:
Häufig werden rand() und mt_rand() in kritschen, sicherheitsbedürftigen Szenarios verwendet (Zum Beispiel um CSRF tokens zu generieren). Dazu sollte man allerdings anderes verwenden – Ich hab nen kleinen Wrapper gebaut: http://incolumitas.com/2013/11/14/cryptographically-secure-rand-replacement/
Warum hat das Relevanz? rand() und mt_rand() sind beide schlecht und man sollte sie allerhöchstens fürs generieren von Testdaten verwenden, ansonsten aber nicht, denn man kann mit genug samples für beide methoden komplett voraussagen welche zufallszahlen generiert werden…
Grüsse
Interessanter Artikel. Allerdings würde ich dir empfehlen mit Hilfe der Zufallszahlszahlen Koordinaten zu bilden und dort einen Punkt zu zeichnen. Dann siehst du einen noch deutlicheren Unterschied bei rand() und mt_rand(), zumindest bei Windows mit Wamp (auf Linux sieht beides gut aus). Beste Ergebnisse würdest du erhalten, wenn du ein 3D Bild erzeugt. bei einem schlechten Zufallsgenerator zeichnen sich dann erkennbare Ebenen ab, bei einem guten eine punktwolke
$img = imagecreatetruecolor(256, 256);
for ( $i=0; $i < 200*200; ++$i )
{
imagesetpixel($img, rand(0,255), rand(0,255), $white);
}
Vielen Dank für den Kommentar. Dann werde ich demnächst wohl noch ein mal ein paar ausführlichere Tests machen und die Ergebnisse hier verbloggen.
Viele Grüße
Auch wenn rand/( verbessert wurde so ging diese Verbesserung an Windows vorbei.
Auf meinem Windows Server weißt es noch ein eindeutiges Muster auf, bei Linux gibt es das Problem nicht. Das kann einem vorallem probleme bereiten wenn man auf seinem Heimrechner z.B XAMPP verwendet.
Danke für die Antwort, vielleicht wurde etwas an der rand() Funktion geändert.
Wir hatten das Problem halt mal bei den Gewinnspielen bzw. in dem Fall die Jackpotverlosung. Es wurde eine Zufallszahl zwischen 1 und 3.000.000 mit rand() erzeugt, wenn diese 1.500.000 war wurde der Jackpot gewonnen.
Hier mal der Post dazu im Forum:
http://www.klamm.de/forum/5942170-post491.html
In den ganzen Testdurchläufen wurde auch nach 10mrd Durchgängen mit rand() nie die Zahl gezogen. Kannst du vielleicht auch noch mal testen.
Lg
E: //
Da hab ich der rand() Funktion wohl unrecht getan. Gerade die Tests wiederholt und sie scheint genauso gute Zufallszahlen zu liefern wie mt_rand(), Danke für die Nachforschungen von dir! :)