Existe-t-il une classe dans la bibliothèque standard de .NET qui me donne la fonctionnalité pour créer des variables aléatoires qui suivent la distribution gaussienne?
Je voudrais juste ajouter un résultat mathématique qui n'est pas immédiatement utile pour les distributions normales (en raison de CDF complexes), mais qui est utile pour de nombreuses autres distributions. Si vous mettez des nombres aléatoires uniformément distribués dans [0,1] (avec Random.NextDouble()) dans l'inverse de la CDF de N'IMPORTE QUELLE distribution, vous obtiendrez des nombres aléatoires qui suivent CETTE distribution. Si votre application n'a pas besoin de variables distribuées avec précision, alors la distribution logistique est une approximation très proche de la normale et a un CDF facilement inversible.
Ozzah
1
Le package MedallionRandom NuGet contient une méthode d'extension pour récupérer des valeurs normalement distribuées à partir d'un Randomutilisant la transformation Box-Muller (mentionnée dans plusieurs réponses ci-dessous).
ChaseMedallion
Réponses:
182
La suggestion de Jarrett d'utiliser une transformation Box-Muller est bonne pour une solution rapide et sale. Une implémentation simple:
Random rand =newRandom();//reuse this if you are generating manydouble u1 =1.0-rand.NextDouble();//uniform(0,1] random doublesdouble 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)
Je l'ai testé et comparé à Mersenne Twister RNG et NormalDistribution de MathNet. Votre version est plus de deux fois plus rapide et le résultat final est fondamentalement le même (inspection visuelle des "cloches").
Johann Gerell
4
@Johann, si vous recherchez une vitesse pure, alors l' algorithme Zigorat est généralement reconnu comme l'approche la plus rapide. En outre, l'approche ci-dessus peut être accélérée en portant une valeur d'un appel à l'autre.
Drew Noakes le
Salut, à quoi la stdDevvariable doit-elle être définie? Je comprends que cela peut être configuré selon des exigences spécifiques, mais y a-t-il des limites (c'est-à-dire des valeurs max / min)?
hofnarwillie
@hofnarwillie stdDev est le paramètre d'échelle de la distribution normale, qui peut être n'importe quel nombre positif. Plus il est grand, plus les nombres générés seront dispersés. Pour une distribution normale standard, utilisez les paramètres mean = 0 et stdDev = 1.
yoyoyoyosef
1
@Jack je ne pense pas. Seul le -2 * Math.Log (u1) est à l'intérieur du sqrt, et le journal sera toujours négatif ou nul depuis u1 <= 1
yoyoyoyosef
63
Cette question semble avoir été déplacée au-dessus de Google pour la génération gaussienne .NET, alors j'ai pensé que je publierais une réponse.
J'ai créé des méthodes d'extension pour la classe .NET Random , 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 référencez la DLL compilée), vous pouvez toujours faire
var r =newRandom();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 dessiner ceci est incluse):
Votre classe d'extension a quelques choses que je recherchais! Merci!
Thomas
1
vous avez un petit bogue dans votre méthode NextGaussian. NextDouble () Renvoie un nombre à virgule flottante aléatoire supérieur ou égal à 0,0 et inférieur à 1,0. Donc, vous devriez avoir u1 = 1.0 - NextDouble () .... autre log (0) va exploser
Mitch Wheat
21
Math.NET fournit cette fonctionnalité. Voici comment:
double mean =100;double stdDev =10;MathNet.Numerics.Distributions.Normal normalDist =newNormal(mean, stdDev);double randomGaussianValue= normalDist.Sample();
Très bonne réponse! Cette fonction est disponible sur NuGet dans le package MathNet.Numerics . Toujours génial de ne pas avoir à rouler le vôtre.
jpmc26
8
J'ai créé une demande pour une telle fonctionnalité sur Microsoft Connect. Si c'est quelque chose que vous recherchez, votez pour cela et augmentez sa visibilité.
Cette fonctionnalité est incluse dans le SDK Java. Son implémentation est disponible dans le cadre de la documentation et est facilement portée en C # ou dans d'autres langages .NET.
Si vous recherchez de la vitesse pure, l' algorithme Zigorat est généralement reconnu comme l'approche la plus rapide.
En attendant, voici un wrapper pour Randomqui fournit une implémentation efficace de la méthode polaire Box Muller:
publicsealedclassGaussianRandom{privatebool _hasDeviate;privatedouble _storedDeviate;privatereadonlyRandom _random;publicGaussianRandom(Random random =null){
_random = random ??newRandom();}/// <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>publicdoubleNextGaussian(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("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 deviatevar polar =Math.Sqrt(-2*Math.Log(rSquared)/rSquared);// store first deviate
_storedDeviate = v2*polar;
_hasDeviate =true;// return second deviatereturn v1*polar*sigma + mu;}}
J'en ai cependant obtenu quelques valeurs. quelqu'un peut-il vérifier ce qui ne va pas?
mk7
@ mk7, une fonction de probabilité gaussienne centrée autour de zéro est tout aussi susceptible de donner des valeurs négatives que de donner des valeurs positives.
Drew Noakes
Vous avez raison! Puisque je voudrais obtenir une liste de poids dans une population typique avec un PDF gaussien, je règle mu à, disons, 75 [en kg] et sigma à 10. Dois-je définir une nouvelle instance de GaussianRandom pour générer chaque poids aléatoire?
mk7
Vous pouvez continuer à dessiner des échantillons à partir d'une seule instance.
Drew Noakes
5
Math.NET Iridium prétend également implémenter "des générateurs aléatoires non uniformes (normal, poisson, binomial, ...)".
Mais ça ne fonctionne pas correctement. J'ai essayé de le tracer, donnant un uniforme aléatoire non.
Nikhil Chilwant
4
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 la distribution de poisson ) simplement en remplaçant la fonction de densité.
staticRandom _rand =newRandom();publicstaticdoubleDraw(){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 functionpublicstaticdouble f(double x,doubleμ=0.5,doubleσ=0.5){return1d/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 laquelle la courbe doit s'inscrire.
Tangenial, mais c'est en fait la première fois que je réalise que vous pouvez utiliser des symboles Unicode pour les variables au lieu de quelque chose de stupide comme _sigma ou _phi ...
Slothario
@Slothario Je remercie les développeurs du monde entier d'avoir utilisé «quelque chose de stupide»: |
user2864740
2
J'aimerais 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 rapide. Ce n'est cependant pas sûr pour les threads.
En développant les réponses de @Noakes et @ Hameer, j'ai également implémenté une classe 'Gaussian', 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 Gaussian 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 considérais pas comme nécessaires puisque 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 à 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.
publicclassGaussian:Random{privatedouble u1;privatedouble u2;privatedouble temp1;privatedouble temp2;publicGaussian(int seed):base(seed){}publicGaussian():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>publicdoubleRandomGauss(double mu =0,double sigma =1){if(sigma <=0)thrownewArgumentOutOfRangeException("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));}}
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:
Ziggurat utilise une table de consultation pour gérer les valeurs qui tombent suffisamment loin de la courbe, qu'il acceptera ou rejettera rapidement. Environ 2,5% du temps, il doit effectuer des calculs supplémentaires pour déterminer de quel côté de la courbe se trouve un nombre.
Vous pouvez essayer Infer.NET. Ce n'est pas encore sous licence commerciale. Voici le lien
C'est 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 d'autres que j'ai laissées de côté.
C'est ma simple implémentation inspirée de Box Muller. Vous pouvez augmenter la résolution en fonction de 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 si nécessaire.
C'est ma simple implémentation inspirée de Box Muller. Vous pouvez augmenter la résolution en fonction de vos besoins. C'est très rapide, simple et fonctionne pour mes applications de réseau neuronal qui ont besoin d'un type approximatif de fonction de densité de probabilité gaussienne pour faire le travail. J'espère que cela aidera quelqu'un à gagner du temps et des cycles CPU. 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 si nécessaire.
Daniel Howard
1
Salut Daniel, j'ai suggéré une modification qui intègre la description de votre commentaire dans la réponse elle-même. Il supprime également le «//» qui commentait le vrai code dans votre réponse. Vous pouvez faire la modification vous-même si vous le souhaitez / si elle est rejetée :)
mbrig
-1
Je ne pense pas qu'il y en ait. Et j'espère vraiment qu'il n'y en a pas, car le cadre est déjà suffisamment gonflé, sans une telle fonctionnalité spécialisée le remplissant encore plus.
Depuis quand la distribution gaussienne est-elle «spécialisée»? C'est beaucoup plus général que, disons, AJAX ou DataTables.
TraumaPony
@TraumaPony: essayez-vous sérieusement de suggérer à plus de développeurs d'utiliser la distribution gaussienne que d'utiliser AJAX sur une base régulière?
David Arno
3
Peut-être; ce que je dis, c'est que c'est beaucoup plus spécialisé. Il n'a qu'une seule utilisation: les applications Web. Les distributions gaussiennes ont un nombre incroyable d'utilisations indépendantes.
TraumaPony
@DavidArno, suggérez-vous sérieusement que moins de fonctionnalités améliorent un cadre.
Jodrell
1
@Jodrell, pour citer un exemple spécifique, je pense que la décision de faire de MVC un framework distinct, plutôt qu'une partie du framework .NET principal, était une bonne décision.
Random.NextDouble()
) dans l'inverse de la CDF de N'IMPORTE QUELLE distribution, vous obtiendrez des nombres aléatoires qui suivent CETTE distribution. Si votre application n'a pas besoin de variables distribuées avec précision, alors la distribution logistique est une approximation très proche de la normale et a un CDF facilement inversible.Random
utilisant la transformation Box-Muller (mentionnée dans plusieurs réponses ci-dessous).Réponses:
La suggestion de Jarrett d'utiliser une transformation Box-Muller est bonne pour une solution rapide et sale. Une implémentation simple:
la source
stdDev
variable doit-elle être définie? Je comprends que cela peut être configuré selon des exigences spécifiques, mais y a-t-il des limites (c'est-à-dire des valeurs max / min)?Cette question semble avoir été déplacée au-dessus de Google pour la génération gaussienne .NET, alors j'ai pensé que je publierais une réponse.
J'ai créé des méthodes d'extension pour la classe .NET Random , 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 référencez la DLL compilée), vous pouvez toujours faire
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 dessiner ceci est incluse):
la source
Math.NET fournit cette fonctionnalité. Voici comment:
Vous pouvez trouver la documentation ici: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
la source
J'ai créé une demande pour une telle fonctionnalité sur Microsoft Connect. Si c'est quelque chose que vous recherchez, votez pour cela 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 en C # ou dans d'autres langages .NET.
Si vous recherchez de la vitesse pure, l' algorithme Zigorat est généralement reconnu comme l'approche la plus rapide.
Je ne suis pas un expert sur ce sujet cependant - je suis tombé sur le besoin de cela 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 de ne pas l'inclure dans le cadre.
En attendant, voici un wrapper pour
Random
qui fournit une implémentation efficace de la méthode polaire Box Muller:la source
Math.NET Iridium prétend également implémenter "des générateurs aléatoires non uniformes (normal, poisson, binomial, ...)".
la source
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 la distribution de poisson ) simplement en remplaçant la fonction de densité.
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 laquelle la courbe doit s'inscrire.
la source
J'aimerais 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 rapide. Ce n'est cependant pas sûr pour les threads.
la source
En développant les réponses de @Noakes et @ Hameer, j'ai également implémenté une classe 'Gaussian', 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 Gaussian 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 considérais pas comme nécessaires puisque 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 à 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.
la source
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 consultation pour gérer les valeurs qui tombent suffisamment loin de la courbe, qu'il acceptera ou rejettera rapidement. Environ 2,5% du temps, il doit effectuer des calculs supplémentaires pour déterminer de quel côté de la courbe se trouve un nombre.
la source
Vous pouvez essayer Infer.NET. Ce n'est pas encore sous licence commerciale. Voici le lien
C'est 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 d'autres que j'ai laissées de côté.
Cela peut accomplir ce que vous voulez. Merci.
la source
C'est ma simple implémentation inspirée de Box Muller. Vous pouvez augmenter la résolution en fonction de 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 si nécessaire.
la source
Je ne pense pas qu'il y en ait. Et j'espère vraiment qu'il n'y en a pas, car le cadre est déjà suffisamment gonflé, sans une telle fonctionnalité spécialisée le remplissant encore plus.
Jetez un œil à http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx et http://www.vbforums.com/showthread.php?t=488959 pour une solution tierce .NET.
la source