70 Stimmen

Wie erzeugt man in C++ einen zufälligen Double, der gleichmäßig zwischen 0 und 1 verteilt ist?

Wie erzeugt man in C++ einen zufälligen Double, der gleichmäßig zwischen 0 und 1 verteilt ist?

Natürlich fallen mir einige Antworten ein, aber ich würde gerne wissen, was die gängige Praxis ist, die man hat:

  • Gute Einhaltung der Normen
  • Gute Zufälligkeit
  • Gute Geschwindigkeit

(Geschwindigkeit ist für meine Anwendung wichtiger als Zufälligkeit).

Herzlichen Dank!

PS: Falls das eine Rolle spielt, meine Zielplattformen sind Linux und Windows.

61voto

Elemental Punkte 7222

Eine Lösung der alten Schule wie:

double X=((double)rand()/(double)RAND_MAX);

Sollte alle Ihre Kriterien erfüllen (tragbar, Standard und schnell). Offensichtlich muss die erzeugte Zufallszahl gekeimt werden, das Standardverfahren ist etwa so:

srand((unsigned)time(NULL));

45voto

Shafik Yaghmour Punkte 147749

In C++11 und C++14 haben wir viel bessere Möglichkeiten mit der zufällige Kopfzeile . Die Präsentation rand() gilt als schädlich von Stephan T. Lavavej erklärt, warum wir auf die Verwendung von rand() in C++ zu Gunsten der random Kopfzeile und N3924: Entmutigung von rand() in C++14 unterstreicht diesen Punkt noch.

Das folgende Beispiel ist eine modifizierte Version des Beispielcodes auf der cppreference-Website und verwendet die std::mersenne_twister_engine Motor und der std::uniform_real_distribution die Zahlen im Bereich der [0,1) Bereich ( es live sehen ):

#include <iostream>
#include <iomanip>
#include <map>
#include <random>

int main()
{
    std::random_device rd;

    std::mt19937 e2(rd());

    std::uniform_real_distribution<> dist(0, 1);

    std::map<int, int> hist;
    for (int n = 0; n < 10000; ++n) {
        ++hist[std::round(dist(e2))];
    }

    for (auto p : hist) {
        std::cout << std::fixed << std::setprecision(1) << std::setw(2)
                  << p.first << ' ' << std::string(p.second/200, '*') << '\n';
    }
}

wird die Ausgabe ähnlich wie die folgende aussehen:

0 ************************
1 *************************

Da im Beitrag erwähnt wurde, dass Geschwindigkeit wichtig ist, sollten wir den Abschnitt cppreference berücksichtigen, der die verschiedenen Zufallszahlen-Engines beschreibt ( Hervorhebung von mir ):

Die Wahl des zu verwendenden Motors beinhaltet eine Reihe von Kompromissen*: die **linearer kongruenter Motor ist mäßig schnell a Speicherbedarf für den Zustand. Die verzögerte Fibonacci-Gattungen sehr schnell, selbst auf Prozessoren ohne fortgeschrittene arithmetische Befehle auf Kosten eines größeren Speichervolumens wünschenswerten spektralen Eigenschaften. Die Mersenne-Twister ist hat größere Anforderungen an die Zustandsspeicherung aber mit den richtigen Parametern die längste sich nicht wiederholende Sequenz mit der wünschenswertesten Spektraleigenschaften (bei einer bestimmten Definition von "wünschenswert").

Wenn also der Wunsch nach einem schnelleren Generator besteht, dann ranlux24_base o ranlux48_base sind besser geeignet als mt19937 .

rand()

Wenn Sie gezwungen sind, die rand() dann die C FAQ für einen Leitfaden über Wie kann ich Gleitkommazufallszahlen erzeugen? gibt uns ein ähnliches Beispiel für die Erzeugung von an auf dem Intervall [0,1) :

#include <stdlib.h>

double randZeroToOne()
{
    return rand() / (RAND_MAX + 1.);
}

und um eine Zufallszahl im Bereich von [M,N) :

double randMToN(double M, double N)
{
    return M + (rand() / ( RAND_MAX / (N-M) ) ) ;  
}

