Wie man dieselbe Zufallszahlenfolge über mehrere Arten von Compilern und Kerneln mit generiert ?

Lesezeit: 5 Minuten

Benutzer-Avatar
DanielTuzes

Das Problem

Ich muss dieselbe (Pseudo-)Zufallszahlenfolge auf verschiedenen Maschinen und Compilern erzeugen. Wenn ich denselben Kernel verwende, scheint die Implementierung von Mersenne Twister (MT) in g++ gut zu funktionieren: Unabhängig davon, ob ich mein Programm auf einer neueren Maschine mit g++ 4.9 oder 4.7 kompiliere, erhalte ich dieselben Zufallszahlen. Aber ich bekomme andere, wenn ich einen älteren Kernel verwende oder wenn ich zum Compiler von Visual Studio wechsle. Das ist ok, denn dafür gibt es keine Garantie mersenne_twister_engine::seed setzt den internen Zustand über verschiedene Compiler hinweg auf den gleichen.

Was ich schon probiert habe

Ich habe das bei der Bewerbung gelernt operator<< auf dem Generator erzeugt ein eindeutiges Ergebnis, das verwendet werden kann, um die Generatoren auf anderen Maschinen mit einzustellen operator>>aber im Falle von mt19937, scheint es nicht zu funktionieren. Um es deutlich zu machen, auf einem Computer hatte die KI den Code

mt19937 generator1A;
uniform_int_distribution<int> distribution(0, 1000);

cout << "Generating random numbers with seed 1000" << endl;

generator1A.seed(1000);
generator1A(); //to advance the state by one so operator>> will give a longer output; this is not necessary indeed
ofstream test("testseed1000.txt");
test << generator1A << endl;

for (int i = 0; i < 10; ++i)
    cout << distribution(generator1A) << endl;

Und es produziert 252, 590, 893, … und eine lange Datei. Ich übertrage die Datei auf die andere Maschine B und führe den folgenden Code aus:

mt19937 generator1B, generator2B;
uniform_int_distribution<int> distribution(0, 1000);

cout << "Generating random numbers with seed 1000, and with operator>>" << endl;
generator2B.seed(1000);
generator2B(); // to advance the state by one here as well

ifstream test("testseed1000.txt");

test >> generator1B;
cout << "************************" << endl;
cout << generator1B << endl;
cout << "************************" << endl;
cout << "With seed\twith operator>>" << endl;

for (int i = 0; i < 10; ++i)
    cout << distribution(generator2B) << "\t" << distribution(generator1B) << endl;

Und es produziert

654     205
205     115
115     610

Die Frage

Können Sie Ratschläge geben, wie Sie dieselben (Pseudo-)Zufallszahlen mit mindestens VC++ unter Windows und g++ unter Debian und Ubuntu generieren können? Ich möchte std verwenden, wenn es möglich ist, und ich möchte nicht meine eigene MT-Engine implementieren.

Anmerkungen:

  • Millionen von Zufallszahlen zu erstellen und dann einzulesen, ist keine Lösung
  • Ich muss MSVS für die Codeentwicklung und Unix-Server für die Simulation verwenden
  • andere als MT-Motoren sind ebenfalls willkommen, aber ich bevorzuge MT

  • Ehrlich gesagt macht es mich ziemlich glücklich zu hören, dass Zufallszahlen nicht in der gleichen Reihenfolge auf verschiedenen Maschinen mit dem gleichen Seed generiert werden.

    – AndyG

    19. Februar 2015 um 13:43 Uhr

  • @AndyG: Das sollte es nicht. Der springende Punkt bei einem Seed ist es, eine reproduzierbare Sequenz zu erhalten.

    – Mike Seymour

    19. Februar 2015 um 13:44 Uhr

  • @MikeSeymour: Punkt genommen. Ich wäre jedoch nicht überrascht, wenn die Maschinen, an denen OP arbeitet, eine Mischung aus 32- und 64-Bit sind, was IIRC auf PRNG auswirken kann.

    – AndyG

    19. Februar 2015 um 13:49 Uhr

  • Kleiner Punkt: Sie haben vergessen zu treten generator2B einmal nach der Erstellung. (oder du bist getreten generator1A bei Unfall)

    Benutzer1084944

    19. Februar 2015 um 14:11 Uhr

  • Wenn Sie wirklich reproduzierbar und portabel sein müssen, verwenden Sie kein PRNG aus der Standardbibliothek – für sich selbst, oder verwenden Sie eine Bibliothek eines Drittanbieters mit Quellcode, wie meine eigene ojrandlib.

    – Lee Daniel Crocker

    19. Februar 2015 um 19:26 Uhr

