Zufällige Gaußsche Variablen


117

Gibt es eine Klasse in der Standardbibliothek von .NET, mit der ich Zufallsvariablen erstellen kann, die der Gaußschen Verteilung folgen?


http://mathworld.wolfram.com/Box-MullerTransformation.html Mit zwei Zufallsvariablen können Sie Zufallswerte entlang einer Gaußschen Verteilung generieren. Es ist überhaupt keine schwierige Aufgabe.
Jarrett Meyer

1
Ich möchte nur ein mathematisches Ergebnis hinzufügen, das für Normalverteilungen (aufgrund komplexer CDF) nicht sofort nützlich ist, aber für viele andere Verteilungen nützlich ist. Wenn Sie gleichmäßig verteilte Zufallszahlen in [0,1] (mit Random.NextDouble()) in die Umkehrung der CDF JEDER Verteilung einfügen, erhalten Sie Zufallszahlen, die DIESER Verteilung folgen. Wenn Ihre Anwendung keine genau normalverteilten Variablen benötigt, ist die logistische Verteilung eine sehr enge Annäherung an die normale und verfügt über eine leicht invertierbare CDF.
Ozzah

1
Das MedallionRandom NuGet-Paket enthält eine Erweiterungsmethode zum Abrufen normalverteilter Werte aus einer Randommithilfe der Box-Muller-Transformation (in den folgenden Antworten unten erwähnt).
ChaseMedallion

Antworten:


180

Jarretts Vorschlag, eine Box-Muller-Transformation zu verwenden, ist gut für eine schnelle und schmutzige Lösung. Eine einfache Implementierung:

Random rand = new Random(); //reuse this if you are generating many
double u1 = 1.0-rand.NextDouble(); //uniform(0,1] random doubles
double u2 = 1.0-rand.NextDouble();
double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) *
             Math.Sin(2.0 * Math.PI * u2); //random normal(0,1)
double randNormal =
             mean + stdDev * randStdNormal; //random normal(mean,stdDev^2)

3
Ich habe es getestet und mit MathNets Mersenne Twister RNG und NormalDistribution verglichen. Ihre Version ist mehr als doppelt so schnell und das Endergebnis ist im Grunde das gleiche (visuelle Überprüfung der "Glocken").
Johann Gerell

4
@Johann, wenn Sie reine Geschwindigkeit suchen, dann wird der Zigorat-Algorithmus allgemein als der schnellste Ansatz anerkannt. Darüber hinaus kann der obige Ansatz beschleunigt werden, indem ein Wert von einem Anruf zum nächsten übertragen wird.
Drew Noakes

Hallo, auf was soll die stdDevVariable gesetzt werden? Ich verstehe, dass dies für bestimmte Anforderungen konfiguriert werden kann, aber gibt es irgendwelche Grenzen (dh Max / Min-Werte)?
Hofnarwillie

@hofnarwillie stdDev ist der Skalierungsparameter der Normalverteilung, der eine beliebige positive Zahl sein kann. Je größer es ist, desto zerstreuter werden die generierten Zahlen sein. Verwenden Sie für eine Standardnormalverteilung die Parameter mean = 0 und stdDev = 1.
Yoyoyoyosef

1
@ Jack Ich glaube nicht. Nur das -2 * Math.Log (u1) befindet sich innerhalb des sqrt, und das Protokoll ist immer negativ oder null, da u1 <= 1
yoyoyoyosef

62

Diese Frage scheint über Google für die .NET-Gauß-Generation zu stehen, daher dachte ich, ich würde eine Antwort posten.

Ich habe einige Erweiterungsmethoden für die .NET Random-Klasse erstellt , einschließlich einer Implementierung der Box-Muller-Transformation. Da es sich um Erweiterungen handelt, können Sie dies tun, solange das Projekt enthalten ist (oder Sie auf die kompilierte DLL verweisen)

var r = new Random();
var x = r.NextGaussian();

Hoffe, niemand stört den schamlosen Stecker.

Beispielhistogramm der Ergebnisse (eine Demo-App zum Zeichnen ist enthalten):

Geben Sie hier die Bildbeschreibung ein


Ihre Erweiterungsklasse hat ein paar Dinge, nach denen ich gesucht habe! Vielen Dank!
Thomas

1
Sie haben einen kleinen Fehler in Ihrer NextGaussian-Methode. NextDouble () Gibt eine zufällige Gleitkommazahl zurück, die größer oder gleich 0,0 und kleiner als 1,0 ist. Also solltest du u1 = 1.0 haben - NextDouble () .... anderes Protokoll (0) wird explodieren
Mitch Wheat


