srand() — warum nur einmal aufrufen?

Lesezeit: 9 Minuten

Benutzeravatar von Lipika Deka
Lipika Deka

Bei dieser Frage geht es um einen Kommentar in dieser Frage. Empfohlene Methode zum Initialisieren von sand? Der erste Kommentar sagt das srand() sollte in einer Anwendung nur EINMAL aufgerufen werden. Wieso ist es so?

  • Versuchen Sie in einer Schleife, srand und dann rand aufzurufen

    – Foo Bah

    8. September 2011 um 6:16 Uhr

  • Siehe auch Dilberts Rundgang durch die Buchhaltung.

    – Jonathan Leffler

    3. Januar 2016 um 0:05 Uhr

  • Siehe auch Empfohlene Initialisierungsmethode srand()? Verwenden time(0) für den übergebenen Wert srand() ist simpel – und daher wahrscheinlich angemessen, da srand() ist oft auch simpel (siehe die Beispielimplementierung in der C-Standard was davon ausgeht, dass RAND_MAX 32.767 ist). Eine Lösung in den verknüpften Fragen und Antworten verwendet eine Funktion zum Mischen von 3 Werten – die Ergebnisse von clock(), time(0)und getpid(). Die Verwendung einer Mischfunktion ist eine gute Idee. Es könnte auch ein CRC verwendet werden.

    – Jonathan Leffler

    22. April um 15:17 Uhr

Benutzeravatar von Kornelije Petak
Kornelije Petak

Das hängt davon ab, was Sie erreichen wollen.

Die Randomisierung wird als eine Funktion durchgeführt, die einen Startwert hat, nämlich der Samen.

Für denselben Seed erhalten Sie also immer dieselbe Folge von Werten.

Wenn Sie versuchen, den Startwert jedes Mal festzulegen, wenn Sie einen zufälligen Wert benötigen, und der Startwert dieselbe Nummer ist, erhalten Sie immer denselben “zufälligen” Wert.

Seed wird normalerweise von der aktuellen Zeit genommen, die die Sekunden sind, wie in time(NULL)wenn Sie also immer den Seed setzen, bevor Sie die Zufallszahl nehmen, erhalten Sie die gleiche Zahl, solange Sie die srand/rand-Kombination mehrmals aufrufen in derselben Sekunde.

Um dieses Problem zu vermeiden, wird srand nur einmal pro Anwendung gesetzt, da es zweifelhaft ist, dass zwei der Anwendungsinstanzen in derselben Sekunde initialisiert werden, sodass jede Instanz dann eine andere Folge von Zufallszahlen hat.

Es besteht jedoch die geringe Möglichkeit, dass Sie Ihre App (insbesondere wenn es sich um ein kurzes oder ein Befehlszeilentool oder ähnliches handelt) viele Male in einer Sekunde ausführen, dann müssen Sie auf eine andere Art der Auswahl zurückgreifen Seed (es sei denn, die gleiche Reihenfolge in verschiedenen Anwendungsinstanzen ist für Sie in Ordnung). Aber wie gesagt, das hängt von Ihrem Anwendungskontext ab.

Außerdem möchten Sie vielleicht versuchen, die Genauigkeit auf Mikrosekunden zu erhöhen (um die Wahrscheinlichkeit desselben Seeds zu minimieren), erfordert (sys/time.h):

struct timeval t1;
gettimeofday(&t1, NULL);
srand(t1.tv_usec * t1.tv_sec);

  • Randnotiz: gettimeofday ist in POSIX 2008 veraltet. Stattdessen führt es ein clock_gettime die möglicherweise eine Verknüpfung mit erfordern -lrt. Es ist jedoch möglicherweise noch nicht auf vielen Plattformen verfügbar. Unter Linux ist das in Ordnung. Auf dem Mac ist es meiner Meinung nach noch nicht verfügbar. In Windows wird es wahrscheinlich nie verfügbar sein.

    – Shahbaz

    7. April 2013 um 10:23 Uhr

  • t1.tv_usec ist eine lange Ganzzahl, und srand nimmt als Eingabe eine Ganzzahl ohne Vorzeichen. (Und ich bin gerade auf ein Problem gestoßen, bei dem es einen Unterschied macht.)

    – Jiminion

    26. April 2017 um 13:12 Uhr


  • Das hat funktioniert. Durch die Erhöhung der Genauigkeit wurden meine Duplikate beseitigt. Vielen Dank sehr viel. Ich habe eine Lieferfrist und das hat meinen Hintern gerettet.

    – Beezer

    1. Dezember 2020 um 4:00 Uhr

Benutzeravatar von phoxis
Phoxie

