Empfohlene Methode zum Initialisieren von Sand?

Lesezeit: 8 Minuten

Empfohlene Methode zum Initialisieren von Sand
Gary Richardson

Ich brauche einen “guten” Weg, um den Pseudozufallszahlengenerator in C++ zu initialisieren. Ich habe gefunden ein Artikel das besagt:

Um zufallsähnliche Zahlen zu generieren, wird srand normalerweise mit einem bestimmten Wert initialisiert, wie z. B. denen, die mit der Ausführungszeit zusammenhängen. Beispielsweise ist der von der Funktion time (in Header ctime deklariert) zurückgegebene Wert jede Sekunde anders, was für die meisten Randoming-Anforderungen ausreichend ausgeprägt ist.

Unixtime ist für meine Anwendung nicht ausgeprägt genug. Wie kann man das besser initialisieren? Bonuspunkte, wenn es portabel ist, aber der Code wird hauptsächlich auf Linux-Hosts ausgeführt.

Ich dachte daran, etwas PID/Unixtime-Mathematik zu machen, um ein Int zu bekommen, oder möglicherweise Daten daraus zu lesen /dev/urandom.

Danke!

BEARBEITEN

Ja, ich starte meine Anwendung tatsächlich mehrmals pro Sekunde und bin auf Kollisionen gestoßen.

  • Warum ist time() nicht genug? Starten Sie die Anwendung mehrmals pro Sekunde? Beachten Sie, dass Sie srand() nur EINMAL in einer Anwendung aufrufen sollten.

    – Martin York

    27. November 2008 um 5:33 Uhr

  • Wenn time() – oder gettimeofday() – nicht ausreicht, dann ist rand() wahrscheinlich nicht gut genug für Sie. Dieser PRNG muss überhaupt nicht sehr gut sein. Kryptografische Zufälligkeit ist schwierig – verwenden Sie eine kryptografische Bibliothek.

    – Jonathan Leffler

    27. November 2008 um 7:19 Uhr

  • Tatsächlich hält zumindest MS CRT den Seed in TLS, also müssten Sie in diesem Fall tatsächlich srand() EINMAL für jeden Thread initialisieren. Ich bin mir jedoch nicht sicher, wie GCC den Seed speichert.

    – Andreas Magnusson

    27. November 2008 um 9:32 Uhr

  • Weitere Informationen zum Initialisieren von srand() mit Mikrosekunden: guyrutenberg.com/2007/09/03/seeding-srand

    – nichts-besonderes-hier

    7. April 2013 um 17:42 Uhr

  • Verwandt: Wie ich Hacker News gehackt habe. HN verwendete die Zeit in Millisekunden als Startwert und verwendete dann den RNG, um Anmelde-Cookies zu generieren. Der Angreifer könnte den Seed wiederherstellen, indem er sich bei seinem Konto anmeldet, ein Cookie erhält, dann mögliche Seeds aufzählt und jeden mit dem Cookie vergleicht, bis eine Übereinstimmung gefunden wird. Mit dem richtigen Startwert könnten sie dann Cookies anderer Benutzer vorhersagen und als diese Benutzer Aktionen auf der Website ausführen.

    – Bain

    15. November 2019 um 11:01 Uhr

1647057611 755 Empfohlene Methode zum Initialisieren von Sand
Jonathan Wright

Dies ist, was ich für kleine Befehlszeilenprogramme verwendet habe, die häufig ausgeführt werden können (mehrmals pro Sekunde):

unsigned long seed = mix(clock(), time(NULL), getpid());

Wo Mischung ist:

// Robert Jenkins' 96 bit Mix Function
unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
    a=a-b;  a=a-c;  a=a^(c >> 13);
    b=b-c;  b=b-a;  b=b^(a << 8);
    c=c-a;  c=c-b;  c=c^(b >> 13);
    a=a-b;  a=a-c;  a=a^(c >> 12);
    b=b-c;  b=b-a;  b=b^(a << 16);
    c=c-a;  c=c-b;  c=c^(b >> 5);
    a=a-b;  a=a-c;  a=a^(c >> 3);
    b=b-c;  b=b-a;  b=b^(a << 10);
    c=c-a;  c=c-b;  c=c^(b >> 15);
    return c;
}

  • Gute Idee mit der PID. Der Link zur Codequelle ist veraltet, er ist immer noch da auf Archiv – Die Quelle dieses spezifischen Codes mit dem Namen “Robert Jenkins’ 96-Bit-Mix-Funktion” in diesem Text ist tatsächlich auch verlinkt: burtleburtle.net/bob/hash/doobs.html

    – Tobias Kienzler

    9. September 2013 um 7:18 Uhr

Empfohlene Methode zum Initialisieren von Sand
Martin York