8

Ich habe eine Anfrage für eine solche Funktion in Microsoft Connect erstellt. Wenn Sie danach suchen, stimmen Sie dafür und erhöhen Sie die Sichtbarkeit.

https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassian-normal-distribution-random-numbers

Diese Funktion ist im Java SDK enthalten. Die Implementierung ist als Teil der Dokumentation verfügbar und kann problemlos in C # oder andere .NET-Sprachen portiert werden.

Wenn Sie reine Geschwindigkeit suchen, wird der Zigorat-Algorithmus allgemein als der schnellste Ansatz anerkannt.

Ich bin jedoch kein Experte in diesem Thema - ich habe dies bei der Implementierung eines Partikelfilters für meine simulierte RoboCup 3D-Roboter-Fußballbibliothek festgestellt und war überrascht, als dies nicht im Framework enthalten war.


In der Zwischenzeit finden Sie hier einen Wrapper Random, der eine effiziente Implementierung der Box Muller-Polarmethode ermöglicht:

public sealed class GaussianRandom
{
    private bool _hasDeviate;
    private double _storedDeviate;
    private readonly Random _random;

    public GaussianRandom(Random random = null)
    {
        _random = random ?? new Random();
    }

    /// <summary>
    /// Obtains normally (Gaussian) distributed random numbers, using the Box-Muller
    /// transformation.  This transformation takes two uniformly distributed deviates
    /// within the unit circle, and transforms them into two independently
    /// distributed normal deviates.
    /// </summary>
    /// <param name="mu">The mean of the distribution.  Default is zero.</param>
    /// <param name="sigma">The standard deviation of the distribution.  Default is one.</param>
    /// <returns></returns>
    public double NextGaussian(double mu = 0, double sigma = 1)
    {
        if (sigma <= 0)
            throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero.");

        if (_hasDeviate)
        {
            _hasDeviate = false;
            return _storedDeviate*sigma + mu;
        }

        double v1, v2, rSquared;
        do
        {
            // two random values between -1.0 and 1.0
            v1 = 2*_random.NextDouble() - 1;
            v2 = 2*_random.NextDouble() - 1;
            rSquared = v1*v1 + v2*v2;
            // ensure within the unit circle
        } while (rSquared >= 1 || rSquared == 0);

        // calculate polar tranformation for each deviate
        var polar = Math.Sqrt(-2*Math.Log(rSquared)/rSquared);
        // store first deviate
        _storedDeviate = v2*polar;
        _hasDeviate = true;
        // return second deviate
        return v1*polar*sigma + mu;
    }
}

Ich habe allerdings ein paar Werte davon. kann jemand überprüfen, was los ist?
mk7

@ mk7, eine um Null zentrierte Gaußsche Wahrscheinlichkeitsfunktion liefert genauso wahrscheinlich negative Werte wie positive Werte.
Drew Noakes

Du hast recht! Da ich eine Liste der Gewichte in einer typischen Population mit Gaußschem PDF erhalten möchte, setze ich mu auf 75 [in kg] und Sigma auf 10. Muss ich eine neue Instanz von GaussianRandom für die Generierung festlegen? jedes zufällige Gewicht?
mk7

Sie können weiterhin Beispiele aus einer Instanz zeichnen.
Drew Noakes


4

Hier ist eine weitere schnelle und schmutzige Lösung zum Generieren von Zufallsvariablen, die normalverteilt sind . Es zeichnet einen zufälligen Punkt (x, y) und prüft, ob dieser Punkt unter der Kurve Ihrer Wahrscheinlichkeitsdichtefunktion liegt, andernfalls wiederholen.

Bonus: Sie können Zufallsvariablen für jede andere Verteilung (z. B. Exponentialverteilung oder Poissonverteilung ) generieren, indem Sie einfach die Dichtefunktion ersetzen.

    static Random _rand = new Random();

    public static double Draw()
    {
        while (true)
        {
            // Get random values from interval [0,1]
            var x = _rand.NextDouble(); 
            var y = _rand.NextDouble(); 

            // Is the point (x,y) under the curve of the density function?
            if (y < f(x)) 
                return x;
        }
    }

    // Normal (or gauss) distribution function
    public static double f(double x, double μ = 0.5, double σ = 0.5)
    {
        return 1d / Math.Sqrt(2 * σ * σ * Math.PI) * Math.Exp(-((x - μ) * (x - μ)) / (2 * σ * σ));
    }