Zufallszahlen sind eigentlich Pseudozufälle. Zunächst wird ein Seed gesetzt, von dem jeder Call ausgeht rand erhält eine Zufallszahl und modifiziert den internen Zustand und dieser neue Zustand wird im nächsten verwendet rand anrufen, um eine andere Nummer zu bekommen. Da eine bestimmte Formel verwendet wird, um diese “Zufallszahlen” zu generieren, wird nach jedem Anruf ein bestimmter Wert von Seed festgelegt rand gibt dieselbe Nummer aus dem Anruf zurück. Zum Beispiel srand (1234); rand (); gibt denselben Wert zurück. Wenn Sie den Anfangszustand einmal mit dem Startwert initialisieren, werden genügend Zufallszahlen generiert, da Sie den internen Zustand nicht mit festlegen srandwodurch es wahrscheinlicher wird, dass die Zahlen zufällig sind.

Im Allgemeinen verwenden wir die time (NULL) zurückgegebener Sekundenwert beim Initialisieren des Seed-Werts. Sagen Sie die srand (time (NULL)); ist in einer Schleife. Dann kann die Schleife mehr als einmal in einer Sekunde iterieren, daher die Anzahl der Wiederholungen der Schleife innerhalb der Schleife in einer Sekunde rand Aufruf in der Schleife wird dieselbe “Zufallszahl” zurückgeben, was nicht erwünscht ist. Durch einmaliges Initialisieren beim Programmstart wird der Seed einmal und jedes Mal gesetzt rand angerufen wird, wird eine neue Nummer generiert und der interne Zustand geändert, also der nächste Anruf rand gibt eine Zahl zurück, die zufällig genug ist.

Zum Beispiel dieser Code von http://linux.die.net/man/3/rand:

static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
    next = next * 1103515245 + 12345;
    return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
    next = seed;
}

Der innere Zustand next wird als global deklariert. Jeder myrand Aufruf ändert den internen Zustand und aktualisiert ihn und gibt eine Zufallszahl zurück. Jeder Anruf von myrand wird eine andere haben next value daher gibt die Methode bei jedem Aufruf die unterschiedlichen Nummern zurück.

Schaue auf die mysrand Implementierung; Es setzt einfach den Seed-Wert, an den Sie übergeben next. Wenn Sie also die next Wert jedes Mal vor dem Anruf gleich rand Aufgrund der identischen Formel, die darauf angewendet wird, wird derselbe Zufallswert zurückgegeben, was nicht wünschenswert ist, da die Funktion zufällig gemacht wird.

Aber je nach Ihren Bedürfnissen können Sie den Seed auf einen bestimmten Wert setzen, um bei jedem Durchlauf dieselbe “Zufallssequenz” zu generieren, beispielsweise für einige Benchmarks oder andere.

  • Meinst du nicht (unsigned long seed) für den Parameter von mysrand() ?

    – Jiminion

    26. April 2017 um 13:15 Uhr

  • @Jiminion Dies ist ein Code-Snippet von man srand . Der Bereich reicht von 0 bis 32767 (unter der Annahme von RAND_MAX), was viel weniger ist als die long Angebot. Die Zustandsvariable next gemacht wird long da die interne Multiplikation und Addition den Bereich von an überschreitet unsigned int. Danach wird das Ergebnis innerhalb des oben angegebenen Bereichs skaliert oder modifiziert. Obwohl Sie den Samen machen können long.

    – phoxie

    8. Mai 2017 um 10:22 Uhr


  • Beachten Sie, dass der C-Standard auch das gezeigte Code-Snippet enthält.

    – Jonathan Leffler

    7. September 2017 um 5:15 Uhr

Benutzeravatar von Steve Summit
Steve Gipfel

Kurze Antwort: anrufen srand() ist nicht wie “Würfeln” für den Zufallszahlengenerator. Es ist auch nicht wie das Mischen eines Kartenspiels. Wenn überhaupt, ist es eher so, als würde man einfach ein Kartenspiel schneiden.

Stellen Sie sich das so vor. rand() Deals aus einem großen Kartenspiel, und jedes Mal, wenn Sie mitgehen, nimmt es nur die nächste Karte von der Oberseite des Decks, gibt Ihnen den Wert und legt diese Karte unter das Deck zurück. (Ja, das bedeutet, dass sich die “zufällige” Sequenz nach einer Weile wiederholt. Es ist ein sehr großes Deck, aber normalerweise 4.294.967.296 Karten.)

