it-swarm.com.de

Zufällige Gaußsche Variablen

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

106

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)
164
yoyoyoyosef

Diese Frage scheint auf Google für die .NET-Gauß-Generation verschoben worden zu sein, daher habe ich mir gedacht, dass ich eine Antwort posten würde.

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 dennoch 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):

enter image description here

55
Superbest

Math.NET bietet diese Funktionalität. Hier ist wie:

double mean = 100;
double stdDev = 10;

MathNet.Numerics.Distributions.Normal normalDist = new Normal(mean, stdDev);
double randomGaussianValue=   normalDist.Sample();

Dokumentation finden Sie hier: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

16
Gordon Slysz

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.

11
Jarrett Meyer

Ich habe eine Anfrage für eine solche Funktion in Microsoft Connect erstellt. Wenn Sie danach suchen, stimmen Sie ab 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. Die Implementierung ist verfügbar als Teil der Dokumentation 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 erkannt.

Ich bin allerdings kein Experte für dieses Thema - ich bin auf die Notwendigkeit gestoßen, als ich Partikelfilter für meine RoboCup 3D simulierte Roboter-Fußballbibliothek implementierte und war überrascht, wann Dies war nicht im Framework enthalten.


In der Zwischenzeit ist hier ein Wrapper für Random, der eine effiziente Implementierung der Box-Muller-Polar-Methode bietet:

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;
    }
}
7
Drew Noakes

Math.NET Iridium behauptet auch, "uneinheitliche Zufallsgeneratoren (normal, poisson, binomial, ...)" zu implementieren.

5
Jason DeFontes

Hier ist eine andere schnelle und schmutzige Lösung zum Erzeugen 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, ansonsten wiederholen.

Bonus: Sie können Zufallsvariablen für jede andere Verteilung erzeugen (z. B. die Exponentialverteilung oder Poissonverteilung ), 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 μ , damit die Kurve der Funktion nicht an ihren Maximal-/Minimalpunkten abgeschnitten wird (z. B. bei x = Mittelwert). Stellen Sie sich die Intervalle von x und y als Begrenzungsrahmen vor in die die Kurve passen muss.

4
Doomjunky

Ich möchte die Antwort von @ yoyoyoyosef erweitern, indem ich sie noch schneller mache und eine Wrapper-Klasse schreibe. Der anfallende Overhead bedeutet vielleicht 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
Hameer Abbasi

Um Drew Noakes 'Antwort zu erläutern: 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. Ungefähr 2,5% der Zeit muss es weitere Berechnungen durchführen, um festzustellen, auf welcher Seite der Kurve sich eine Zahl befindet.

1
Neil

Ausgehend von den Antworten von @Noakes und @ Hameer habe ich auch eine 'Gauß'-Klasse implementiert. Zur Vereinfachung des Speicherplatzes habe ich sie jedoch zu einer untergeordneten Klasse der Random-Klasse gemacht, sodass Sie auch die grundlegenden Funktionen Next (), NextDouble () aufrufen können. usw. aus der Gaußschen Klasse, ohne ein zusätzliches Zufallsobjekt erstellen zu müssen, 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 zuweisen. Ich habe auch alle zur Laufzeit zugewiesenen Variablen aus der Funktion entfernt und sie zu Klasseneigenschaften gemacht. Dadurch wird die Anzahl der Aufrufe des Speichermanagers verringert, da die 4 Double theoretisch niemals freigegeben 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));
    }
}
1
user8262209

Sie könnten Infer.NET versuchen. Es ist jedoch noch nicht kommerziell lizenziert. Hier ist da 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, Gaussian, Poisson und wahrscheinlich einige mehr, die ich ausgelassen habe.

Es kann erreichen, was Sie wollen. Vielen Dank.

0
Aaron Stainback

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 gut funktioniert, ist dies eine begrenzte Bereichsannäherung. Denken Sie also daran, dass die Schwänze geschlossen und endlich sind, aber Sie können sie 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;
   }
} 
0
Daniel Howard