Javascript: Zufallszahlen

  • Moin,

    momentan benutze ich für Zufallszahlen in Javascript folgende Funktion:

    Code
    function zufallszahl(min,max)
    {
    	return(min+parseInt(Math.random()*(max-min+1)));
    }

    Oft kommen da jedoch gleiche Werte raus bzw die Wahrscheinlichkeit, dass bestimmte Werte kommen ist recht hoch. So wirklich zufällig ist das also nicht. Kann man das irgendwie verbessern?

  • PGP-Key E384 009D 3B54 DCD3 21BF  9532 95EE 94A4 3258 3DB1 | S/MIME-Key 0x1A33706DAD44DA
    G d-@ s+:- a--- C+++ UB+L++ P--- L++@ E-@>++ W+ N o? K? w>++ !O !M !V PS+++ PE-- Y+>++ PGP++>+++ !t 5? X? !R tv b+++>++++ DI !D G>+ e>+++ h !r>++ !z
    „Die Aachener gelten als Erfinder des 4. Hauptsatzes der Thermodynamik: ‚Thermo schreibt man zweimal.“‘
    “Saying that Java is good because it works on all platforms is like saying oral sex is good because it works on all sexes.”
    „Es gibt 10 Sorten von Leuten: Die einen verstehen das Binärsystem, die anderen nicht.“
    „Manche Männer lieben Männer, Manche Frauen eben Frauen; Da gibt's nix zu bedauern und nichts zu staunen; Das ist genau so normal wie Kaugummi kauen; Doch die meisten werden sich das niemals trauen“

  • Uhm, bei mir kommen recht unterschiedliche Zahlen raus?


    Das sieht doch auf dem ersten Blick solide verteilt aus?

    Edit: parseInt ist eigentlich eine dumme Methode, um Zahlen zu runden. Es gibt extra Math.round dafür, denn parseInt macht aus 1.5 auch 1 und schon ist dein Zufall auf einer kleinen Zahlenmenge direkt über diesen Generator verfälscht.

    Einmal editiert, zuletzt von CHRiSNEW (16. Juni 2015 um 15:58)


  • Den wollte ich eigentlich auch bringen. Zum Thema: Wenn ein Pseudozufallsgenerator öfters die selben Zahlen bringt, muss es nicht heißen, das er nicht funktioniert. Zufällig kann es auch sein, dass der auch häufig die selbe Zahl bringt. Es gabs auch mal bei Apple Ipod shuffle, die leuten beschwerten sich, das der zufall manche songs bevorzugen würde. Die Apple Techniker konnten aber nichts am Allgorythmus finden und haben einfach ne Glättung rübergezogen, das die Zufall veränderte, in dem die Vergangenheit einbezogen wird.

    Du kannst dir also Variablen für die letzten x Zahlen machen und in der aktuellen Ziehung überprüfen, ob in den letzten x Mal schonmal die Zufallszahl gezogen wurde. Wenn ja zieht er neu und falls neu, werden die variblen umgeschrieben.

    Alternativ: Arbeite mit sehr großen Zahlbereichen mit Random, und Rechne den Modulo Rest von deinem Bereich. Ne Glättung tritt dabei nicht auf, aber vielleicht die Chance auf mehr Varianz im Pseudozufallszahlalgorytmus.

  • Ich hab eben nochmal herumgespielt und dieses +1 und parseInt verzerrt doch das Ergebnis, daher lieber:

    Code
    function zufallszahl(min,max) {
        return(min+Math.round(Math.random()*(max-min)));
    }

    Ansonsten schließe ich mich Dirk an, wenn es um Pseudozufall-Anwendungsbereiche dieser Art geht.

  • xkcd quoten ist nie falsch. :D

    Bist du dir sicher, dass die Entropie zu gering ist? Ich hab’s mal mit 1 bis 100 getestet und ich finde es »ausreichend zufällig«. Geringe Zufallsbereiche sind für Menschen tückisch, weil sie oft den Eindruck vermitteln, sie wären nicht zufällig genug, aber auch mehrere aufeinanderfolgende Ergebnisse (im nicht ausufernden Maße) sind ein Zeichen für Entropie.

    CHRiSNEW: Erreicht man durch Runden nicht den ungewünschten Nebeneffekt wie bei Schulnoten, dass man für das erste und letzte Element nur den halben Spielraum hat?

    • • • – • – – • – –

    Einmal editiert, zuletzt von s4ndwichMakeR (16. Juni 2015 um 16:08)


  • CHRiSNEW: Erreicht man durch Runden nicht den ungewünschten Nebeneffekt wie bei Schulnoten, dass man für das erste und letzte Element nur den halben Spielraum hat?

    Hah, lustig. Ich wollte mich gerade im Augenblick noch verbessern. :) Da hast du natürlich Recht!

    Daher doch (bzw die erste Version mit parseInt)

    Code
    function zufallszahl(min,max) {
        return(min+Math.floor(Math.random()*(max-min+1)));
    }

    Also die Verteilung sieht jetzt weiterhin recht fair aus:


    Oder liege ich falsch? :)

  • Ich habs über nen Array gejagt, welches das Alphabet enthielt. Kam leider relativ häufig E raus. Nicht immer, aber doch erschreckend häufig.

    Ich werde mal testen, wie sich das mit der Variante "return(min+Math.round(Math.random()*(max-min)));" verhält, Vielen Dank schon mal!

  • Fast alle Browser (mit Ausnahme von Apple-WebKit unter Windows, das einen kryptografisch sicheren Zufallsgenerator nutzt) nutzen einen linearen Kongruenzgenerator mit unterschiedlicher Periode auf Basis der ursprünglichen Java-Implementation bzw. rand() in der GNU- oder BSD-C-Standardbibliothek. Ein Problem ist dabei, dass der Startwert dabei neben anderen Faktoren (z. B. Fenster-Handle beim IE) häufig nur vom Startzeitpunkt des Browserprozesses abhängt.

    Deswegen kann es sein, dass der Zufall häufig nicht zufällig genug aussieht. Ich habe auch in ein Projekt wie von Dirk beschrieben eine Warteschlange eingebaut, die die letzten 100 Ergebnisse nicht als zufälliges Ergebnis zulässt. Da handelte es sich aber auch schon um den gesamten Bestand von Flashloops auf z0r.de, die sich in ihrer Gesamtheit z. Zt. auf 7099 belaufen.

    Ggf. wäre ein anderer, in Javascript implementierter PRNG wie z. B. ein Mersenne-Twister geeignet.

    Ansonsten kann ich deinen Anwendungsfall auch nicht weiter nachvollziehen.

  • So habe ich das momentan:

    (Q, R, X und Y bewusst weggelassen)

  • Erster kurzer Hinweis, da ist ein Bug im Code. So wie deine Funktion geschrieben ist, sind sowohl min als auch max inklusive. Da hast du in manchen Fällen Rückgabewerte ausserhalb des Arrays (zufall == array.length).

    Bugfix, einfach abrunden, siehe unten.

    Math.round() ist sowieso gefährlich, siehe: https://de.wikipedia.org/wiki/Rundung#Mathematisches_Runden

    »Das kaufmännische Runden erzeugt kleine systematische Fehler, da das Aufrunden um 0,5 vorkommt, das Abrunden um 0,5 jedoch nie; das kann Statistiken geringfügig verzerren.«

    In deinem Falle ist hier aber noch ein anderes Problem: Weil A am Rand ist, können nur die Werte [0 bis 0.4999] zu A werden. Für alle anderen ist es aber mehr, nämlich z.B. [3.5 bis 4.4999] für den Buchstaben D. Das heisst konkret, dass A bei dir halb mal so oft vorkommt wie alle anderen Buchstaben! Für das letzte Element gilt das gleiche - das ist bei dir wegen obigem Bug aber nicht einem Buchstaben zuzuweisen.

    Darum: Unbedingt Math.floor() (oder .ceil()) nehmen!

    Code
    function zufallszahl(min,max)
    {
        return(min+Math.floor(Math.random()*(max-min)));
    }

    Die Zufälligkeit sieht für mich aber ziemlich okay aus, das ist alles Uniformverteilt wie es sein soll: https://jsfiddle.net/p7r3owL3/
    Tipp: Mach Math.round() an, dann siehst du auch den statistischen Fehler den ich erwähnt habe.

    Wenn du die Anzahl versuche runterdrehst (z.B. auf 100) dann sieht es anders aus. Kann gut sein dass beim manuellen Testen (mit weniger als 100 Versuchen) ein Buchstabe überdurchschnittlich häufig vorkommt, so funktioniert halt Zufall. Erst bei sehr vielen Versuchen gleicht sich das wieder aus.

    Meine Konklusion ist also, dass alles okay ist. Wenn du trotzdem einen besseren Zufallsgenerator willst (wobei wie heinze sagt, das Ding ist zufällig, sieht man halt nicht), alle modernen Browser (IE11+) besitzen einen kryptografisch sicheren Zufallsgenerator: https://developer.mozilla.org/en-US/docs/Web…getRandomValues

    Der obligatorische Comic zu dem Thema

    Nachtrag: Habe die Diskussion zwischen sandi und CHRISNEW oben übersehen, sorry!

    Einmal editiert, zuletzt von gandro (16. Juni 2015 um 22:25)

  • Danke für den Hinweis! Habe es jetzt auf "return(min+Math.floor(Math.random()*(max-min)));" umgebaut :)

  • Aus aktuellem Anlass (Blogpost von Kumpel, Englisch): http://nickler.ninja/blog/2015/06/28/csgojackpots-weak-rng/

    Kurzum: Math.random() ist zwar uniform (d.h. jede Zahl hat die gleiche Wahrscheinlichkeit), aber vorhersagbar. Wenn man zwei rohe (d.h der direkte Output zwischen 0 und 1), aufeinander folgende Zufallszahlen kennt, kann man die restlichen voraus sagen.

    Einmal editiert, zuletzt von gandro (9. Juli 2015 um 01:13)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!