Die beste Antwort ist zu <random>. Wenn Sie eine Pre-C++11-Version verwenden, können Sie sich alternativ die Boost-Zufallszahlen ansehen.

Aber wenn wir reden rand() und srand()

Die Beste Der einfachste Weg ist nur zu verwenden time():

int main()
{
    srand(time(nullptr));

    ...
}

Stellen Sie sicher, dass Sie dies zu Beginn Ihres Programms tun und nicht bei jedem Anruf rand()!


Randnotiz:

HINWEIS: Es gibt eine Diskussion in den Kommentaren unten darüber, dass dies unsicher ist (was wahr ist, aber letztendlich nicht relevant ist (weiterlesen)). Eine Alternative ist also das Seeden vom Zufallsgerät /dev/random (oder einen anderen sicheren echten (er) Zufallszahlengenerator). ABER: Lassen Sie sich davon nicht in falscher Sicherheit wiegen. Das ist rand() wir benutzen. Selbst wenn Sie es mit einem brillant generierten Seed versehen, ist es immer noch vorhersehbar (wenn Sie einen Wert haben, können Sie die vollständige Folge der nächsten Werte vorhersagen). Dies ist nur für die Generierung sinnvoll "pseudo" zufällige Werte.

Wenn Sie “sicher” wollen, sollten Sie wahrscheinlich verwenden <random> (Obwohl ich auf einer Website mit Sicherheitsinformationen noch etwas mehr lesen würde). Sehen Sie sich die folgende Antwort als Ausgangspunkt an: https://stackoverflow.com/a/29190957/14065 für eine bessere Antwort.

Sekundäre Anmerkung: Die Verwendung eines zufälligen Geräts löst die Probleme mit dem Starten mehrerer Kopien pro Sekunde tatsächlich besser als mein ursprünglicher Vorschlag unten (nur nicht das Sicherheitsproblem).


Zurück zur ursprünglichen Geschichte:

Bei jedem Start gibt time() einen eindeutigen Wert zurück (es sei denn, Sie starten die Anwendung mehrmals pro Sekunde). In 32-Bit-Systemen wird es nur etwa alle 60 Jahre wiederholt.

Ich weiß, Sie denken nicht, dass die Zeit einzigartig genug ist, aber ich kann das kaum glauben. Aber es ist bekannt, dass ich falsch liege.

Wenn Sie viele Kopien Ihrer Anwendung gleichzeitig starten, können Sie einen Timer mit einer feineren Auflösung verwenden. Aber dann riskieren Sie eine kürzere Zeitspanne, bevor sich der Wert wiederholt.

OK, also wenn Sie wirklich glauben, dass Sie mehrere Anwendungen pro Sekunde starten.
Verwenden Sie dann eine feinere Körnung auf dem Timer.

 int main()
 {
     struct timeval time; 
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     // Assuming you did not need quite that accuracy
     // Also do not assume the system clock has that accuracy.
     srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

     // The trouble here is that the seed will repeat every
     // 24 days or so.

     // If you use 100 (rather than 1000) the seed repeats every 248 days.

     // Do not make the MISTAKE of using just the tv_usec
     // This will mean your seed repeats every second.
 }

  • Eigentlich starte ich mehrere Instanzen meiner App pro Sekunde 🙂

    – Gary Richardson

    27. November 2008 um 15:26 Uhr

  • Ich habe irgendwo eine Lösung gesehen, die mit timeval/gettimeofday funktioniert…. ah, ja, meine eigene!! Sie haben kritisiert, dass es SEHR SCHLECHT ist, obwohl die Chancen 1/1000000 stehen, dass es sich wiederholen wird (mit den gleichen Annahmen, die Sie gemacht haben). Und deine ist 1/60000. Es ist also meine Lösung, nur noch schlimmer, und sie wird als Ihre präsentiert.

    – Benutzer39307

    28. November 2008 um 0:44 Uhr

  • 1) Gehen Sie nicht davon aus, dass die Taktauflösung 1/1000000 beträgt. 2) Meine Wahrscheinlichkeit ist 1/2147483648 (beachten Sie die tv_sec). 3) Seed-Wiederholungen liegen im Abstand von 24 Tagen (Interaktion zwischen Apps ist ebenfalls wichtig).

    – Martin York

    28. November 2008 um 8:37 Uhr

  • “Seed-Wiederholungen liegen im Abstand von 24 Tagen” Sie haben 1000 Kollisionen in einer Sekunde.

    – Benutzer39307

    28. November 2008 um 11:19 Uhr

  • Teilen tv_usec um 1000 wirft die gesamte Entropie weg. Millisekunden sind leicht vorhersehbar, und tatsächlich ist es einfach, ein Programm innerhalb von weniger als einer Millisekunde zweimal auszuführen …

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    10. Mai 2011 um 5:10 Uhr

