2010-12-15 11 views
4

Ich erstellte ein einfaches Programm, das einen Münzwurf für meine Klasse simuliert. (Eigentlich ist Klasse über diesen Begriff und ich arbeite gerade durch den Rest der Projekte, die nicht benötigt wurden). Es beinhaltet das Erzeugen und Aufrufen einer Funktion, die eine Zufallszahl zwischen 1 und 2 erzeugt. Ursprünglich habe ich versucht, den Zufallszahlengenerator innerhalb der Funktion zu setzen, die ihn verwenden würde (coinToss); Es hat jedoch keine Zufallszahl erzeugt. Jedes Mal wenn das Programm ausgeführt wurde, war es die gleiche Zahl, als ob ich nurC++: Zufallszahlengenerator außerhalb von main()

rand()

statt

unsigned seed = time(0); 
srand(seed); 
rand(); 

Doch benutzt hatte, wenn ich die oben bewegt innerhalb

int main() 

es hat gut funktioniert.

Meine Frage ist 1), warum hat es nicht funktioniert, wenn Setup in der Funktion, die sie aufgerufen und (2) wie funktioniert rand() hat Zugang zu dem, was von srand() getan wurde, wenn sie beide nicht in der gleichen Funktion auftreten? Offensichtlich bin ich ein Anfänger also bitte vergib mir, wenn ich die Frage nicht richtig formulierte. Auch hat mein Buch nur kurz auf rand() und srand() berührt, das ist alles, was ich wirklich weiß.
danke für jede Hilfe!

Einschlägige Code:

Erster Versuch, das hat nicht funktioniert:

int main() 
{ 
    //........... 
    coinToss(); 
    //........... 
} 

int coinToss() 
{ 
    unsigned seed = time(0); 
    srand(seed); 

    return 1 + rand() % 2; 
    } 

Zweiter Versuch, die Arbeit haben:

int main() 
{ 
    unsigned seed = time(0); 
    srand(seed); 

    coinToss(); 
} 

int coinToss() 
{ 
    return 1 + rand() % 2; 
    } 
+0

Indem Sie% 2 tun, überprüfen Sie wirklich nur die Zufälligkeit von Bit 0 der Zahl (die im rand() Fall als nicht zufällig gezeigt wurde). Ich habe einmal (auf SO) eine ganzzahlige Division vorgeschlagen. dh rand()/(RAND_MAX/2) aber jemand wies darauf hin, dass nur Bit 31 der Nummer überprüft wurde. –

Antwort

9

Wahrscheinlich möchten Sie den Zufallsgenerator nur einmal einsetzen. rand() gibt die nächste Pseudozufallszahl aus dem internen Generator zurück. Jedes Mal, wenn Sie rand() anrufen, erhalten Sie die nächste Nummer vom internen Generator.

srand() setzt jedoch die Anfangsbedingungen des Zufallszahlengenerators. Man kann sich vorstellen, dass es den Startpunkt für den internen Zufallszahlengenerator darstellt (in Wirklichkeit ist es viel komplizierter, aber es ist ein nützliches kognitives Modell, dem man folgen kann).

Also, Sie sollten srand(time(0)) genau einmal in Ihrer Anwendung aufrufen - irgendwo in der Nähe von Anfang. Danach können Sie rand() so oft wie Sie möchten anrufen!

jedoch

Ihre eigentliche Frage zu beantworten - die erste Version nicht funktioniert, weil time() die Anzahl der Sekunden seit der Epoche kehrt. Wenn Sie also in einer Sekunde mehrmals (coinToss()) anrufen (wenn Sie beispielsweise 100 Münzwürfe simulieren wollten), würden Sie den Zufallszahlengenerator immer mit der gleichen Nummer säen und dadurch seinen internen Status (und damit die nächste Zahl) zurücksetzen Sie bekommen) jedes Mal.

Wie dem auch sei - mit time() als Saatgut zu srand() aus diesem Grund etwas beschissen - time() chage nicht sehr oft, und schlimmer noch, es ist vorhersehbar. Wenn Sie die aktuelle Uhrzeit kennen, können Sie herausfinden, was rand() zurückgibt. Das Internet hat viele, viele Beispiele für bessere srand() Samen.

+0