Um eine Variable aus dem Problem zu entfernen, versuchen Sie, sich die Ausgaben Ihrer Zufallszahlenmaschine anzusehen, anstatt sie in eine Verteilung einzuspeisen.

Ich habe mir gerade ein paar Dokumentationsseiten für angeschaut uniform_int_distribution, und es gibt keine Aussage darüber, wie die Ausgänge des einheitlichen Zufallszahlengenerators verwendet werden. Ich bin mir nicht sicher, was die Norm sagt.

Ich sage dir voraus sind Sie erhalten die gleichen Ausgaben von Ihrem Mersenne-Twister, aber Sie speisen diese Ausgaben in zwei unäquivalente Implementierungen von ein uniform_int_distribution.

Wenn dies zutrifft, müssen Sie, um dieselbe Zufallszahlenfolge zu erhalten, eine externe Implementierung der Verteilungsfunktionen verwenden, um sicherzustellen, dass Sie auf allen Systemen dieselben Ergebnisse erhalten.

  • Du hast recht. Insbesondere ist es nicht erforderlich, den internen Zustand zu verbessern, wie ich es getan habe, aber das Starten mit der gleichen Startnummer führt einfach zum gleichen Ergebnis. Aber jetzt muss ich die Distributionen implementieren? Nun, es ist immer noch einfacher, als ein PRNG zu implementieren 🙂

    – DanielTuzes

    19. Februar 2015 um 15:02 Uhr

Benutzer-Avatar
TC

Der Standard verlangt, dass die Engines reproduzierbare Zahlen über Implementierungen hinweg erzeugen, indem der Wert angegeben wird, den eine standardmäßig konstruierte Engine beim 10000. Aufruf erzeugen muss (er spezifiziert auch die tatsächlichen Übergangs- und Generierungsalgorithmen für die Engines). Zum Beispiel z mt19937 die Norm legt fest, dass ([rand.predef]/p3):

typedef mersenne_twister_engine<uint_fast32_t,
       32,624,397,31,0x9908b0df,11,0xffffffff,7,0x9d2c5680,15,0xefc60000,18,1812433253>
       mt19937;

Erforderliches Verhalten: Die 10000th fortlaufender Aufruf eines standardmäßig konstruierten Objekts des Typs mt19937 soll den Wert 4123659995 ergeben.

Für Distributionen gibt es keine solche Anforderung; Stattdessen sagt der Standard, dass ([rand.dist.general]/p3):

Die Algorithmen zum Erzeugen jeder der angegebenen Verteilungen sind implementierungsdefiniert.

Mit anderen Worten, Implementierungen können unterschiedliche Algorithmen für die Verteilungen verwenden, müssen aber die von ihnen verwendeten Algorithmen dokumentieren. MSVC und libstdc++ verwenden vermutlich unterschiedliche Algorithmen.

Sie können die Verwendung einer externen Implementierung in Betracht ziehen, wie z. B. die Verteilungsklassen von Boost.Random, wenn Sie vollständige Portabilität wünschen.

1012620cookie-checkWie man dieselbe Zufallszahlenfolge über mehrere Arten von Compilern und Kerneln mit generiert ?

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

Privacy policy