Außerdem wird jedes Mal, wenn Ihr Programm läuft, ein brandneues Kartenspiel im Spieleshop gekauft, und jedes nagelneue Kartenspiel hat immer die gleiche Reihenfolge. Wenn Sie also nichts Besonderes tun, erhält Ihr Programm jedes Mal, wenn es ausgeführt wird, genau dieselben “Zufallszahlen”. rand().

Jetzt könnten Sie sagen: “Okay, also wie mische ich das Deck?” Und die Antwort – zumindest soweit rand und srand besorgt sind — ist, dass es keine Möglichkeit gibt, das Deck zu mischen.

Also was macht srand tun? Basierend auf der Analogie, die ich hier aufgebaut habe, Anrufen srand(n) ist im Grunde wie zu sagen: “Cut the Deck n Karten von oben”. Aber Moment mal, noch etwas: Es ist tatsächlich so Beginnen Sie mit einem anderen brandneuen Deck und schneiden Sie es n Karten von oben.

Also wenn du anrufst srand(n), rand(), srand(n), rand()…, mit dem gleichen n Jedes Mal erhalten Sie nicht nur eine nicht sehr zufällige Sequenz, sondern tatsächlich dieselbe Nummer zurück rand() jedes Mal. (Wahrscheinlich nicht die gleiche Nummer, die Sie übergeben haben srandaber die gleiche Nummer zurück von rand über und über.)

Das Beste, was Sie tun können, ist, das Deck zu schneiden einmaldas heißt anrufen srand() einmal, zu Beginn Ihres Programms, mit an n das ist ziemlich zufällig, so dass Sie jedes Mal, wenn Ihr Programm läuft, an einer anderen zufälligen Stelle im großen Stapel beginnen. Mit rand()das ist wirklich das Beste, was Sie tun können.

[P.S. Yes, I know, in real life, when you buy a brand-new deck of cards it’s typically in order, not in random order. For the analogy here to work, I’m imagining that each deck you buy from the game shop is in a seemingly random order, but the exact same seemingly-random order as every other deck of cards you buy from that same shop. Sort of like the identically shuffled decks of cards they use in bridge tournaments.]


Nachtrag: Für eine sehr niedliche Demonstration der Tatsache, dass Sie für einen bestimmten PRNG-Algorithmus und einen bestimmten Seed-Wert immer dieselbe Sequenz erhalten, sehen Sie sich diese Frage an (die sich auf Java bezieht, nicht auf C, aber trotzdem).

Der Grund ist, dass srand() setzt den Anfangszustand des Zufallsgenerators, und alle Werte, die der Generator erzeugt, sind nur “zufällig genug”, wenn Sie den Zustand zwischendurch nicht selbst berühren.

Zum Beispiel könnten Sie Folgendes tun:

int getRandomValue()
{
    srand(time(0));
    return rand();
}

und dann, wenn Sie diese Funktion wiederholt so aufrufen time() dieselben Werte in benachbarten Aufrufen zurückgibt, wird nur derselbe Wert generiert – das ist beabsichtigt.

Benutzeravatar von achoora
Achora

Eine einfachere Lösung für die Verwendung srand() zum Generieren unterschiedlicher Startwerte für Anwendungsinstanzen, die in der gleichen Sekunde ausgeführt werden, ist zu sehen.

srand(time(NULL)-getpid());

Diese Methode macht Ihren Seed sehr zufällig, da es keine Möglichkeit gibt, zu erraten, wann Ihr Thread gestartet wurde, und die PID wird auch anders sein.

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

srand startet den Pseudozufallszahlengenerator. Wenn Sie es mehr als einmal anrufen, werden Sie den RNG neu setzen. Und wenn Sie es mit demselben Argument aufrufen, wird dieselbe Sequenz neu gestartet.

Um es zu beweisen, wenn Sie so etwas Einfaches tun, sehen Sie dieselbe Zahl 100 Mal gedruckt:

#include <stdlib.h>
#include <stdio.h>
int main() {
    for(int i = 0; i != 100; ++i) {
        srand(0);
        printf("%d\n", rand());
    }
}

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

  1. Es scheint, dass jedes Mal rand() läuft, wird es einen neuen Samen für die nächste setzen rand().

  2. Wenn srand() mehrmals läuft, ist das Problem, wenn die beiden Läufe in einer Sekunde passieren (die time(NULL) ändert sich nicht), die nächste rand() wird die gleiche sein wie die rand() direkt nach dem vorherigen srand().

  • Der Hauptpunkt ist die Initialisierung mit srand() mehrere Male mit demselben Startwert führt zu identischen Werten, die von zurückgegeben werden rand().

    – König Drosselbart

    2. Dezember 2017 um 6:25 Uhr

1420280cookie-checksrand() — warum nur einmal aufrufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy