Welche Zufallszahlen-Engines von <random> sollte man in der Praxis tatsächlich verwenden? std :: mt19937?

21

Angenommen, Sie möchten C ++ - Funktionen <random>in einem praktischen Programm verwenden (für eine Definition von "praktisch" - die Einschränkungen hier sind Teil dieser Frage). Sie haben ungefähr folgenden Code:

int main(int argc, char **argv) {
    int seed = get_user_provided_seed_value(argc, argv);
    if (seed == 0) seed = std::random_device()();
    ENGINE g(seed);  // TODO: proper seeding?
    go_on_and_use(g);
}

Meine Frage ist, für welchen Typ sollten Sie verwenden ENGINE?

  • Ich habe immer gesagt, std::mt19937weil es schnell zu tippen war und eine Namenserkennung hatte. Aber heutzutage scheinen alle zu sagen, dass der Mersenne Twister sehr schwer und cache-unfreundlich ist und nicht einmal alle statistischen Tests besteht, die andere durchführen.

  • Ich möchte sagen, std::default_random_engineweil es die offensichtliche "Standardeinstellung" ist. Aber ich weiß nicht, ob es von Plattform zu Plattform unterschiedlich ist. und ich weiß nicht, ob es statistisch etwas Gutes ist.

  • Da jeder in diesen Tagen auf einer 64-Bit - Plattform ist, sollten wir zumindest sein mit std::mt19937_64überstd::mt19937 ?

  • Ich würde gerne sagen pcg64oder xoroshiro128weil sie angesehen und leicht erscheinen, aber sie existieren nicht in<random> überhaupt nicht.

  • Ich weiß nichts über minstd_rand, minstd_rand0, ranlux24,knuth_b etc. - sicher müssen sie für etwas gut sein?

Offensichtlich gibt es hier einige konkurrierende Einschränkungen.

  • Stärke des Motors. (<random> hat keine kryptografisch starken PRNGs, aber dennoch sind einige der standardisierten "schwächer" als andere, oder?)

  • sizeof der Motor.

  • Geschwindigkeit seiner operator().

  • Einfache Aussaat. mt19937ist notorisch schwer richtig zu säen, weil es so viel Zustand zu initialisieren hat.

  • Portabilität zwischen Bibliotheksanbietern. Wenn ein Anbieter foo_engineandere Nummern produziert als ein anderer Anbieter foo_engine, ist dies für einige Anwendungen nicht gut. (Hoffentlich schließt dies nichts aus, außer vielleicht default_random_engine.)

Wenn Sie all diese Einschränkungen so gut wie möglich abwägen, was ist Ihrer Meinung nach die ultimative Antwort "Best Practice, innerhalb der Standardbibliothek zu bleiben"? Soll ich einfach weiter benutzen std::mt19937oder was?

Quuxpluson
quelle
2
Bis zu Ihrem letzten Punkt sind alle Standard-Engine-Adapter so spezifiziert, dass sie einen bestimmten Wert bei einem bestimmten aufeinanderfolgenden Aufruf des standardmäßig erstellten zurückgeben. Sie sollten daher portabel sein.
1201ProgramAlarm

Antworten:

15

Die C ++ - Referenz listet alle zufälligen Engines auf, die derzeit von C ++ bereitgestellt werden. Die Auswahl der Motoren lässt jedoch zu wünschen übrig (siehe z. B. meine Liste hochwertiger Zufallsgeneratoren) ). Zum Beispiel:

  • default_random_engine ist implementierungsdefiniert, daher ist nicht bekannt, ob die Engine statistische Fehler aufweist, die die Anwendung möglicherweise interessiert.
  • linear_congruential_engineimplementiert lineare Kongruenzgeneratoren. Sie haben jedoch tendenziell eine schlechte Qualität, es sei denn, der Modul ist prim und sehr groß (mindestens 64 Bit). Außerdem können sie nicht mehr Samen als ihren Modul zulassen.
  • minstd_rand0und minstd_randnur etwa 2 ^ 31 Samen zugeben. knuth_bWraps a minstd_rand0und mischt Bays-Durham davon.
  • mt19937 und mt19937_64 könnten viel mehr Seeds zulassen, wenn sie besser initialisiert würden (z. B. durch Initialisieren von a std::seed_seqmit mehreren Ausgaben von random_devicenicht nur einer), aber sie verwenden ungefähr 2500 Bytes Status.
  • ranlux24und ranlux48verwenden ungefähr 577 Bit Status, aber sie sind langsam (sie arbeiten, indem sie einige beibehalten und andere pseudozufällige Ausgaben verwerfen).

C ++ verfügt jedoch auch über zwei Engines, die eine andere Engine umschließen, um möglicherweise ihre Zufälligkeitseigenschaften zu verbessern:

  • discard_block_engine verwirft einige der Ausgaben einer bestimmten zufälligen Engine.
  • shuffle_order_engine implementiert ein Bays-Durham-Shuffle einer bestimmten zufälligen Engine.

Zum Beispiel ist es möglich, sagen wir, ein Bays-Durham shuffle zu haben mt19937, ranlux24oder eine benutzerdefinierte linear_congruential_enginemit shuffle_order_engine. Vielleicht ist der verpackte Motor von besserer Qualität als der ursprüngliche. Es ist jedoch schwierig, die statistische Qualität des neuen Motors ohne vorherzusagen testen .

In Erwartung solcher Tests scheint dies mt19937die derzeit praktischste Engine im C ++ - Standard zu sein. Mir ist jedoch mindestens ein Vorschlag bekannt, zukünftigen Versionen von C ++ eine weitere Zufallszahlen-Engine hinzuzufügen (siehe C ++ - Papier P2075 ).

Peter O.
quelle
1

Laut C ++ Referenz , default_random_engine:

Ist die Auswahl eines Generators durch die Bibliotheksimplementierung, der zumindest ein akzeptables Motorverhalten für eine relativ gelegentliche, unsachgemäße und / oder leichte Verwendung bietet .

Also für leichten Einsatz brauchen Sie sich keine Sorgen über alles, Samen zu sein default_random_enginemit Epoch Time (time(0))und das wäre fein genug;)

Farbod Ahmadian
quelle
Ich glaube, dass das Problem hier die Portabilität ist. Die Standardeinstellung ist möglicherweise eine Engine mit guter Leistung, die jedoch möglicherweise nicht auf einer anderen Plattform reproduzierbar ist.
bremen_matt
@bremen_matt Hmm ... Nun, warum müssen wir eine "Zufallszahl" reproduzieren?
Farbod Ahmadian
2
Testen. Zu Testzwecken benötigen Sie reproduzierbare Eingaben. Gleichzeitig möchten oder müssen diese Eingaben möglicherweise zufällig sein. Beispielsweise gehen die meisten Algorithmen für maschinelles Lernen davon aus, dass Parameter zufällig initialisiert werden. Ransac, CNNs, DNNs, ... viele Algorithmen erfordern zufällige Parameter.
bremen_matt