it-swarm.com.de

Generieren Sie nach einer Normalverteilung in C/C++ Zufallszahlen

Wie kann ich nach einer Normalverteilung in C oder C++ leicht Zufallszahlen generieren?

Ich möchte keinen Einsatz von Boost.

Ich weiß, dass Knuth ausführlich darüber spricht, aber ich habe momentan keine Bücher zur Hand.

107
Damien

Es gibt viele Methoden, um Gaußsche verteilte Nummern aus einem regulären RNG generieren .

Die Box-Muller-Transformation wird häufig verwendet. Es erzeugt korrekt Werte mit einer Normalverteilung. Die Rechnung ist einfach. Sie erzeugen zwei (einheitliche) Zufallszahlen und durch Anwenden einer Formel erhalten Sie zwei normalverteilte Zufallszahlen. Gib eins zurück und speichere das andere für die nächste Anfrage für eine Zufallszahl.

87
S.Lott

C++ 11

C++ 11 bietet std::normal_distribution , so würde ich heute gehen.

C oder älteres C++

Hier sind einige Lösungen in aufsteigender Komplexität:

  1. Addiere 12 einheitliche Zufallszahlen von 0 bis 1 und subtrahiere 6. Dies entspricht dem Mittelwert und der Standardabweichung einer normalen Variablen. Ein offensichtlicher Nachteil besteht darin, dass der Bereich - im Gegensatz zu einer echten Normalverteilung - auf ± 6 begrenzt ist.

  2. Die Box-Muller-Transformation. Dies ist oben aufgeführt und relativ einfach zu implementieren. Wenn Sie sehr genaue Proben benötigen, müssen Sie jedoch beachten, dass die Box-Muller-Transformation in Kombination mit einigen gleichförmigen Generatoren unter einer Anomalie namens Neave-Effekt leidet1.

  3. Für die beste Genauigkeit empfehle ich das Zeichnen von Uniformen und das Anwenden der inversen kumulativen Normalverteilung, um normalverteilte Abweichungen zu erhalten. Hier ist ein sehr guter Algorithmus für inverse kumulative Normalverteilungen.

1. H. R. Neave, "Zur Verwendung der Box-Muller-Transformation mit multiplikativen kongruentiellen Pseudozufallszahlengeneratoren", Applied Statistics, 22, 92-97, 1973

44
Peter G.

Eine schnelle und einfache Methode besteht darin, eine Anzahl gleichmäßig verteilter Zufallszahlen zu summieren und deren Durchschnitt zu ermitteln. Eine ausführliche Erklärung, warum dies funktioniert, finden Sie im Central Limit Theorem .

31
Paul R

Ich habe ein C++ - Open-Source-Projekt für normalverteilte Benchmarks zur Erzeugung von Zufallszahlen erstellt

Es vergleicht mehrere Algorithmen, einschließlich

  • Methode des zentralen Grenzwertsatzes
  • Box-Muller-Transformation
  • Marsaglia Polartechnik
  • Ziggurat-Algorithmus
  • Inverse Transformations-Abtastmethode.
  • cpp11random verwendet C++ 11 std::normal_distribution mit std::minstd_Rand (es handelt sich tatsächlich um eine Box-Muller-Transformation in clang).

Die Ergebnisse der Version mit einfacher Genauigkeit (float) auf iMac [email protected], clang 6.1, 64-Bit: 

normaldistf

Für die Richtigkeit überprüft das Programm den Mittelwert, die Standardabweichung, die Schiefe und die Kurtosis der Proben. Es wurde festgestellt, dass das CLT-Verfahren durch Summieren von 4, 8 oder 16 einheitlichen Zahlen keine gute Wölbung wie die anderen Verfahren aufweist.

Der Ziggurat-Algorithmus hat eine bessere Leistung als die anderen. Es ist jedoch nicht für SIMD-Parallelität geeignet, da es eine Tabellensuche und -verzweigungen erfordert. Box-Muller mit SSE2/AVX-Befehlssatz ist viel schneller (x1.79, x2.99) als die Nicht-SIMD-Version des Ziggurat-Algorithmus.

