Question

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

Était-ce utile?

La solution

La suggestion de Jarrett d’utiliser une transformation de Box-Muller est bonne pour une solution rapide. 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)

Autres conseils

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

J'ai créé des méthodes d'extension pour la classe .NET Random , notamment une implémentation de la transformation de Box-Muller. Puisque ce sont des extensions, tant que le projet est inclus (ou que vous référencez la DLL compilée), vous pouvez toujours le faire

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

J'espère que personne ne se soucie de la fiche éhontée.

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

entrer la description de l'image ici

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 la documentation ici: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

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

https://connect.microsoft. com / VisualStudio / feedback / details / 634346 / guassian-distribution-aléatoire-nombres

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

Si vous recherchez une vitesse pure, alors le algorithme de Zigorat est généralement reconnu comme étant le approche la plus rapide.

Je ne suis toutefois pas un expert sur ce sujet. J'en ai ressenti le besoin tout en mettant en œuvre un filtre à particules pour ma bibliothèque de football robotique simulée 3D RoboCup et a été surpris de constater 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 de 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;
    }
}

Math.NET Iridium prétend également implémenter des "générateurs aléatoires non uniformes (normal, poisson, binomial"). , ...) ".

Voici une autre solution simple et rapide pour générer des variables aléatoires distribuées normalement . Il dessine un point quelconque (x, y) et vérifie si ce point se situe 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 s et µ afin que la courbe de la fonction ne soit pas coupée à son maximum. / points minimum (par exemple à x = moyenne). Pensez aux intervalles de x et de y comme un cadre de sélection dans lequel la courbe doit s’inscrire.

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 ne signifient peut-être pas deux fois plus vite, mais je pense que cela devrait être presque deux fois plus vite. 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();
    }
}

Pour en savoir plus 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 de Ziggurat en C #, que vous trouverez ici:

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

Ziggurat utilise une table de correspondance pour gérer les valeurs suffisamment éloignées de la courbe, qu’elle 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 se trouve un chiffre.

En élargissant les réponses de @Noakes et @ Hameer, j'ai également implémenté une classe 'gaussienne', mais pour simplifier l'espace mémoire, j'en 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 qu'il soit nécessaire de créer un objet aléatoire supplémentaire pour le gérer. J'ai également éliminé les propriétés de la classe globale _available et _nextgauss, car je ne les considérais pas nécessaires, car cette classe est basée sur une instance. Elle doit donc être thread-safe si vous attribuez à chaque thread son propre objet gaussien. J'ai également déplacé toutes les variables allouées à l'exécution de la fonction et les ai transformées en propriétés de classe. Cela réduira le nombre d'appels au gestionnaire de mémoire, car les 4 doublons ne devraient théoriquement jamais être désaffectés tant que l'objet n'a pas été détruit. / p>

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

Vous pouvez essayer Infer.NET. Ce n'est pas encore une licence commerciale. Voici le lien

.

C’est un framework probabiliste pour .NET qui a développé mes recherches Microsoft. Ils ont des types .NET pour les distributions de Bernoulli, Bêta, Gamma, Gaussien, Poisson et probablement d’autres que j’ai laissés de côté.

Cela peut accomplir ce que vous voulez. Merci.

Ceci est ma simple implémentation inspirée de Box Muller. Vous pouvez augmenter la résolution pour répondre à vos besoins. Bien que cela fonctionne très bien pour moi, il s’agit d’une approximation de portée limitée. N'oubliez donc pas que les queues sont fermées et finies, mais vous pouvez certainement les agrandir si nécessaire.

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

Je ne pense pas qu'il y en ait. Et j’espère vraiment que ce n’est pas le cas, car le framework est déjà assez lourd, sans une telle fonctionnalité spécialisée qui le remplisse davantage.

Jetez un coup d'oeil à http://www.extremeoptimization.com/Statistics /UsersGuide/ContinuousDistributions/NormalDistribution.aspx et http: //www.vbforums .com / showthread.php? t = 488959 pour des solutions .NET tierces cependant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top