Woraus besteht eine zufällige C ++ 11-Distribution?

8

Ich versuche, die folgende Klasse zu implementieren:

%Vor%

Ich möchte, dass die Klasse in einer Multithread-Umgebung arbeitet. Jeder Thread hat seine eigene Engine Objektinstanz und übergibt die Engine an Objekte jeder Klasse, die randomisiert ist.

Um Zufallszahlen einheitlich in C ++ 11 zu generieren, müsste die Implementierung von generateUniformRandomNumber in etwa so aussehen:

%Vor%

Das Problem ist, dass ich C ++ 11-Verteilungen nicht verstehe. Ich weiß, dass C ++ 11-Zufallszahlen-Engines sehr große Objekte (wenige Kilobyte) sein können, aber was ist mit Distributionen? Zuerst dachte ich, dass Verteilungen nur einfache Funktoren sind, wobei operator() eine pure const -Funktion ist, aber es scheint, dass es weder pure noch const ist. Entsprechend der Referenz verfügt jede Verteilungsinstanz über eine reset() -Memberfunktion. Das heißt, es hat einen potentiell großen internen Zustand oder vielleicht einen Cache.

Meine Frage ist:

  1. Haben Verteilungen einen internen Zustand? Wenn ja warum? Sagt der Standard etwas über die Größe dieses Zustandes?

  2. Ist es eine gute Idee, eine Implementierung wie ich zu machen? Gibt es einen besseren Weg?

Martin Drozdik 15.04.2013, 14:15
quelle

4 Antworten

2

Suchen Sie in den Dokumenten nach der RandomNumberDistribution-Vorlagenrichtlinie ...

reset() :

  

Setzt den internen Status des Verteilungsobjekts zurück. Nach einem Anruf an   Diese Funktion ist der nächste Aufruf von operator () für das Verteilungsobjekt   ist nicht von vorherigen Aufrufen von operator () abhängig.

Dies bedeutet, dass Aufrufe von operator() den Status ändern können, der nachfolgende Aufrufe von operator() bewirkt. Das ist der Grund, warum reset() existiert und warum operator() nicht const ist.

uniform_real_distribution sollte ein kleiner einfacher Funktor sein, wie du gesagt hast. Es würde wahrscheinlich nur die 2 Real s halten, mit denen es gebaut wurde und sonst nichts. Und reset() sollte dafür nichts tun.

    
David 15.04.2013, 14:35
quelle
6

Eine Verteilung kann sehr gut und gewöhnlich einen Zustand haben. Der Standard gibt hier keine Einschränkungen. Ich kann mir einige Gründe vorstellen, warum dieser Status verwendet werden könnte:

  1. Eine Zufallszahlenverteilung weist sehr oft einige Parameter auf, um sie zu konfigurieren. Zum Beispiel hat eine Normalverteilung Mittelwert- und Varianzparameter. Diese sind Teil seines Staates, weil sie zwischen den Anrufungen festgehalten werden müssen.

  2. Die Verteilung wird in Form einer anderen Verteilung implementiert. Man kann sich das wirklich als komplexeren Parameter vorstellen. Zum Beispiel hält meine Beta-Verteilung Implementierung zwei Gamma-Distributionen fest, die jeweils ihre eigene Konfiguration haben.

  3. Die Verteilung könnte sich im Laufe der Zeit ändern. Es gibt nichts zu sagen, dass wiederholte Aufrufe einer Verteilung unabhängig sein müssen. Hier kommt die reset -Memberfunktion ins Spiel. Die meisten Distributionen haben unabhängige Aufrufe an operator() und die reset -Funktion tut also tatsächlich nichts (ihre Definition ist leer). Wenn Ihre Aufrufe jedoch abhängig sind, sollte reset die Verteilung in einen Zustand zurückversetzen, in dem der nächste Aufruf unabhängig ist.

Ihre Implementierung scheint in Ordnung zu sein. A uniform_real_distribution<double> ist sehr unwahrscheinlich, dass die Parameter, mit denen Sie es konstruieren, viel mehr angeben.

    
Joseph Mansfield 15.04.2013 14:36
quelle
2

Ja, Verteilungen können interne Zustände haben. Sie werden normalerweise klein sein, aber wenn Sie über die Größe besorgt sind, überprüfen Sie es.

    
Pete Becker 15.04.2013 14:51
quelle
2

Die Angabe für reset() lautet:

  

Nachfolgende Verwendungen von d hängen nicht von Werten ab, die von einer Engine vor dem Zurücksetzen erzeugt wurden.

Das bedeutet, dass normalerweise Aufrufe von operator() von Werten abhängen können, die von Engines erzeugt werden, die beim vorherigen Aufruf von operator() verwendet wurden. Das heißt, eine Distribution kann zwischengespeichert werden oder auf andere Weise von früheren Engine-Ergebnissen abhängig sein.

Zum Beispiel benötigt eine Bernoulli-Distribution möglicherweise nur ein Bit, um ein Ergebnis zu erzeugen, während eine bestimmte Engine 32 Bits gleichzeitig bereitstellt. Daher kann die Verteilung die 32 Bits zwischenspeichern und keine Engine erneut aufrufen, bis 32 Werte generiert wurden.

Ein weiteres Beispiel ist eine Verteilung (ich vergesse, welche), wo der gemeinsame Algorithmus natürlich zwei Werte gleichzeitig erzeugt, so dass die Verteilung den zweiten Wert für den nächsten Aufruf speichern könnte.

Also ja, Distributionen können einen internen Status haben. Der Standard stellt keine Anforderungen an die Größe dieses Status.

Wenn Sie fragen, ob es in Ordnung wäre, eine Verteilung zwischen Threads zu teilen, dann ist das keine gute Idee. Zum einen ist dies ein Datenrennen und führt zu undefiniertem Verhalten, es sei denn, Sie fügen entweder eine Synchronisation hinzu oder machen die Distribution const (was, selbst wenn Sie dies mit Ihrer Implementierung der Standardbibliothek tun können, nicht übertragbar ist). Zweitens gibt der Standard nur dann Garantien, wenn Sie die Distributionen und Engines in einer bestimmten Weise verwenden und eine Aufteilung auf mehrere Engines nicht gemeinsam nutzen. Es ist unwahrscheinlich, dass das Teilen einer Distribution tatsächlich schlechte Daten produziert, aber IMO ist es immer noch eine gute Idee, das nicht zu tun. Stattdessen behält jeder Thread seine eigene Engine und seine eigene Distribution.

    
bames53 15.04.2013 15:48
quelle

Tags und Links