Wichtig: Wählen Sie das Intervall von y und die Parameter σ und μ so, dass die Kurve der Funktion nicht an ihren Maximal- / Minimalpunkten (z. B. bei x = Mittelwert) abgeschnitten wird. Stellen Sie sich die Intervalle von x und y als Begrenzungsrahmen vor, in den die Kurve passen muss.


4
Tangenial, aber dies ist tatsächlich das erste Mal, dass mir klar wird, dass Sie Unicode-Symbole für Variablen anstelle von etwas Dummem wie _sigma oder _phi verwenden können ...
Slothario

@Slothario Ich danke Entwicklern überall für die Verwendung von 'etwas Dummem': |
user2864740

2

Ich möchte die Antwort von @ yoyoyoyosef erweitern, indem ich sie noch schneller mache und eine Wrapper-Klasse schreibe. Der anfallende Overhead bedeutet möglicherweise nicht doppelt so schnell, aber ich denke, er sollte fast doppelt so schnell sein. Es ist jedoch nicht threadsicher.

public class Gaussian
{
     private bool _available;
     private double _nextGauss;
     private Random _rng;

     public Gaussian()
     {
         _rng = new Random();
     }

     public double RandomGauss()
     {
        if (_available)
        {
            _available = false;
            return _nextGauss;
        }

        double u1 = _rng.NextDouble();
        double u2 = _rng.NextDouble();
        double temp1 = Math.Sqrt(-2.0*Math.Log(u1));
        double temp2 = 2.0*Math.PI*u2;

        _nextGauss = temp1 * Math.Sin(temp2);
        _available = true;
        return temp1*Math.Cos(temp2);
     }

    public double RandomGauss(double mu, double sigma)
    {
        return mu + sigma*RandomGauss();
    }

    public double RandomGauss(double sigma)
    {
        return sigma*RandomGauss();
    }
}

2

Ausgehend von den Antworten von @Noakes und @ Hameer habe ich auch eine 'Gauß'sche' Klasse implementiert. Um den Speicherplatz zu vereinfachen, habe ich sie zu einem Kind der Random-Klasse gemacht, sodass Sie auch das grundlegende Next (), NextDouble () aufrufen können. usw. aus der Gaußschen Klasse, ohne dass ein zusätzliches Zufallsobjekt erstellt werden muss, um damit umzugehen. Ich habe auch die globalen Klasseneigenschaften _available und _nextgauss entfernt, da ich sie nicht als notwendig angesehen habe, da diese Klasse instanzbasiert ist. Sie sollte threadsicher sein, wenn Sie jedem Thread ein eigenes Gauß-Objekt geben. Ich habe auch alle zur Laufzeit zugewiesenen Variablen aus der Funktion verschoben und sie zu Klasseneigenschaften gemacht. Dadurch wird die Anzahl der Aufrufe des Speichermanagers verringert, da die 4 Doubles theoretisch niemals aufgehoben werden sollten, bis das Objekt zerstört ist.

public class Gaussian : Random
{

    private double u1;
    private double u2;
    private double temp1;
    private double temp2;

    public Gaussian(int seed):base(seed)
    {
    }

    public Gaussian() : base()
    {
    }

    /// <summary>
    /// Obtains normally (Gaussian) distrubuted random numbers, using the Box-Muller
    /// transformation.  This transformation takes two uniformly distributed deviates
    /// within the unit circle, and transforms them into two independently distributed normal deviates.
    /// </summary>
    /// <param name="mu">The mean of the distribution.  Default is zero</param>
    /// <param name="sigma">The standard deviation of the distribution.  Default is one.</param>
    /// <returns></returns>

    public double RandomGauss(double mu = 0, double sigma = 1)
    {
        if (sigma <= 0)
            throw new ArgumentOutOfRangeException("sigma", "Must be greater than zero.");

        u1 = base.NextDouble();
        u2 = base.NextDouble();
        temp1 = Math.Sqrt(-2 * Math.Log(u1));
        temp2 = 2 * Math.PI * u2;

        return mu + sigma*(temp1 * Math.Cos(temp2));
    }
}

Lokale Variablen sind auch Freunde.
user2864740

1

Um die Antwort von Drew Noakes zu erweitern: Wenn Sie eine bessere Leistung als Box-Muller benötigen (etwa 50-75% schneller), hat Colin Green eine Implementierung des Ziggurat-Algorithmus in C # freigegeben, die Sie hier finden:

