Zufallszahlen und ihre "Parameter"



  • Hallo Leute!
    Also ich erzeuge ANZAHL (il) Zufallszahlen. Nun möchte ich gerne die Bedingungen quasi rekonstruieren, die zu genau diesen Zufallszahlen geführt haben. Es ist ja bekanntlich so, wenn man bei gleicher Zeit und bei mehreren Zufalls-Funktions aufrufen auch gleiche (Zufalls)zahlen bekommt, hier ist die Bedingung also die ZEIT. Das heißt, so denke ich mir, wenn ich jetzt Zufallszahlen bei einem bestimmten Zeitpunkt generiere, kann ich genau diese Zufallszahlen wieder rekonstruieren, indem ich den Zeitpunkt der Erzeugung speichere und ihn dann als Parameter wieder einer Zufallsfunktion übergebe, oder?!

    Ist diese Theoretische Überlegung richtig? Wenn ja, wie speichere ich den Zeitpunkt bzw die Parameter dieses "Standardscodes" für Zufallszahlen und übergebe diese einer weiteren Zufallsfunktion??

    /*
    * Berechne Zufallszahl im Bereich [von, bis]
    */
    int zufall(int von, int bis)
    {
        static int srand_bereits_aufgerufen = 0;
    
        if (!srand_bereits_aufgerufen)
        {
            srand(time(NULL));
            srand_bereits_aufgerufen = 1;
        }
        return von + rand()%(bis-von+1);
    }
    

    Wie gesagt, ich erzeuge ANZAHL Zufallszahlen (hängt von der Eingabe ab) und möchte diese später wieder erzeugen können, deswegen meine Vorüberlegungen..ist dies denn machbar? Wenn ja wie?

    Danke!!!

    MfG
    Fraenky



  • zB so:

    #include <cstdlib>
    #include <ctime>
    
    #include <iostream>
    
    using namespace std;
    
    int main( )
    {
        unsigned int seed = static_cast< unsigned int >( time( 0 ) );
        srand( seed );
    
        for( int i = 0; i < 5; ++ i ) { // Erste Folge aus 5 Zufallszahlen
    
            cout.fill( ' ' );
            cout.width( 2 );
            cout << i;
            cout << ": ";
            cout.fill( ' ' );
            cout.width( 6 );
            cout << rand( ) << endl;
        }
    
        for( int i = 0; i < 5; ++ i ) { // Zweite Folge
    
            cout.fill( ' ' );
            cout.width( 2 );
            cout << i;
            cout << ": ";
            cout.fill( ' ' );
            cout.width( 6 );
            cout << rand( ) << endl;
        }
    
        srand( seed ); // srand auf alten Wert setzten
    
        for( int i = 0; i < 5; ++ i ) { // Dritte, zur ersten identische Folge
    
            cout.fill( ' ' );
            cout.width( 2 );
            cout << i;
            cout << ": ";
            cout.fill( ' ' );
            cout.width( 6 );
            cout << rand( ) << endl;
        }
    }
    

    Greetz, Swordfish



  • hier kommen auch schnelle antworten...manoman.. 👍 😉

    ich verstehe diese Zeile nicht ganz:

    unsigned int seed = static_cast< unsigned int >( time( 0 ) );
    

    warum wird hier gecastet?

    wenn ich jetzt aber mehrere Zufallszahlen auf einmal haben möchte, muss doch srand() immer unterschiedlich sein, zB hier:

    //Anzhal der Textänge il Instanzen erzeugen..
     CZufall *CZahl;
     CZahl=new CZufall[il];
    
    // il Zufallszahlen und dazugehörige Zeiten erzeugen
     for(i=0; i<il; i++)
     {
     CZahl[i].erzeugeZahl(min, max);
     }
    

    in der Klasse:

    void CZufall::erzeugeZahl(int min, int max)
    {
        static int srand_set = 0;
    
        if (!srand_set)
        {
            srand(time(NULL));
            srand_set = 1;
        }
        zahl = min+rand()%(max-min+1);
    };
    

    wie kann ich denn jetzt, die ganzen Zeiten (Parameter), zB wenn il=10 ist, abspeichern? das also quasi in eine Funktion einer Klasse (hier CZufall) packen??

    Danke!!



  • warum wird hier gecastet?

    Weil die Funktion time kein unsigned int zurückliefert, sondern, soweit ich weiß, einen signed-Wert. (War das nicht sogar time_t, was dann ein typedef von long oder so ist?)

    wenn ich jetzt aber mehrere Zufallszahlen auf einmal haben möchte, muss doch srand() immer unterschiedlich sein

    Nein, Du brauchst srand nur einmal zu setzen und erhältst dann bei jedem Aufruf von rand eine andere Zufallszahl. Also, ich würde Deine Klasse wahrscheinlich folgendermaßen schreiben:

    class CZufall
    {
    public:
    
        static void setzeSrand (unsigned int wert);
        static unsigned int holeSrand ();
    
        void erzeugeZahl (int min, int max);
        int holeZahl ();
    
    private:
    
        static unsigned int srandWert;
        int zahl;
    };
    
    unsigned int CZufall::srandWert=0;
    
    void CZufall::setzeSrand (unsigned int wert)
    {
        srandWert=wert;
    
        srand (srandWert);
    }
    
    uinsigned int CZufall::holeSrand ()
    {
        return srandWert;
    }
    
    void CZufall::erzeugeZahl (int min, int max)
    {
        zahl=min+rand ()%(max-min+1);
    }
    
    int CZufall::holeZahl ()
    {
        return zahl;
    }
    

    static bedeutet hier, daß die Variable srandWert nur ein einziges Mal für die gesamte Klasse existiert und nicht individuell für jedes Objekt. Für die Funktion ist das analog zu betrachten: Man braucht kein Objekt, sondern kann das ganze folgendermaßen aufrufen:

    CZufall::setzeSrand (static_cast<unsigned int> (time (NULL));
    

    Das machst Du einmal am Anfang. Dann läßt Du Dir die Zufallszahlen geben. Und wenn Du sie rekonsruieren möchstest, rufst Du

    CZufall::setzeSrand (CZufall::holeSrand ())
    

    auf und läßt Dir wieder Zufallszahlen geben.



  • hi!
    kann ich das

    CZufall::setzeSrand (static_cast<unsigned int> (time (NULL));
    

    auch in einen Kontruktor packen, um es nicht in der main() Funktion aufrufen zu müssen?

    // Konstruktor
    CZufall::CZufall()
    {
    	setzeSrand(time(NULL));
    };
    
    void CZufall::setzeSrand(unsigned int wert) 
    { 
        zeit=wert; 
        srand(zeit); 
    };
    

    Leider bekomme ich keine Ausgabe der (unterschiedlichen) Zeiten...

    mit

    for(i=0; i<il; i++)
     {
     CZahl[i].erzeugeZahl(min, max);
     }
    for(i=0; i<il; i++)
     {
     cout << " " << i+1 << ":\t"; 
     CZahl[i].zeigeZahl();
     cout << "\t";
     //iZeit[i];
     CZahl[i].zeigeZeit();
     cout << endl;
     }
    

    bekomme ich zwar unterschiedliche Zufallszahlen,aber die Zeiten sind alle gleich.. 😕

    CZahl.zeigeZeit():

    void CZufall::zeigeZeit()
    {
    cout << zeit;	
    };
    

    MfG
    Fraenky



  • bekomme ich zwar unterschiedliche Zufallszahlen,aber die Zeiten sind alle gleich..

    ich glaube ich hab das mit den Zufallszahlen und srand() nciht so wirlkich verstanden..

    Ich habe jetzt die Zufallszhalen, trotz gleicher Zeiten, rekonstruieren lassen und sie wurden gleich. Wenn ich das

    CZufall::setzeSrand(CZufall::returnZeit());
    

    auskommentiere, entstehen wieder unterschiedliche Zahlen..also funktioniert das ganze so wie es soll! 🙂

    Nein, Du brauchst srand nur einmal zu setzen und erhältst dann bei jedem Aufruf von rand eine andere Zufallszahl.

    Bleibt bloß die Frage, warum ich srand nur einmal setzen muss (meine gleichen Zeiten) aber trotzdem unterschiedliche Zufallszahlen herausbekommen..das habe ich wohl nciht verstanden.. 🙄
    Kann man irgendwo nachlesen?

    Für die Funktion ist das analog zu betrachten: Man braucht kein Objekt, sondern kann das ganze folgendermaßen aufrufen:

    CZufall::setzeSrand (static_cast<unsigned int> (time (NULL));
    

    Ich brauche kein Objekt? Also ich instanziere il Objekte, halt so viele, wie ich Zufallszahlen brauche:

    CZufall *CZahl;
    CZahl= new CZufall[il];
    

    Kann ich denn die beiden Funktionen

    CZufall::setzeSrand(static_cast<unsigned int> (time (NULL)));
    CZufall::setzeSrand(CZufall::returnZeit());
    

    irgendwie in meine Klasse implementieren, damit ich sie nur nach dem Schema

    CZufall.rekZahl();
    

    aufrufen kann? Die erstere zB wie beschrieben im Konstruktor?

    Danke!

    MfG
    Fraenky



  • Fraenky schrieb:

    Nein, Du brauchst srand nur einmal zu setzen und erhältst dann bei jedem Aufruf von rand eine andere Zufallszahl.

    Bleibt bloß die Frage, warum ich srand nur einmal setzen muss (meine gleichen Zeiten) aber trotzdem unterschiedliche Zufallszahlen herausbekommen..das habe ich wohl nciht verstanden.. 🙄
    Kann man irgendwo nachlesen?

    Im Wikipedia unter Zufallszahlen könntest du mal nachsehen.

    Ansonsten: srand() setzt einen sog. "Seed" für die Berechnung. Jeder anschließende Aufruf von rand() berechnet daraus eine Zufallszahl (die er ausgibt) und einen neuen Seed, der den vorigen ersetzt.



  • Jeder anschließende Aufruf von rand() berechnet daraus eine Zufallszahl (die er ausgibt) und einen neuen Seed, der den vorigen ersetzt.

    Ah, das sagt natürlich alles..das der Wert einfach durch die Zufallsfunktion rand() bei jedem Durchlauf neu generiert wird..

    Das heißt ja dann quasi bei mir, dass wir nur den aller ersten Anfangswert von srand() speichern und dann rand() daraus immer die gleichen Parameter (seed) generiert.. Ist der Anfangswert von srand() unterschiedlich, gibts auch unterschieldiche nachfolge seeds..und darum andere Zufallszahlen..jo cih dnek ich habs verstanden! 👍

    Kann man trotzdem jeden Nachfolge Seed, der von rand() produziert wird, speichern??

    MfG
    Fraenky



  • kann ich das

    CZufall::setzeSrand (static_cast<unsigned int> (time (NULL));
    

    auch in einen Kontruktor packen, um es nicht in der main() Funktion aufrufen zu müssen?

    Dann würdest Du es ja wieder für jede Zahl einzeln aufrufen, da der Konstruktor jedesmal beim Erstellen einer Variable von dem Typ aufgerufen wird. Und in Deinem Fall mit dem Zahlenfeld wären das dann sovielmal wie Du Zahlen hast.

    Leider bekomme ich keine Ausgabe der (unterschiedlichen) Zeiten...
    mit [...] bekomme ich zwar unterschiedliche Zufallszahlen,aber die Zeiten sind alle gleich..

    Ja, gut, das Erstellen dauert ja nur einen ganz kurzen Augenblick. Das heißt, wenn er es in einer Millisekunde schafft, Dein Feld zu deklarieren und den Konstruktor entsprechend oft aufzurufen, dann ist es natürlich klar, daß time (NULL) immer den gleichen Wert, eben den, der in dieser Millisekunde dran war, liefert.

    Ich brauche kein Objekt? Also ich instanziere il Objekte, halt so viele, wie ich Zufallszahlen brauche:

    Ich sagte, wenn Du eine Funktion oder eine Variable static machst, dann brauchst Du kein Objekt, um sie zu benutzen. Wenn Du in Deiner Klasse also die Membervariable int zahl hast und Du erstellst ein Feld mit 10 Objekten dieser Klasse, dann hat natürlich jedes Objekt/jeder Feldeintrag eine eigene Membervariable zahl. Hast Du allerdings eine Membervariable static unsigned int srandWert, dann gibt es diese Variable nur einmal, egal, wie viele Objekte Du hast. Alle Objekte teilen sich diese Variable und infolgedessen brauchst Du nichtmal ein Objekt, sondern kannst die Variable auch direkt aufrufen (wenn sie public wäre). Das gleiche gilt für statische Funktionen. Auch für sie brauchst Du kein Objekt, sondern kannst sie gleich mit CZufall::setzeSrand (...) aufrufen.

    Kann man trotzdem jeden Nachfolge Seed, der von rand() produziert wird, speichern??

    Soweit ich das sehe, ist die Zufallszahl, die Du bekommst, der nächste Seed. Die Funktion srand sieht im Wesentlichen folgendermaßen aus:

    void srand (unsigned int seed)
    {
        holdrand=(long) seed;
    }
    

    (Ich habe den ganzen Schnickschnack, wie #ifdef, in das er sowieso nicht reingeht, und dieses __cdecl mal rausgenommen, damit Du die wesentliche Funktionsweise sehen kannst. holdrand scheint wohl eine globale Variable zu sein. Die rand-Funktion sieht dann analog so aus:

    int rand ()
    {
        return ((holdrand=holdrand*214013L+2531011L)>>16)&0x7fff;
    }
    

    Wie Du siehst, liefert er eine Zufallszahl zurück, die auch gleichzeitig das neue holdrand ist.

    Ich frage mich jetzt jedoch immer noch, warum Du da jede einzelne Zeit speichern willst. Und warum Du vor jeder Zufallszahl das srand machen wolltest (srand in den Konstruktor packen). Vielleicht hast Du es ja bereits verstanden, aber falls nicht, schreibe ich Dir nochmal etwas dazu: Die srand-Funktion liefert einen "Größenfaktor", anhand dessen Zufallszahlen erzeugt werden. Dieser Faktor stellt sozusagen die Anfangsbedingung dar. Da der Computer nicht in der Lage ist, echte Zufallszahlen zu ermitteln, muß man ihm eine Anfangszahl nennen, aus der er dann die Zufallszahlen generiert. Ein Anfangswert, eine Startbedingung. Und schon kann man sich Zufallszahlen ausgeben lassen. Deshalb ruft man srand auch nicht mehrmals auf, da es ja die Startbedingung darstellt. Es macht nicht allzuviel Sinn bzw. es ist zu umständlich, für jede neue Zufallszahl eine eigene Startbedingung zu schaffen. Deshalb ruft man srand nur einmal auf. Will man die Meßreihe an Zufallszahlen rekonstruieren, merkt man sich am Anfang den Wert, den man srand übergibt (und deshalb sagte ich: mach die srand-Wert-Variable static, so hast Du unabhängig von der Anzahl Deiner Zufallszahlen nur einen Startwert, der trotzdem in der Klasse gespeichert wird und nicht außerhalb irgendwo rumliegen muß). Beim zweiten Durchlauf, der wieder die gleichen Zufallszahlen liefern soll, ruft man srand wieder auf (und legt damit eine neue Startbedingung fest) und übergibt der Funktion den alten Startwert.


Log in to reply