Daher schlage ich vor, Box-Muller für die Architektur mit SIMD-Befehlssätzen zu verwenden, und kann ansonsten ziggurat werden.


P.S. Der Benchmark verwendet eine einfachste LCG PRNG zur Erzeugung gleichmäßig verteilter Zufallszahlen. Daher kann es für einige Anwendungen nicht ausreichen. Der Leistungsvergleich sollte jedoch fair sein, da bei allen Implementierungen das gleiche PRNG verwendet wird. Der Benchmark testet daher hauptsächlich die Leistung der Transformation.

23
Milo Yip

Hier ist ein C++ - Beispiel, das auf einigen Referenzen basiert. Dies ist schnell und schmutzig, es ist besser, wenn Sie die Boost-Bibliothek nicht neu erfinden und verwenden.

#include "math.h" // for Rand, and Rand
double sampleNormal() {
    double u = ((double) Rand() / (Rand_MAX)) * 2 - 1;
    double v = ((double) Rand() / (Rand_MAX)) * 2 - 1;
    double r = u * u + v * v;
    if (r == 0 || r > 1) return sampleNormal();
    double c = sqrt(-2 * log(r) / r);
    return u * c;
}

Sie können ein QQ-Diagramm verwenden, um die Ergebnisse zu untersuchen und zu sehen, wie gut sie sich einer echten Normalverteilung annähert (Rangfolge Ihrer Stichproben 1..x, Umwandlung der Ränge in Proportionen der Gesamtzahl von x, dh wie viele Stichproben die Z-Werte erhalten und zeichnen sie auf. Eine gerade Linie nach oben ist das gewünschte Ergebnis.

14
Pete855217

Verwenden Sie std::tr1::normal_distribution.

Der std :: tr1-Namespace ist nicht Teil von boost. Es ist der Namespace, der die Bibliotheksergänzungen aus dem C++ Technical Report 1 enthält und in aktuellen Microsoft-Compilern und gcc unabhängig von boost verfügbar ist.

12
Joe Gauterin

So generieren Sie die Beispiele auf einem modernen C++ - Compiler.

#include <random>
...
std::mt19937 generator;
double mean = 0.0;
double stddev  = 1.0;
std::normal_distribution<double> normal(mean, stddev);
cerr << "Normal: " << normal(generator) << endl;
12
Petter

Sie können die GSL verwenden. Einige vollständige Beispiele werden angegeben , um die Verwendung zu demonstrieren.

4
Denis Arnaud

Schauen Sie unter: http://www.cplusplus.com/reference/random/normal_distribution/ . Es ist der einfachste Weg, normale Distributionen zu erstellen.

4
telcom

Wenn Sie C++ 11 verwenden, können Sie std::normal_distribution verwenden:

#include <random>

std::default_random_engine generator;
std::normal_distribution<double> distribution(/*mean=*/0.0, /*stddev=*/1.0);

double randomNumber = distribution(generator);

Es gibt viele andere Distributionen, mit denen Sie die Ausgabe der Zufallszahlen-Engine transformieren können.

4
Drew Noakes

Ich bin der Definition von PDF gefolgt, die in http://www.mathworks.com/help/stats/normal-distribution.html angegeben ist, und habe folgendes gefunden:

const double DBL_EPS_COMP = 1 - DBL_EPSILON; // DBL_EPSILON is defined in <limits.h>.
inline double RandU() {
    return DBL_EPSILON + ((double) Rand()/Rand_MAX);
}
inline double RandN2(double mu, double sigma) {
    return mu + (Rand()%2 ? -1.0 : 1.0)*sigma*pow(-log(DBL_EPS_COMP*RandU()), 0.5);
}
inline double RandN() {
    return RandN2(0, 1.0);
}

Es ist vielleicht nicht der beste Ansatz, aber es ist ziemlich einfach.

3
MJVC

Box-Muller-Implementierung:

#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iostream>
using namespace std;
 // return a uniformly distributed random number
double RandomGenerator()
{
  return ( (double)(Rand()) + 1. )/( (double)(Rand_MAX) + 1. );
}
 // return a normally distributed random number
double normalRandom()
{
  double y1=RandomGenerator();
  double y2=RandomGenerator();
  return cos(2*3.14*y2)*sqrt(-2.*log(y1));
}

int main(){
double sigma = 82.;
double Mi = 40.;
  for(int i=0;i<100;i++){
double x = normalRandom()*sigma+Mi;
    cout << " x = " << x << endl;
  }
  return 0;
}
0
Gall Anonim

1) Grafisch intuitiv können Sie Gaußsche Zufallszahlen generieren, indem Sie die Monte-Carlo-Methode verwenden. Sie würden einen zufälligen Punkt in einer Box um die Gaußsche Kurve erzeugen, indem Sie Ihren Pseudozufallszahlengenerator in C verwenden. Sie können mithilfe der Verteilungsgleichung berechnen, ob sich dieser Punkt innerhalb oder unterhalb der Gaußschen Verteilung befindet. Wenn sich dieser Punkt in der Gaußschen Verteilung befindet, haben Sie Ihre Gaußsche Zufallszahl als x-Wert des Punktes. 

