Problem mit Zufallszahlen-Generator



  • Hallo Leute,

    ich habe ein kleines Problem mit dem Zufallszahlen-Genarator.

    Ich habe ein Klasse Die implementiert. Mit dieser kann ich beliebigseitige Würfel erzeugen. Mit der Funktion pitch() erhalte ich das Ergebnis des letzen Wurfes zurück.

    abgespeckte Version:

    import java.util.Random ;
    public class Die
    {
        private int sides = 0 ;
        private Random r ;
    
        public Die (int v)
        { 
            sides=v ;
            r = new Random () ;
        }
    
        int pitch () { return (Math.abs(r.nextInt() % sides))+1 ; }
    
        public static void main(String[] args)
        {
            Die d1 = new Die (6) , d2 = new Die (6) ;
            for (int i = 1; i <= 10 ; i++)
                System.out.println(d1.pitch() + " - " + d2.pitch());
        }
    }
    

    Bei einem Würfel klappt es recht gut. Die erzeugten Zufallszahlen sind scheinbar sogar normalverteilt.

    Wenn ich allerdings, wie in der main, zwei Würfel erzeuge, so habe ich das Problem, dass ich für beide im gleichen Wurf immer die gleichen Werte zurück bekomme.

    Ausgabe: (z. B.)
    6 - 6
    5 - 5
    4 - 4
    4 - 4
    3 - 3
    5 - 5
    1 - 1
    3 - 3
    5 - 5
    2 - 2

    Kann mir jemand sagen, wie ich dies abstellen kann bzw. wo der Fehler liegt?

    Vielen Dank!

    Ciao
    shoat



  • wenns so is wie in c, dann liegts daran, dass bei programmstart der zufallsgenerator mit einem wert initialisiert wird.
    zufallszahlen sind ja auch nur werte einer funktion, die relativ kompliziert ausschaut.
    bei dir wird in beiden fällen ja die gleiche funktion aufgerufen, die mit diesem startwert initialisiert ist, also kommen immer die gleichen zahlen raus.
    ich kann leider kein java, aber bei c gibts ne funktion srand(unsigned). die initialisiert die zufallsfunktion mit dem übergebenen wert. wenn du da die zeit übergibst, hast du ja immer einen anderen startwert, also müsste in deiner funktion pitch noch eine zeile

    int pitch () { 
    //hier die zufallsfunkt. initialisieren, bei c: srand((unsigned)time(NULL));
    return (Math.abs(r.nextInt() % sides))+1 ; 
    }
    

    da musst du mal schauen, was es da äquivalentes zu srand bei java gibt, weiss ich wie gesagt net...
    gruß
    E-the-Real



  • Random() wird mit aktueller Zeit initialisiert.
    Es gibt aber auch einen Random(long seed) ctor, den musst du nehmen,
    aber nicht in der Funktion pitch(), sondern ruhig im ctor.
    Übergib doch einfach ein Die-Objekt und initialisiere r mit nextInt.

    Jockel



  • Hallo Leute,

    erstmal danke für Euere Antworten!

    Wenn ich es richtig verstanden habe, dann ist das Problem, dass die Instanzen von Random mit dem gleichen Wert initialisiert werden. Deswegen habe ich jetzt einfach eine statische Random-Variable für alle Würfel angelegt.

    import java.util.Random ; 
    public class Die 
    { 
        private int sides = 0 ; 
        private static Random r = new Random () ; 
    
        public Die (int v) { sides=v ; } 
    
        int pitch () { return ((Math.abs(r.nextInt() % sides)) + 1) ; } 
    }
    

    Dass sollte die sauberste Lösung sein! Oder habe ich etwas übersehen?

    Nochmals vielen Dank für die Hilfe!

    Ciao
    shoat



  • shoat schrieb:

    Dass sollte die sauberste Lösung sein! Oder habe ich etwas übersehen?

    ja, ist das die sauberste.
    (die falle verrate ich nicht, ist zu unwahrscheinlich, daß sie zuschnappt)



  • volkard schrieb:

    shoat schrieb:

    Dass sollte die sauberste Lösung sein! Oder habe ich etwas übersehen?

    ja, ist das die sauberste.
    (die falle verrate ich nicht, ist zu unwahrscheinlich, daß sie zuschnappt)

    Hallo Volkard!

    Jetzt hast Du mich schon neugierig gemacht. Wenn Du also die Zeit findest und Lust hast, dann würde ich gerne etwas mehr über die Falle erfahren.

    Schließlich möchte ich diese Klasse in einem eigenen kleinen Freeware-Projekt einsetzen und es wäre doch tragisch, wenn irgendwann Fehler auftauchen und ich kann mir nicht erklären woher?

    Vielen Dank!

    Ciao
    shoat



  • falls Random() nicht stark ist (ich weiß nicht, wie gut der zufallszahlengenerator dieser sprache ist), kannste die an sich tolle version kaputtmachen, wenn du öfters sachen wie
    while(d.pitch()<98)
    schreibst.
    ins hauptgeschäft fallen danach nur noch die zahlen, die einer 99 oder 100 folgen und das kann bei nem kleinen zufallsraum sehr beengend werden. ist natürlich nur gefährlich, wenn Random schlecht ist. in diesem fall wären unabhängige zufallszahlengeneratoren besser.



  • Hallo Volkard,

    zuerst nochmals Danke für Deinen Einsatz, aber ich verstehe die letzten Ausführungen nicht so richtig.

    volkard schrieb:

    falls Random() nicht stark ist (ich weiß nicht, wie gut der zufallszahlengenerator dieser sprache ist), ...

    Ok, ich bin im klaren darüber, dass der Zufallszahlen-Generator nicht der tollste ist und dass sich die Zahlen wahrscheinlich (ich kenne den genauen Algorithmus nicht) zyklisch in einer Periode von 2^n (mit n ziemlich groß) wiederholen. Dies ist aber für meine Anwendung nicht so wichtig, da ich mit fast 100%iger Sicherheit sagen kann, dass ich Die.pitch() deutlich weniger als diese 2^n-mal aufrufen werde.

    volkard schrieb:

    ...kannste die an sich tolle version kaputtmachen, wenn du öfters sachen wie
    while(d.pitch()<98)
    schreibst.
    ins hauptgeschäft fallen danach nur noch die zahlen, die einer 99 oder 100 folgen und das kann bei nem kleinen zufallsraum sehr beengend werden. ist natürlich nur gefährlich, wenn Random schlecht ist.

    Ich weiß ehrlich gesagt nicht genau, was Du hier meinst. Wahrscheinlich ist mein Verstand gerade nicht anwesend. 😉 (*gähn* \me braucht mal wieder Schlaf) Vielleicht kannst Du mir ja nochmal ein wenig ausführlich erklären worauf Du hinaus willst. Danke dafür!

    Ich habe jedenfalls durch Empirie herausgefunden, dass die erzeugten Zahlen wenigstens halbwegs normalverteilt sind (meint alle Zahlen, die Die.pitch() zurückgibt, kommen ungefähr gleich häufig vor). Dies war für mich der wichtigste Punkt - schließlich geht es bei meiner Anwendung auch um Wahrscheinlichkeitsrechnung (z. B. hier: die Wahrscheinlichkeit, dass eine Zahl auf einem Würfel gewürfelt wird, ist für jeden Zahl gleich).

    volkard schrieb:

    in diesem fall wären unabhängige zufallszahlengeneratoren besser.

    Gerne! Bloß habe ich keine Ahnung (bin ja auch shoat == Anfänger, wie ich die mit java ansteuere, geschweige denn, dass ich einen unabhängigen, guten Zufallsgenerator kenne/besitze.

    Ich würde mich gerne in diesem Punkt belehren lassen bzw. mich verbessern.

    Viele Grüße!

    Ciao
    shoat



  • shoat schrieb:

    Ich habe jedenfalls durch Empirie herausgefunden, dass die erzeugten Zahlen wenigstens halbwegs normalverteilt sind (meint alle Zahlen, die Die.pitch() zurückgibt, kommen ungefähr gleich häufig vor). Dies war für mich der wichtigste Punkt - schließlich geht es bei meiner Anwendung auch um Wahrscheinlichkeitsrechnung (z. B. hier: die Wahrscheinlichkeit, dass eine Zahl auf einem Würfel gewürfelt wird, ist für jeden Zahl gleich).

    kannste mal messen, ob bei nem würfel mit einer million seiten die 0 sichtbar häufiger als die 999999 drankommt? wenn da nix schief wirkt, brauchste dir keine gedanken über nen besseren zufallszahlengenerator zu machen, dann ist deiner echt gut.


  • Mod

    volkard schrieb:

    kannste mal messen, ob bei nem würfel mit einer million seiten die 0 sichtbar häufiger als die 999999 drankommt? wenn da nix schief wirkt, brauchste dir keine gedanken über nen besseren zufallszahlengenerator zu machen, dann ist deiner echt gut.

    Wenn die ints, die der Zufallsgenerator ausspuckt, gleichverteilt sind, dann sollte die 999999 etwas seltener vorkommen als die 0 (abgesehen davon, dass in dem Code da oben natürlich garkeine 0 entsteht). Das bringt das "%", das hier verwendet wird, wohl mit sich.



  • Eventuell hilft hier ja auch java.security.SecureRandom weiter... DEr sollte bessere Zufallszahlen liefern.



  • destruct0r schrieb:

    Eventuell hilft hier ja auch java.security.SecureRandom weiter... DEr sollte bessere Zufallszahlen liefern.

    Danke für die Idee. Ich habe mir eben mal die Klasse angeschaut. Ich denke, es ist fast schon ein zu großer Aufwand, wenn man nur eine halbwegs gleichverteilte Zufallszahl im Bereich von 0-n haben möchte. Für weitergehende Anwendungen werde ich die mögliche Verwendung auf jeden Fall prüfen.

    Vielen Dank!

    Ciao
    shoat



  • volkard schrieb:

    kannste mal messen, ob bei nem würfel mit einer million seiten die 0 sichtbar häufiger als die 999999 drankommt? wenn da nix schief wirkt, brauchste dir keine gedanken über nen besseren zufallszahlengenerator zu machen, dann ist deiner echt gut.

    Ich habe eben ein paar Tests durchlaufen lassen. Es ist keine signifikante Abweichung festzustellen. Warum auch? (siehe auch unten)

    Gregor schrieb:

    Wenn die ints, die der Zufallsgenerator ausspuckt, gleichverteilt sind, dann sollte die 999999 etwas seltener vorkommen als die 0

    Ich bin mir dessen bewußt! Ich denke, Du meinst folgendes Problem:

    Beispiel (vereinfacht!):

    Automatische Zufallszahlenerzeugung zz_10 im Bereich von 0-9 durch die Klasse Random. 
    Annahme: Die Wahrscheinlichkeit für jeden Wert von 0-9 ist jeweils 0,1.
    
    Ich benötige nur Zufallszahlen zz_3 im Bereich von 0-3. Also rechne ich
    zz_3 = zz_10 % 4 ;
    
    Damit ist
    zz_3 = 0 für zz_10 == {0,4,8}
    zz_3 = 1 für zz_10 == {1,5,9}
    zz_3 = 2 für zz_10 == {2,6}
    zz_3 = 3 für zz_10 == {3,7}
    
    Damit ergibt sich folgende Wahrscheinlichkeitsverteilung 
    für zz_3 == 0    0,3
    für zz_3 == 1    0,3
    für zz_3 == 2    0,2
    für zz_3 == 3    0,2
    

    Die Zahlen sind also nicht mehr wirklich zufällig. Es gibt es Präferenz zu den niedrigeren Zahlen!

    Da allerdings die tatsächlich automatisch erzeugten Zufallszahlen (vgl. zz_10) im Bereich von Integer.MinValue (-2^31) bis Integer.MaxValue(2^31-1) und die von mir errechneten Zufallszahlen (vgl. zz_3) sehr klein bleiben, fällt die Verfälschung nicht besonders ins Gewicht.

    Oder sollte ich vielleicht doch lieber die nextLong() anstatt von nextInt() benutzen? Was meint ihr?

    Gregor schrieb:

    (abgesehen davon, dass in dem Code da oben natürlich garkeine 0 entsteht). Das bringt das "%", das hier verwendet wird, wohl mit sich.

    Um genau zu sein liegt es nicht an dem Modulo, sondern an dem +1 . 😉

    Vielen Dank!

    Ciao
    shoat


Anmelden zum Antworten