web-dev-qa-db-fra.com

Variables gaussiennes aléatoires

Existe-t-il une classe dans la bibliothèque standard de .NET qui me donne la fonctionnalité de créer des variables aléatoires qui suivent la distribution gaussienne?

106
Sebastian Müller

La suggestion de Jarrett d'utiliser une transformation Box-Muller est bonne pour une solution rapide et sale. Une implémentation simple:

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

Cette question semble s'être déplacée au-dessus de Google pour la génération gaussienne .NET, j'ai donc pensé publier une réponse.

J'ai fait quelques méthodes d'extension pour la classe aléatoire .NET , y compris une implémentation de la transformation Box-Muller. Puisqu'il s'agit d'extensions, tant que le projet est inclus (ou que vous faites référence à la DLL compilée), vous pouvez toujours faire

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

J'espère que personne ne se soucie de la prise sans vergogne.

Exemple d'histogramme des résultats (une application de démonstration pour le dessiner est incluse):

enter image description here

55
Superbest

Math.NET fournit cette fonctionnalité. Voici comment:

double mean = 100;
double stdDev = 10;

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

Vous pouvez trouver de la documentation ici: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

16
Gordon Slysz

http://mathworld.wolfram.com/Box-MullerTransformation.html

À l'aide de deux variables aléatoires, vous pouvez générer des valeurs aléatoires le long d'une distribution gaussienne. Ce n'est pas du tout une tâche difficile.

11
Jarrett Meyer

J'ai créé une demande pour une telle fonctionnalité sur Microsoft Connect. Si c'est quelque chose que vous recherchez, votez pour et augmentez sa visibilité.

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

Cette fonctionnalité est incluse dans le SDK Java. Son implémentation est disponible dans le cadre de la documentation et est facilement portée vers C # ou d'autres langages .NET.

Si vous recherchez une vitesse pure, alors Zigorat Algorithm est généralement reconnu comme l'approche la plus rapide.

Je ne suis pas un expert sur ce sujet - j'ai rencontré ce besoin lors de l'implémentation d'un filtre à particules pour ma bibliothèque de football robotique simulée RoboCup 3D et j'ai été surpris quand cela n'a pas été inclus dans le cadre.


En attendant, voici un wrapper pour Random qui fournit une implémentation efficace de la méthode polaire Box Muller:

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 prétend également implémenter "des générateurs aléatoires non uniformes (normal, poisson, binomial, ...)".

5
Jason DeFontes

Voici une autre solution rapide et sale pour générer des variables aléatoires qui sont distribuées normalement . Il dessine un point aléatoire (x, y) et vérifie si ce point se trouve sous la courbe de votre fonction de densité de probabilité, sinon répétez.

Bonus: Vous pouvez générer des variables aléatoires pour toute autre distribution (par exemple, la distribution exponentielle ou distribution de poisson ) simplement en remplaçant la fonction de densité.

    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 * σ * σ));
    }

Important: Sélectionnez l'intervalle de y et les paramètres σ et μ pour que la courbe de la fonction ne soit pas coupée à ses points maximum/minimum (par exemple à x = moyenne). Considérez les intervalles de x et y comme une boîte englobante, dans dans lequel la courbe doit s'insérer.

4
Doomjunky

Je voudrais développer la réponse de @ yoyoyoyosef en la rendant encore plus rapide et en écrivant une classe wrapper. Les frais généraux encourus peuvent ne pas signifier deux fois plus vite, mais je pense que cela devrait être presque deux fois plus rapide. Ce n'est pas thread-safe, cependant.

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

S'étendant sur la réponse de Drew Noakes, si vous avez besoin de meilleures performances que Box-Muller (environ 50 à 75% plus rapide), Colin Green a partagé une implémentation de l'algorithme Ziggurat en C #, que vous pouvez trouver ici:

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

Ziggurat utilise une table de recherche pour gérer les valeurs suffisamment éloignées de la courbe, qu'il acceptera ou rejettera rapidement. Environ 2,5% du temps, il doit effectuer d'autres calculs pour déterminer de quel côté de la courbe un nombre est activé.

1
Neil

En développant les réponses de @Noakes et @ Hameer, j'ai également implémenté une classe "gaussienne", mais pour simplifier l'espace mémoire, je l'ai fait un enfant de la classe Random afin que vous puissiez également appeler la base Next (), NextDouble () , etc. de la classe gaussienne également sans avoir à créer un objet Random supplémentaire pour le gérer. J'ai également éliminé les propriétés de classe globale _available et _nextgauss, car je ne les voyais pas comme nécessaires car cette classe est basée sur une instance, elle devrait être thread-safe, si vous donnez à chaque thread son propre objet gaussien. J'ai également déplacé toutes les variables allouées au moment de l'exécution hors de la fonction et en ai fait des propriétés de classe, cela réduira le nombre d'appels au gestionnaire de mémoire car les 4 doubles ne devraient théoriquement jamais être désalloués jusqu'à ce que l'objet soit détruit.

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

Vous pouvez essayer Infer.NET. Ce n'est pas encore sous licence commerciale. Ici est là lien

Il s'agit d'un cadre probabiliste pour .NET développé mes recherches Microsoft. Ils ont des types .NET pour les distributions de Bernoulli, Beta, Gamma, Gaussian, Poisson, et probablement un peu plus que j'ai omis.

Il peut accomplir ce que vous voulez. Merci.

0
Aaron Stainback

Ceci est ma mise en œuvre simple inspirée de Box Muller. Vous pouvez augmenter la résolution pour l'adapter à vos besoins. Bien que cela fonctionne très bien pour moi, il s'agit d'une approximation de plage limitée, alors gardez à l'esprit que les queues sont fermées et finies, mais vous pouvez certainement les étendre au besoin.

    //
    // 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