Die Frage ist viel zu weit gefasst für eine vollständige Antwort, aber lassen Sie mich ein paar interessante Punkte herausgreifen:
Warum "gleich wahrscheinlich"
Angenommen, Sie haben einen einfachen Zufallszahlengenerator, der die Zahlen 0, 1, ..., 10 mit jeweils gleicher Wahrscheinlichkeit generiert (stellen Sie sich dies als Klassiker vor rand()
). Jetzt möchten Sie eine Zufallszahl im Bereich 0, 1, 2 mit jeweils gleicher Wahrscheinlichkeit. Ihre Knie-Ruck-Reaktion wäre zu nehmen rand() % 3
. Aber warten Sie, die Reste 0 und 1 kommen häufiger vor als die restlichen 2, also ist das nicht korrekt!
Aus diesem Grund benötigen wir geeignete Verteilungen , die eine Quelle einheitlicher zufälliger Ganzzahlen verwenden und diese wie Uniform[0,2]
im Beispiel in unsere gewünschte Verteilung umwandeln. Überlassen Sie dies am besten einer guten Bibliothek!
Motoren
Das Herzstück aller Zufälligkeit ist also ein guter Pseudozufallszahlengenerator, der eine Folge von Zahlen erzeugt, die gleichmäßig über ein bestimmtes Intervall verteilt sind und idealerweise eine sehr lange Periode haben. Die Standardimplementierung von rand()
ist oft nicht die beste, und daher ist es gut, eine Wahl zu haben. Linear-kongruent und der Mersenne-Twister sind zwei gute Optionen (LG wird auch häufig von verwendet rand()
). Auch hier ist es gut, die Bibliothek damit umgehen zu lassen.
Wie es funktioniert
Einfach: Stellen Sie zuerst einen Motor auf und setzen Sie ihn ein. Der Startwert bestimmt vollständig die gesamte Folge von "Zufallszahlen". Verwenden Sie daher /dev/urandom
jedes Mal eine andere (z. B. entnommene ) und b) speichern Sie den Startwert, wenn Sie eine Folge von Zufallswahlen neu erstellen möchten.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Jetzt können wir Distributionen erstellen:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... und benutze die Engine, um Zufallszahlen zu erstellen!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Parallelität
Ein weiterer wichtiger Grund, <random>
den herkömmlichen zu bevorzugen, rand()
ist, dass es jetzt sehr klar und offensichtlich ist, wie die Generierung von Zufallszahlen threadsicher gemacht werden kann: Stellen Sie entweder jedem Thread eine eigene thread-lokale Engine zur Verfügung, die auf einem thread-lokalen Seed basiert, oder synchronisieren Sie den Zugriff zum Motorobjekt.
Sonstiges
- Ein interessanter Artikel über TR1 zufällig auf Codeguru.
- Wikipedia hat eine gute Zusammenfassung (danke, @Justin).
- Grundsätzlich sollte jeder Motor a eingeben
result_type
, was der richtige Integraltyp für das Saatgut ist. Ich glaube , ich hatte eine Buggy Umsetzung einmal die mich gezwungen , für den Samen zu zwingen , std::mt19937
um uint32_t
auf x64, schließlich dieses Problem behoben werden soll , und man kann sagen , MyRNG::result_type seed_val
und damit den Motor macht sehr leicht austauschbar.
rand
, sollten Sie einen kurzen Blick auf Wikipedia werfen, um einige grundlegende statistische und RNG-Konzepte zu finden. Andernfalls ist es sehr schwierig, Ihnen die Gründe<random>
und die Verwendung der verschiedenen Teile zu erklären .