Diese Methode ist nicht perfekt, da die Gaußsche Kurve technisch unendlich weit geht und Sie keine Box erstellen können, die in der x-Dimension gegen unendlich geht. Aber die Guassian-Kurve nähert sich in der y-Dimension ziemlich schnell 0, sodass ich mir keine Sorgen machen muss. Die Einschränkung der Größe Ihrer Variablen in C kann für Ihre Genauigkeit eher einschränkend sein.

2) Eine andere Möglichkeit wäre die Verwendung des zentralen Grenzwertsatzes, der besagt, dass unabhängige Zufallsvariablen eine Normalverteilung bilden. Unter Berücksichtigung dieses Theorems können Sie eine Gaußsche Zufallszahl approximieren, indem Sie eine große Anzahl unabhängiger Zufallsvariablen hinzufügen. 

Diese Methoden sind nicht die praktischsten, aber dies ist zu erwarten, wenn Sie keine bereits vorhandene Bibliothek verwenden möchten. Denken Sie daran, dass diese Antwort von jemandem kommt, der wenig oder keine Erfahrung mit Berechnungen oder Statistiken hat.

0
dan dan

Für die inverse kumulative Normalverteilung gibt es verschiedene Algorithmen. Die beliebtesten in quantitativer Finanzierung werden am http://chasethedevil.github.io/post/monte-carlo--inverse-cumulative-normal-distribution/ getestet.

Meiner Meinung nach gibt es nicht viel Anreiz, etwas anderes als den Algorithmus AS241 von Wichura zu verwenden: Es ist Maschinengenauigkeit, zuverlässig und schnell. Engpässe gibt es selten bei der Gaußschen Zufallszahlengenerierung.

Darüber hinaus zeigt es den Nachteil von Ziggurat-ähnlichen Ansätzen.

Die Top-Antwort hier spricht sich für Box-Müller aus, man sollte sich dessen bewusst sein, dass Mängel bekannt sind. Ich zitiere https://www.sciencedirect.com/science/article/pii/S0895717710005935 :

box-Muller wird in der Literatur vor allem aus zwei Gründen als etwas minderwertig angesehen. Wenn die Box-Muller-Methode auf Zahlen aus einem fehlerhaften linearen Kongruenzgenerator angewendet wird, bieten die transformierten Zahlen eine extrem schlechte Abdeckung des Bereichs. Darstellungen von transformierten Zahlen mit spiralförmigen Schwänzen finden sich in vielen Büchern, vor allem im klassischen Buch von Ripley, der wahrscheinlich der erste war, der diese Beobachtung machte. "

0
jherek

Die comp.lang.c FAQ -Liste hat drei Möglichkeiten, um auf einfache Weise Zufallszahlen mit einer Gaußschen Verteilung zu erzeugen.

Sie können einen Blick darauf werfen: http://c-faq.com/lib/gaussian.html

0
Delgan