2009-06-10 19 views
5

Ich habe einige Empfehlungen gesehen, Pseudozufallszahlengeneratoren nicht mehr als einmal pro Ausführung zu setzen, aber niemals mit einer gründlichen Erklärung. Natürlich ist es leicht, das folgende (C/C++), um zu sehen, warum Beispiel keine gute Idee ist:Probleme beim mehrfachen Ausseuchen eines Pseudozufallszahlengenerators?

int get_rand() { 
    srand(time(NULL)); 
    return rand(); 
} 

seit get_rand mehrmals pro Sekunde Aufruf führt zu Ergebnissen wiederholt.

Aber wäre das folgende Beispiel nicht immer noch eine akzeptable Lösung?

MyRand.h

#ifndef MY_RAND_H 
#define MY_RAND_H 

class MyRand 
{ 
    public: 
    MyRand(); 
    int get_rand() const; 
    private: 
    static unsigned int seed_base; 
}; 

#endif 

MyRand.cpp

#include <ctime> 
#include <cstdlib> 
#include "MyRand.h" 

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL)); 

MyRand::MyRand() 
{ 
    srand(seed_base++); 
} 

int MyRand::get_rand() const 
{ 
    return rand(); 
} 

main.cpp

#include <iostream> 
#include "MyRand.h" 

int main(int argc, char *argv[]) 
{ 
    for (int i = 0; i < 100; i++) 
    { 
    MyRand r; 
    std::cout << r.get_rand() << " "; 
    } 
} 

, d.h. obwohl der Konstruktor MyRand mehrere Male in schneller Folge aufgerufen wird, hat jeder Aufruf von srand einen anderen Parameter. Offensichtlich ist dies nicht Thread-sicher, aber auch nicht rand.

+0

Ich könnte hinzufügen, dass der ganze Zweck dieser Übung ist, die "Last" von srand vom Client von 'MyRand' zu befreien, wo' MyRand' einen Würfel modellieren könnte. Auf der anderen Seite, wenn wir auch Glücksräder, Münzwürfe usw. auf die gleiche Weise bauen, werden wir eine Menge Samen bekommen. – a038c56f

Antwort

6

Jedes Mal, wenn Sie eine Pseudozufallszahlengeneratorfunktion aufrufen, nimmt der Generator einen internen Zustand ein und erzeugt eine Pseudozufallszahl und einen neuen internen Zustand. Der Algorithmus zum Transformieren des internen Zustands wird sorgfältig ausgewählt, so dass die Ausgabe zufällig erscheint.

Wenn Sie den Zufallsgenerator generieren, legen Sie diesen internen Status fest. Wenn Sie den internen Status auf einen vorhersehbaren Wert zurücksetzen, werden Sie das Erscheinungsbild der Zufälligkeit verlieren.

Zum Beispiel ist ein beliebter, einfacher Zufallsgenerator ein linearer kongruenter Generator. Nummern werden wie folgt generiert:

X[n+1] = (a X[n] + c) mod m 

In diesem Fall ist X [n + 1] ist sowohl das Ergebnis und der neue interne Zustand.Wenn Sie den Generator jedes Mal, Samen, wie Sie oben vorschlagen, erhalten Sie eine Sequenz erhalten, die wie folgt aussieht:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...} 

wo b Ihr seed_base ist. Das sieht überhaupt nicht zufällig aus.

+0

Mein main.cpp-Beispiel ist etwas übertrieben, um das Fehlen des Fehlers des ersten Beispiels zu demonstrieren. Die Instantiierung eines neuen Objekts für jeden Aufruf von "get_rand" führt zu der oben beschriebenen Situation, aber das ist nur eine verschwenderische Programmierung. Unter der Annahme, dass die Anzahl der 'MyRand'-Instanziierungen im Vergleich zu' get_rand'-Aufrufen gering ist, sehen die Dinge etwas besser aus. – a038c56f

1

Wenn Ihr Seed vorhersehbar ist, was es hier ist, da Sie es nur inkrementieren, wird auch die Ausgabe von rand() vorhersagbar sein.

Es hängt wirklich davon ab, warum Sie Zufallszahlen generieren möchten und wie "zufällig" für Sie eine akzeptable Zufallszahl ist. In Ihrem Beispiel kann es dazu kommen, dass Duplikate in schneller Folge vermieden werden, und das kann gut genug für Dich sein. Wichtig ist schließlich, dass es läuft.

Auf fast jeder Plattform gibt es eine bessere Möglichkeit, Zufallszahlen als rand() zu generieren.

1

Nun, es ist zusätzliche Verarbeitung, die nicht getan werden muss.

In diesem Szenario würde ich den Konstruktor nur einmal mit einem zeitbasierten Startwert vor dem Start der Schleife aufrufen. Das garantiert zufällige Ergebnisse ohne den zusätzlichen Aufwand, den Seed für jede Iteration zu ändern.

Ich würde nicht denken, Ihre Methode ist mehr zufällig als das.

0

Sie können sich eine Zufallszahlengenerierung vorstellen (dies ist nicht unbedingt eine Implementierung, sondern dient als Illustration) als eine Tabelle von Werten. Wenn Sie sich daran erinnern, in Statistiken für einfache Zufallsstichproben etwas davon zu tun, sagt Ihnen ein Seed im Grunde, in welcher Zeile und Spalte Sie in Ihrer großen Tabelle von Zufallszahlen anfangen sollen. Das nochmalige Nachsenden ist einfach unnötig, da wir bereits davon ausgehen können, dass die Zahlen bereits normal verteilt sind.

Es gibt einfach keinen zusätzlichen Vorteil für die Aussaat mehr als einmal, da dies gut genug sein sollte (abhängig von der Anwendung). Wenn Sie "mehr" Zufallszahlen benötigen, gibt es viele Methoden der Zufallszahlengenerierung. Ein Fall, an den ich denken kann, ist, talentierte Zufallszahlen zu erzeugen.

Während Ihre Lösung akzeptabel ist, werden Ihre Zahlen nicht zufälliger sein als einmal global. Srand sollte generell nicht in einen Konstruktor gehören. Wenn Sie Zufallszahlen unterstützen möchten, starten Sie sie einmal, wenn das Programm startet, und vergessen Sie sie.

Verwandte Themen