1647057612 803 Empfohlene Methode zum Initialisieren von Sand
Evan Teran

Wenn Sie einen besseren Zufallszahlengenerator benötigen, verwenden Sie nicht die libc rand. Verwenden Sie stattdessen einfach so etwas wie /dev/random oder /dev/urandom direkt (einlesen int direkt daraus oder so).

Der einzige wirkliche Vorteil von libc rand ist, dass er bei gegebenem Seed vorhersehbar ist, was beim Debuggen hilft.

  • Unter Windows können Sie rand_s verwenden.

    – Pawel Hajdan

    27. November 2008 um 18:36 Uhr

  • Sie sollten nicht wiederholt ab lesen /dev/urandom für Zufallszahlen, nur für den Seed. Es ist viel langsamer und erschöpft den Entropiepool des Systems.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    10. Mai 2011 um 5:20 Uhr

  • Nur um es hier nicht herauszupicken: Jeder PRNG ist vorhersehbar, wenn man den gleichen Startwert hat. Das ist, was die Pseudo bedeutet in PRNG.

    – Mischmasch

    30. Oktober 2015 um 16:03 Uhr


  • @vanneto, wenn du herausfindest, wie man sät /dev/random Um es vollständig vorhersehbar zu machen, lassen Sie es mich wissen :-P. Es ist auch ein PRNG.

    – Evan Teran

    30. Oktober 2015 um 19:41 Uhr

  • std::random_device ist ein tragbarer Wrapper vorbei /dev/urandom in glibc: stackoverflow.com/a/13004555/895245

    – Ciro Santilli Путлер Капут 六四事

    14. Januar 2017 um 16:28 Uhr


Unter Windows:

srand(GetTickCount());

liefert einen besseren Samen als time() seit seiner in Millisekunden.

1647057613 679 Empfohlene Methode zum Initialisieren von Sand
Namen53

C++11 random_device

Wenn Sie eine angemessene Qualität benötigen, sollten Sie rand() überhaupt nicht verwenden. Sie sollten die verwenden <random> Bücherei. Es bietet viele großartige Funktionen, wie z. B. eine Vielzahl von Engines für unterschiedliche Qualitäts-/Größen-/Leistungskompromisse, Wiedereintritt und vordefinierte Verteilungen, damit Sie am Ende nichts falsch machen. Abhängig von Ihrer Implementierung kann es sogar einen einfachen Zugriff auf nicht deterministische Zufallsdaten (z. B. /dev/random) bieten.

#include <random>
#include <iostream>

int main() {
    std::random_device r;
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
    std::mt19937 eng(seed);

    std::uniform_int_distribution<> dist{1,100};

    for (int i=0; i<50; ++i)
        std::cout << dist(eng) << '\n';
}

eng ist eine Quelle der Zufälligkeit, hier eine eingebaute Implementierung von Mersenne Twister. Wir setzen es mit random_device, das in jeder anständigen Implementierung ein nicht deterministisches RNG sein wird, und seed_seq, um mehr als 32-Bit-Zufallsdaten zu kombinieren. Zum Beispiel greift random_device in libc++ standardmäßig auf /dev/urandom zu (obwohl Sie ihm stattdessen eine andere Datei für den Zugriff geben können).

Als Nächstes erstellen wir eine Verteilung, sodass bei gegebener Zufälligkeitsquelle wiederholte Aufrufe der Verteilung eine gleichmäßige Verteilung von Ints von 1 bis 100 erzeugen. Dann fahren wir damit fort, die Verteilung wiederholt zu verwenden und die Ergebnisse auszugeben.

1647057613 201 Empfohlene Methode zum Initialisieren von Sand
Benutzer39307

Der beste Weg ist, einen anderen Pseudozufallszahlengenerator zu verwenden. Mersenne Twister (und Wichmann-Hill) ist meine Empfehlung.

http://en.wikipedia.org/wiki/Mersenne_twister

1647057614 597 Empfohlene Methode zum Initialisieren von Sand
FL4SOF

Ich schlage vor, Sie sehen die Datei unix_random.c im Mozilla-Code. (schätze, es ist mozilla/security/freebl/ …) es sollte in der freebl-Bibliothek sein.

dort verwendet es Systemaufrufinformationen (wie pwd, netstat ….), um Rauschen für die Zufallszahl zu erzeugen; es ist so geschrieben, dass es die meisten Plattformen unterstützt (was mir einen Bonuspunkt einbringen kann: D).

992620cookie-checkEmpfohlene Methode zum Initialisieren von Sand?

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

Privacy policy