6voto

DarthGizka Punkte 3888

Die C++11-Standardbibliothek enthält ein anständiges Gerüst und einige brauchbare Generatoren, was für Hausaufgaben und den spontanen Einsatz vollkommen ausreicht.

Für produktiven Code sollten Sie jedoch genau wissen, welche spezifischen Eigenschaften die verschiedenen Generatoren haben, bevor Sie sie verwenden, da sie alle ihre Tücken haben. Außerdem besteht keiner von ihnen Standardtests für PRNGs wie TestU01, mit Ausnahme der Ranlux-Generatoren, wenn sie mit einem großzügigen Luxusfaktor verwendet werden.

Wenn Sie solide, wiederholbare Ergebnisse erzielen wollen, müssen Sie Ihren eigenen Generator mitbringen.

Wenn Sie mobil sein wollen, müssen Sie Ihren eigenen Generator mitbringen.

Wenn Sie mit der eingeschränkten Portabilität leben können, können Sie boost oder das C++11-Framework in Verbindung mit Ihren eigenen Generatoren verwenden.

Weitere Einzelheiten - einschließlich des Codes für einen einfachen und schnellen Generator von hervorragender Qualität und reichlich Links - finden Sie in meinen Antworten zu ähnlichen Themen:

Für professionelle einheitliche Gleitkommaabweichungen gibt es zwei weitere Punkte zu beachten:

  • offener vs. halboffener vs. geschlossener Bereich, d. h. (0,1), [0, 1) oder [0,1]
  • Methode der Umwandlung von Integral- in Gleitkommazahlen (Genauigkeit, Geschwindigkeit)

Beides sind eigentlich zwei Seiten derselben Medaille, denn die Umrechnungsmethode sorgt für den Einschluss bzw. Ausschluss von 0 und 1. Hier sind drei verschiedene Methoden für das halboffene Intervall:

// exact values computed with bc

#define POW2_M32   2.3283064365386962890625e-010
#define POW2_M64   5.421010862427522170037264004349e-020

double random_double_a ()
{
   double lo = random_uint32() * POW2_M64;
   return lo + random_uint32() * POW2_M32;
}

double random_double_b ()
{
   return random_uint64() * POW2_M64;
}

double random_double_c ()
{
   return int64_t(random_uint64()) * POW2_M64 + 0.5;
}

( random_uint32() y random_uint64() sind Platzhalter für Ihre eigentlichen Funktionen und würden normalerweise als Template-Parameter übergeben werden)

Methode a demonstriert, wie man eine einheitliche Abweichung erzeugt, die nicht durch übermäßige Präzision für niedrigere Werte verzerrt wird; der Code für 64-Bit wird nicht gezeigt, da er einfacher ist und nur die Maskierung von 11 Bits beinhaltet. Die Verteilung ist für alle Funktionen gleichmäßig, aber ohne diesen Trick gäbe es mehr unterschiedliche Werte in dem Bereich, der näher an 0 liegt als anderswo (feinere Rasterabstände aufgrund des variierenden ulp).

Methode c zeigt, wie man auf bestimmten gängigen Plattformen, auf denen die FPU nur einen vorzeichenbehafteten 64-Bit-Integraltyp kennt, eine einheitliche Abweichung schneller erreichen kann. Was Sie am häufigsten sehen, ist die Methode b aber dort muss der Compiler unter der Haube eine Menge zusätzlichen Code erzeugen, um die vorzeichenlose Semantik zu erhalten.

Kombinieren Sie diese Prinzipien, um Ihre eigene maßgeschneiderte Lösung zu schaffen.

All dies wird in Jürgen Doorniks ausgezeichnetem Aufsatz erläutert Umwandlung von Zufallszahlen mit hoher Periode in Fließkommazahlen .

5voto

Vijay Mathew Punkte 25917

En Klasse random_real von der Boost Zufallsbibliothek ist das, was Sie brauchen.

5voto

John D. Cook Punkte 28817

So würden Sie es machen, wenn Sie C++ TR1 .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X