http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html

Ziggurat verwendet eine Nachschlagetabelle, um Werte zu verarbeiten, die weit genug von der Kurve entfernt sind und die es schnell akzeptiert oder ablehnt. In etwa 2,5% der Fälle müssen weitere Berechnungen durchgeführt werden, um festzustellen, auf welcher Seite der Kurve sich eine Zahl befindet.


0

Sie könnten Infer.NET ausprobieren. Es ist jedoch noch nicht kommerziell lizenziert. Hier ist dort Link

Es ist ein probabilistisches Framework für .NET, das meine Microsoft-Forschung entwickelt hat. Sie haben .NET-Typen für Distributionen von Bernoulli, Beta, Gamma, Gauß, Poisson und wahrscheinlich einige weitere, die ich ausgelassen habe.

Es kann erreichen, was Sie wollen. Vielen Dank.


0

Dies ist meine einfache, von Box Muller inspirierte Implementierung. Sie können die Auflösung an Ihre Bedürfnisse anpassen. Obwohl dies für mich sehr gut funktioniert, handelt es sich um eine begrenzte Bereichsnäherung. Denken Sie also daran, dass die Schwänze geschlossen und endlich sind, aber Sie können sie sicherlich nach Bedarf erweitern.

    //
    // by Dan
    // islandTraderFX
    // copyright 2015
    // Siesta Key, FL
    //    
// 0.0  3231 ********************************
// 0.1  1981 *******************
// 0.2  1411 **************
// 0.3  1048 **********
// 0.4  810 ********
// 0.5  573 *****
// 0.6  464 ****
// 0.7  262 **
// 0.8  161 *
// 0.9  59 
//Total: 10000

double g()
{
   double res = 1000000;
   return random.Next(0, (int)(res * random.NextDouble()) + 1) / res;
}

public static class RandomProvider
{
   public static int seed = Environment.TickCount;

   private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
       new Random(Interlocked.Increment(ref seed))
   );

   public static Random GetThreadRandom()
   {
       return randomWrapper.Value;
   }
} 

Dies ist meine einfache, von Box Muller inspirierte Implementierung. Sie können die Auflösung an Ihre Bedürfnisse anpassen. Dies ist sehr schnell, einfach und funktioniert für meine neuronalen Netz-Apps, die eine ungefähre Gaußsche Wahrscheinlichkeitsdichtefunktion benötigen, um die Aufgabe zu erledigen. Hoffe, es hilft jemandem, Zeit und CPU-Zyklen zu sparen. Obwohl dies für mich sehr gut funktioniert, handelt es sich um eine Annäherung an eine begrenzte Reichweite. Denken Sie also daran, dass die Schwänze geschlossen und endlich sind, aber Sie können sie sicherlich nach Bedarf erweitern.
Daniel Howard

1
Hey Daniel, ich habe eine Bearbeitung vorgeschlagen, die die Beschreibung aus Ihrem Kommentar in die Antwort selbst einbezieht. Außerdem wird das '//' entfernt, das den tatsächlichen Code in Ihrer Antwort auskommentiert hat. Sie können die Bearbeitung selbst
vornehmen,

-1

Ich glaube nicht. Und ich hoffe wirklich, dass dies nicht der Fall ist, da das Framework bereits ausreichend aufgebläht ist, ohne dass eine solche spezielle Funktionalität es noch mehr ausfüllt.

Unter http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx und http://www.vbforums.com/showthread.php?t=488959 finden Sie jedoch .NET-Lösungen von Drittanbietern.


7
Seit wann ist die Gaußsche Distribution "spezialisiert"? Es ist weitaus allgemeiner als beispielsweise AJAX oder DataTables.
TraumaPony

@TraumaPony: Versuchen Sie ernsthaft vorzuschlagen, dass mehr Entwickler die Gaußsche Distribution verwenden als AJAX regelmäßig?
David Arno

3
Möglicherweise; Was ich sage ist, dass es viel spezialisierter ist. Es gibt nur eine Verwendung - Web-Apps. Gaußsche Verteilungen haben eine unglaubliche Anzahl von nicht verwandten Verwendungen.
TraumaPony

@ DavidArno, schlagen Sie ernsthaft vor, dass weniger Funktionalität ein Framework verbessert?
Jodrell

1
@Jodrell, um ein bestimmtes Beispiel zu nennen, ich denke, die Entscheidung, MVC als separates Framework und nicht als Teil des .NET-Hauptframeworks zu definieren, war gut.
David Arno
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.