Wie kurz und bündig, tragbar und gründlich Saatgut der mt19937 PRNG?
Scheine ich zu sehen, viele Antworten, in denen jemand schlägt mit <random>
um Zufallszahlen zu generieren, in der Regel zusammen mit code wie diesem:
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);
In der Regel ersetzt eine Art von "unholy abomination" wie:
srand(time(NULL));
rand()%6;
Könnten wir kritisieren der alten Weise, mit dem argument, dass time(NULL)
bietet niedrige Entropie time(NULL)
ist vorhersehbar, und das Ergebnis ist nicht einheitlich.
Aber das ist alles richtig und einen neuen Weg: es muss nur ein glänzender Furnier.
rd()
gibt eine einzelneunsigned int
. Dieser ist mindestens 16 bit und vermutlich 32. Das ist nicht genug, um die Samen MT 19937 bits des Staates.- Mit
std::mt19937 gen(rd());gen()
(seeding mit 32 bits und einem Blick auf die erste Ausgabe) nicht geben einen guten Ausgang Verteilung. 7 und 13 kann nie die erste Ausgabe. Zwei Samen produzieren 0. Zwölf Samen produzieren 1226181350. (Link) std::random_device
werden kann, und manchmal ist, implementiert als einfache PRNG mit einer festen Samen. Es könnte daher produzieren die gleiche Sequenz auf jedem Lauf. (Link) Das ist noch schlimmer alstime(NULL)
.
Schlimmer noch, es ist sehr einfach zu kopieren und fügen Sie den vorangegangenen code-snippets, trotz der Probleme, die Sie enthalten. Einige Lösungen, die diese erfordern den Erwerb groessere Bibliotheken, die möglicherweise nicht geeignet für alle.
In diesem Licht, meine Frage ist Wie kann man kurz und bündig, tragbar und gründlich Saatgut der mt19937 PRNG in C++?
Angesichts der vorstehenden Probleme, eine gute Antwort:
- Muss voll Samen der mt19937/mt19937_64.
- Nicht allein auf
std::random_device
odertime(NULL)
als Quelle der Entropie. - Sollte sich nicht darauf verlassen, Boost oder anderen libaries.
- Sollte passen in eine kleine Anzahl von Linien, so dass es schön Aussehen würde, kopieren-einfügen in eine Antwort.
Gedanken
- Mein Aktueller Gedanke ist, dass Ausgänge von
std::random_device
können püriert bis (vielleicht über XOR) mittime(NULL)
Werte abgeleitet aus address space randomization, und eine hart codierte Konstante (die festgelegt werden konnte, bei der Verteilung) um einen best-effort-Schuss an Entropie. std::random_device::entropy()
nicht einen guten Anhaltspunkt geben, wasstd::random_device
könnte oder nicht.
Mein persönlicher Gedanke war, dass vielleicht die Werte konnten gezogen werden aus
std::random_device
, time(NULL)
und Funktion-Adressen, dann XORed zusammen ergeben eine Art " best-effort-Entropie-Quelle.Es wäre schön, wenn es die Funktion wie does_random_device_actually_work (), so könnte man zumindest ordnungsgemäß beeinträchtigen, oder produzieren Sie Warnungen oder Fehler für den Benutzer.
Die richtige Lösung ist nicht kurz, die kurze Lösung nicht richtig sein. Mein Ansatz verwende ich in meinem seed11 Bibliothek ist im Grunde zu implementieren
std::random_device
richtig auf den Plattformen Sie planen, führen Ihr Programm auf, und eine Hilfsfunktion, erzeugt einen gesetzten generator (seed11::make_seeded<std::mt19937>()
)Beiseite: deine zweite Kugel etwas neues hinzufügen. Es ist nicht verwunderlich, dass Sie einen gewissen Wert, erscheint 12-mal. Sie sollten erwarten, dass es nur über drei Werte, die erscheinen genau 12-mal, unter der Annahme, dass du 2^32 independent, einheitlich random Beispiele.
InformationsquelleAutor Richard | 2017-07-12
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich würde behaupten der größte Fehler mit
std::random_device
ist die, dass es erlaubt ist eine deterministische fallback, wenn keine CSPRNG verfügbar ist. Dies allein ist ein guter Grund, nicht zu Saatgut ein PRNG mitstd::random_device
, da der bytes erzeugt werden kann deterministisch. Es ist leider nicht eine API bereitstellen, um herauszufinden, Wann dies geschieht, oder zu verlangen, Versagen anstelle von low-Qualität von Zufallszahlen.Ist, es gibt keine komplett portable Lösung: allerdings, es ist ein anständiger, minimalistischen Ansatz. Sie können einen minimalen wrapper um eine CSPRNG (definiert als
sysrandom
unten), der Samen der PRNG.Windows
Können Sie sich auf
CryptGenRandom
ein CSPRNG. Sie können beispielsweise den folgenden code verwenden:Unix-Wie
Auf vielen Unix-artigen Systemen, die Sie verwenden sollten /dev/urandom, wenn möglich (obwohl dies nicht garantiert ist, gibt es auf POSIX-kompatiblen Systemen).
Anderen
Wenn keine CSPRNG verfügbar ist, können Sie wählen, auf die Sie sich verlassen
std::random_device
. Allerdings würde ich vermeiden, wenn möglich, da verschiedene Compiler (vor allem, MinGW) implementieren Sie es mit als PRNG (in der Tat produzieren die gleiche Reihenfolge jedes mal warnen, die Menschen, dass es nicht richtig random).Seeding
Nun, wir haben unsere Stücke mit minimalem Aufwand können wir erzeugen die gewünschten bits von random entropy Seeding unsere PRNG. Das Beispiel verwendet (offensichtlich unzureichend) 32-bits, um die Samen der PRNG, und Sie sollten diesen Wert erhöhen (das ist abhängig von Ihrer CSPRNG).
Vergleich Zum Boost
Sehen wir parallelen zu boost::random_device (eine wahre CSPRNG) nach einem kurzen Blick auf die source code. Boost verwendet
MS_DEF_PROV
auf Windows, die die Anbieter geben fürPROV_RSA_FULL
. Das einzige, was fehlt, wäre die überprüfung der kryptographischen Kontext, die getan werden kann, mitCRYPT_VERIFYCONTEXT
. Auf *Nix-Boost verwendet/dev/urandom
. DH, diese Lösung ist transportabel, gut getestet und einfach zu verwenden.Linux Spezialisierung
Wenn Sie zu opfern bereit sind lapidare für Sicherheit,
getrandom
ist eine ausgezeichnete Wahl auf Linux 3.17 und oben, und auf den letzten Solaris.getrandom
verhält sich identisch zu/dev/urandom
, außer es blockiert wird, wenn der kernel nicht initialisiert seine CSPRNG noch nach dem Booten. Der folgende Code erkennt, ob Linuxgetrandom
verfügbar ist, und wenn nicht fällt zurück auf/dev/urandom
.OpenBSD
Es ist eine Letzte Warnung: moderne OpenBSD nicht
/dev/urandom
. Sie verwenden sollten, getentropy statt.Andere Gedanken
Wenn Sie brauchen, kryptografisch sichere zufällige bytes, sollten Sie wahrscheinlich ersetzen die fstream sind mit POSIX ist Ungepuffert öffnen/Lesen/schließen. Dies ist, weil beide
basic_filebuf
undFILE
enthalten einen internen Puffer, die zugeteilt werden, der über eine standard-Zuweisung (und deshalb nicht ausgelöscht aus dem Gedächtnis).Dies kann leicht getan werden, indem Sie ändern
sysrandom
zu:Dank
Besonderen Dank an Ben Voigt für den Hinweis
FILE
verwendet gepuffert liest, und sollte daher nicht verwendet werden.Ich möchte auch meinen Dank an Peter Cordes für die Erwähnung
getrandom
ist, und OpenBSD ist der Mangel an/dev/urandom
.OP hier: Es wäre schön, wenn diese Antwort gezeigt, die seeding ein bisschen besser. So viel wie möglich, ich hoffe auf Antworten zu erstellen, copy-pastable code, der macht den job besser als das einfache Beispiel habe ich geschrieben in meiner Frage, ohne viel technischen Auslegung oder dachte seitens der Programmierer.
Ich dachte
/dev/random
wäre die bessere Wahl für das seeding ein RNG, aber anscheinend/dev/urandom
ist immer noch als sehr sicher, auch wenn/dev/random
blockieren würden, aufgrund des geringen verfügbaren Entropie, sourandom
ist die empfohlene Wahl für alles außer vielleicht one-time-pads. Siehe auch unix.stackexchange.com/questions/324209/.... Hüten Sie sich vor vorhersehbaren Samen vonurandom
sehr früh nach dem Start, wenn.Linux ist
getrandom(2)
- system nennen, ist wie das öffnen und Lesen/dev/urandom
, außer es blockiert werden, wenn der kernel die Zufälligkeit Quellen wurden noch nicht initialisiert, noch nicht. Ich denke, das erspart dir den early-boot low-quality-Zufälligkeit problem, ohne zu blockieren in anderen Fällen, wie/dev/random
tut.sicher, und das ist eine tolle option, wenn verfügbar. Jedoch funktioniert es nicht auf BSD oder anderen *Nixen, die etwas
/dev/urandom
im Allgemeinen funktioniert. Der Python-Mailingliste Diskussion über das ist etwas, was ich in der Regel abonnieren: bugs.python.org/issue27266InformationsquelleAutor Alexander Huszagh
In ein Gefühl, das kann nicht getan werden, tragbar. Das heißt, man kann sich vorstellen, eine gültige voll-deterministische Plattform mit C++ (sagen, ein simulator, welche Schritte die Maschine Uhr deterministisch, und mit "determinized" I/O"), in denen es keine Quelle von Zufälligkeit zu Saatgut ein PRNG.
Ich Schätze, diese Antwort, sondern auch das Gefühl, als ob ein Programm sollte auch einen angemessenen best-effort-Versuch.
Vereinbart, aber das Problem ist der C++ - standard-Autoren (oder zumindest versuchen Ihre darndest) für diese Arten von bizarren Situationen. Das ist, warum man diese Art von wischi-waschi-standard-Definitionen, von denen Sie möglicherweise erhalten, die anständige Ergebnisse, aber der compiler kann immer noch Standard-konform, auch wenn es die zurück gibt, etwas, das funktionell wertlos. -- So Ihre Einschränkungen ("kurz und kann nicht auf andere verlassen Bibliotheken") auszuschließen, keine Antwort, wie Sie effektiv brauchen eine Plattform-Plattform/compiler-compiler spezielle Gehäuse. (z.B. was-Boost so gut tut.)
was er erklärt, aber, dass Sie bekommen, was Sie in der standard - denn es gibt keinen portablen Weg, dies besser zu machen. Wenn Sie wollen, es besser zu machen (das ist ein edles Ziel), müssen Sie akzeptieren, einige der größeren oder geringeren Menge von Greuel 🙂
Manchmal muss man einfach akzeptieren, dass es möglich ist, eine standardkonforme C++ - Implementierung, die nicht nützlich. Da die Implementierungen benutzen die Menschen für alles, was zählt entwickelt, um nützlich zu sein, müssen Sie manchmal live mit Argumenten wie "jeder vernünftige Umsetzung wird mal etwas vernünftiges anfangen". Ich hätte gehofft, dass
std::random_device
wäre in dieser Kategorie, aber scheinbar ist es nicht, wenn einige Echtzeit-Implementierungen verwenden eine Feste-seed PRNG! Das geht weit über einpoklum argument.InformationsquelleAutor einpoklum
Können Sie eine
std::seed_seq
und füllen Sie mindestens die erfordert Zustand Größe für den generator mit Alexander Huszagh ' s Methode, die Entropie:Wenn es eine richtige Art und Weise zu füllen oder erstellen Sie eine SeedSequence von einem UniformRandomBitGenerator in der standard-Bibliothek, die mit
std::random_device
für das seeding richtig wäre doch viel einfacher.InformationsquelleAutor ratchet freak
Die Implementierung, die ich arbeite nutzt die
state_size
Eigenschaft desmt19937
PRNG zu entscheiden, wie viele Samen bei der Initialisierung:Ich denke, es gibt Raum für Verbesserung, denn
std::random_device::result_type
konnte, unterscheiden sich vonstd::mt19937::result_type
in Größe und Angebot, also das sollte wirklich berücksichtigt werden.Ein Hinweis zu std::random_device.
Entsprechend der
C++11(/14/17)
standard(s):Dies bedeutet, die Umsetzung kann nur generieren deterministische Werte, wenn es daran gehindert ist, die Erzeugung nicht-deterministische durch einige Einschränkungen.
Den
MinGW
compiler aufWindows
bekanntlich nicht nicht-deterministische Werte aus seinerstd::random_device
trotz Ihnen leicht, die vom Betriebssystem bereitgestellt. Also ich halte das ein bug und wahrscheinlich nicht ein gemeinsames auftreten, Implementierungen und Plattformen.std::random_device
, und ist deshalb anfällig für Probleme, die sich aus es.Ich glaube, ich erklärte Ihnen deutlich genug in der Frage. Gerne klären/diskutieren, obwohl.
Gibt es Reale Systeme, die eigentlich gar nicht implementieren einer angemessenen
std::random_device
? Ich weiß, der standard ermöglicht einePRNG
wie Rücken fallen, aber ich glaube, dass ist einfach, sich zu bedecken, wie es schwer ist zu fordern, dass jedes Gerät, das verwendetC++
hat einen nicht-deterministischen random source. Und wenn Sie das nicht tun, dann, was konnte Sie tun, um das überhaupt?Ich bin mir nicht so sicher. Meine Absicht ist es, meine "tragbare Lösung" in Abhängigkeit von das Gerät, denn wenn das Gerät unterstützt nicht-deterministische Generatoren, so sollte
std::random_device
. Ich glaube, das ist der Geist der Norm. Also ich habe gesucht und finden nurMinGW
zerbrochen ist in dieser Hinsicht. Niemand scheint die Meldung dieses problem mit irgendetwas, dass ich gefunden habe. Also, in meiner Bibliothek, ich habe einfach markiertMinGW
als nicht unterstützt. Wenn es ein größeres problem, dann würde ich wieder so denken. Ich weiß nur nicht sehen, den Beweis, dass gerade jetzt.Ich bin wirklich enttäuscht, dass MinGW ruiniert
std::random_device
für alle verfügbar zu machen in einer form, die nicht liefern die Plattform Zufälligkeit Fähigkeiten. Low-Qualität-Implementierungen, die Niederlage der Zweck der API existieren. Besser wäre es IMO, wenn Sie einfach nicht umsetzen, es überhaupt, bis Sie es zu arbeiten. (Oder besser, wenn die API eine Möglichkeit zur Anforderung fehlschlagen, wenn die hohe Qualität der Zufälligkeit war nicht verfügbar, also MinGW vermeiden könnte, wodurch Sicherheitsrisiken während noch die verschiedenen Samen für Spiele oder was auch immer.)InformationsquelleAutor Galik
Es ist nichts falsch mit Aussaat von mit der Zeit, vorausgesetzt, Sie brauchen es nicht, um sicher zu sein (und Sie nicht sagen, das war nötig). Die Erkenntnis ist, dass Sie verwenden können, hashing zu beheben, die nicht-Zufälligkeit. Ich habe festgestellt, dass dies funktioniert, angemessen, in allen Fällen, einschließlich Fällen, und insbesondere bei schweren Monte-Carlo-Simulationen.
Ein nettes feature von diesem Ansatz ist, dass er verallgemeinert auf die Initialisierung von anderen nicht-wirklich-zufällige Mengen von Samen. Zum Beispiel, wenn Sie wollen, dass jeder thread seinen eigenen RNG (für threadsafety), Sie kann nur initialisiert werden, basierend auf Hash-thread-ID.
Folgende ist eine SSCCE, destilliert aus meine codebase (für Einfachheit, einige OO-support-Strukturen erstellte):
Empirisch ist es sehr besser. Der Grund dafür ist, dass der hash ist nicht zusammenhängend, und umfasst ein viel breiteres Spektrum an Werten (versuchen Sie es selbst: Samen mit
1
und2
und beobachten Sie, dass die Reihenfolge der Schwimmer von Ihnen erzeugten dauert eine Weile, bis Sie wirklich auseinander).Ich sehe nicht ein, warum, was zählt. Wir sind nur auf einem einzigen Samen zu einer Zeit. Der Raum der möglichen Werte für die Samen (die Entropie des seed) ist die gleiche, so oder so -- hashing nicht erhöht Entropie. Vielleicht könnten Sie Bearbeiten die Frage, zu erklären, warum hashing ist besser?
InformationsquelleAutor imallett
Hier meine eigenen stab auf die Frage:
Die Idee hier ist die Verwendung von XOR zu kombinieren viele mögliche Quellen von Entropie (schnell mal langsam Zeit,
std::random-device
statische variable Standorte, heap Standorte, Funktion, Standorte, Standorten, Programm-spezifische Werte), um ein best-effort-Versuch, die Initialisierung des mt19937. Solange mindestens einmal Quelle ist "gut", das Ergebnis wird sein, dass zumindest die "guten".Diese Antwort ist nicht so kurz, wie es wäre vorzuziehen, und enthalten ein oder mehrere Fehler der Logik. Also ich überlege mir, es ein work in progress. Bitte Kommentar, wenn Sie Anregungen haben.
Ich würde vermuten
&i ^ &myseed
sollten erheblich weniger Entropie als entweder eine allein, da beide Objekte mit statischer Speicherdauer in der gleichen übersetzungseinheit und daher wahrscheinlich eher in der Nähe zusammen. Und Sie scheinen nicht, um tatsächlich mit dem speziellen Wert, der von der Initialisierung desmyseed
?Konvertieren dealocated Zeiger auf ints ist Undefiniertes Verhalten; tun Sie es, während es noch existiert.
^
ist eine schreckliche hash-combiner; wenn zwei Werte, die beide sehr viel Entropie, aber wenig im Vergleich zu jedem anderen, es entfernt es.+
ist in der Regel besser (als x+x nur Verbrennungen 1 bit Entropie von x, während x^x verbrennt Sie alle). Funktion wird nicht in die thead-safe vermute ich (rd()
)Achja und von
+
ich meine auf unsigned (+
auf unterzeichnet ist UB-bait). Während diese sind etwas lächerlich UB Fällen haben Sie sagen, portable. Auch erwägen, die Adresse einer Funktion als integral-Wert, falls möglich (unsicher, wenn es ist?)Selbst auf einem full-Stärke-PC, obwohl die Zuweisungen erhalten möglicherweise unterschiedlichen Standorten (je nach Zustand der Maschine externen, für den Prozess), die Zeiger sind abstrahiert von der process virtual address space und wahrscheinlich sehr wiederholbare, insbesondere ist ASLR ist nicht in Kraft.
InformationsquelleAutor Richard
Eine bestimmte Plattform möglicherweise eine Quelle der Entropie, wie
/dev/random
. Nanosekunden, die seit der Epoche mitstd::chrono::high_resolution_clock::now()
ist wahrscheinlich der beste Samen in der Standard-Bibliothek.Ich zuvor verwendet haben, so etwas wie
(uint64_t)( time(NULL)*CLOCKS_PER_SEC + clock() )
um mehr bits der Entropie für Anwendungen, die nicht Sicherheits-kritische./dev/urandom
vor allem in einem Fall wie diesem./dev/random
Blöcke, und oft, ohne gute Gründe dafür ([insert lange Erklärung darüber, wie viele verschiedene Betriebssysteme Schätzung der Zufälligkeit der bytes produziert von /dev/random]).Wahr, aber ich habe die code auf Systemen, auf denen
/dev/urandom
gar nicht gibt, und die alternative zu blockieren war Determinismus. Eine box haben könnte/dev/hwrng
oder/dev/hw_random
als auch, was sollte noch besser werden.2uo.de/myths-about-urandom
Okay, sagte ich, "wie
/dev/random
", und das scheint zu haben, löste einen Heiligen Krieg über/dev/random
versus/dev/urandom
auf Linux, dass ich nicht beabsichtige, wenn ich gab das Beispiel..InformationsquelleAutor Davislor