Ich könnte mir vorstellen, dass mehrere Aufrufe von 'srand (time (0))' immer noch OK sind, wenn sie durch genug Zeit getrennt sind (was vielleicht schwer zu garantieren ist, also vielleicht am besten nur einmal am Anfang). – FrustratedWithFormsDesigner

+0

danke! Also, erzeugte es die gleiche Antwort, weil 'unsigned seed = time (0)' Schlüssel 'same' zum Systemtakt und das Programm dauert weniger als eine Sekunde zu laufen, so 'unsigned same = time (0)' wird das gleiche ziehen Wert jedes Mal, wenn es aufgerufen wird, da 'Zeit (0)' auf Sekunden basiert? – knobcreekman

+0

@knobcreekman: Nicht sicher ... Sie könnten das vielleicht testen, indem Sie zwischen den Aufrufen der Funktion einen "sleep()" von 2-5 Sekunden hinzufügen. – FrustratedWithFormsDesigner

1

Seed nur einmal pro Programm, nicht jedes Mal, Sie rufen coinToss()

3

Pseudozufallszahlengeneratoren (wie rand) arbeiten, indem Sie eine einzelne Startnummer (den Startwert) nehmen und bei jeder neuen Nummer eine numerische Transformation durchführen. Sie möchten den Generator nur einmal einsetzen oder er wird ständig zurückgesetzt, was nicht gewünscht ist.

Wie Sie herausgefunden haben, rufen Sie einfach srand nur einmal in main. Beachten Sie auch, dass eine Reihe von rand Implementierungen ziemlich kurze Zyklen auf den niedrigen 4 Bits oder so haben. In der Praxis bedeutet dies, dass Sie einen leicht vorhersagbaren Wiederholungszyklus von Zahlen erhalten. Sie könnten den Rückgabewert von rand um 4-8 Bits nach rechts verschieben, bevor Sie die % 2 übernehmen.

EDIT: Der Anruf würde in etwa so aussehen: return 1 + (rand() >> 6) % 2;

+0

danke für die Info. Ich bin mir nicht sicher, was der letzte Teil über das Verschieben von Bits bedeutet, ich denke, das ist fortgeschrittener als ich. – knobcreekman

+1

@knobcreekman Ich habe meine Antwort bearbeitet, um zu zeigen, wie der Code aussehen würde. Neuere 'rand'-Implementierungen haben diesen Fehler jedoch nicht. –

+0

ok, danke. Ich werde darüber nachlesen – knobcreekman

0

Um auf Mark B Antwort zu erweitern: Es ist nicht so viel, dass der Zufallszahlengenerator zurückgesetzt wird, wenn es eine neue Variable setzt bei der Berechnung des Zufall verwendet werden sollte Zahlen. Ihr Programm arbeitet jedoch nicht so viel zwischen Srand-Anrufen. Jedes Mal, wenn Sie srand (time (0)) aufrufen, wird daher der selbe Seed verwendet, so dass Sie den internen Zustand des Zufallsgenerators zurücksetzen. Wenn Sie dort schlafen, so dass sich die Zeit (0) ändert, erhalten Sie nicht jedes Mal die gleiche Nummer.

Wie Daten von Srand zu Rand übergeben werden, ist ziemlich einfach, eine globale Variable wird verwendet. Alle Namen, die mit einem Unterstrich und einem Großbuchstaben oder zwei Unterstrichen beginnen, sind für Variablen reserviert, die von Ihrem Compiler verwendet werden. Mehr als wahrscheinlich wurde diese Variable als statisch deklariert, so dass sie außerhalb der Übersetzungseinheit nicht sichtbar ist (aka die Bibliotheksdatei, die die Standardbibliothek Ihres Compilers enthält). Dies geschieht, damit #define STUFF 5 Ihre Standardbibliothek nicht bricht.

0

Bei einfachen Simulationen darf der Seed während der Simulation nicht geändert werden. Ihre Simulation wird in diesem Fall "schlechter" sein.

Um dies zu verstehen, sollten Sie pseudozufällige Sequenzen als ein großes Glücksrad sehen. Wenn Sie den Seed ändern, ist es so, als würden Sie die Position ändern, und dann erhält jeder Aufruf von Rand eine andere Nummer. Wenn Sie erneut würfeln, ist es wahrscheinlicher, dass Sie sich wiederholen.