¿Existe una clase en la biblioteca estándar de .NET que me brinde la funcionalidad para crear variables aleatorias que sigan la distribución gaussiana?
Solo me gustaría agregar un resultado matemático que no es inmediatamente útil para las distribuciones normales (debido a la compleja CDF), pero es útil para muchas otras distribuciones. Si coloca números aleatorios distribuidos uniformemente en [0,1] (con Random.NextDouble()) en el inverso del CDF de CUALQUIER distribución, obtendrá números aleatorios que siguen ESA distribución. Si su aplicación no necesita variables exactamente distribuidas normalmente, entonces la Distribución Logística es una aproximación muy cercana a la normal y tiene un CDF fácilmente invertible.
Ozzah
1
El paquete MedallionRandom NuGet contiene un método de extensión para recuperar valores distribuidos normalmente de un Randomuso de la transformación Box-Muller (mencionado en varias respuestas a continuación).
ChaseMedallion
Respuestas:
181
La sugerencia de Jarrett de usar una transformación Box-Muller es buena para una solución rápida y sucia. Una implementación 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)
Lo probé y lo comparé con Mersenne Twister RNG y NormalDistribution de MathNet. Su versión es más del doble de rápida y el resultado final es básicamente el mismo (inspección visual de las "campanas").
Johann Gerell
4
@Johann, si está buscando velocidad pura, el algoritmo Zigorat generalmente se reconoce como el enfoque más rápido. Además, el enfoque anterior se puede hacer más rápido llevando un valor de una llamada a la siguiente.
Drew Noakes
Hola, ¿en qué debería establecerse la stdDevvariable? Entiendo que esto se puede configurar para requisitos específicos, pero ¿hay algún límite (es decir, valores máximo / mínimo)?
hofnarwillie
@hofnarwillie stdDev es el parámetro de escala de la distribución normal, que puede ser cualquier número positivo. Cuanto más grande sea, más dispersos serán los números generados. Para una distribución normal estándar, use los parámetros mean = 0 y stdDev = 1.
yoyoyoyosef
1
@Jack No lo creo. Solo el -2 * Math.Log (u1) está dentro de la raíz cuadrada, y el registro siempre será negativo o cero desde u1 <= 1
yoyoyoyosef
62
Esta pregunta parece haberse movido sobre Google para la generación gaussiana de .NET, así que pensé que publicaría una respuesta.
He creado algunos métodos de extensión para la clase .NET Random , incluida una implementación de la transformación Box-Muller. Dado que son extensiones, siempre que el proyecto esté incluido (o haga referencia a la DLL compilada), aún puede hacer
var r =newRandom();var x = r.NextGaussian();
Espero que a nadie le importe el desvergonzado enchufe.
Ejemplo de histograma de resultados (se incluye una aplicación de demostración para dibujar esto):
¡Tu clase de extensión tiene algunas cosas que estaba buscando! ¡Gracias!
Thomas
1
tiene un pequeño error en su método NextGaussian. NextDouble () Devuelve un número de punto flotante aleatorio que es mayor o igual que 0.0 y menor que 1.0. Entonces debería tener u1 = 1.0 - NextDouble () .... otro log (0) explotará
Mitch Wheat
21
Math.NET proporciona esta funcionalidad. Así es cómo:
double mean =100;double stdDev =10;MathNet.Numerics.Distributions.Normal normalDist =newNormal(mean, stdDev);double randomGaussianValue= normalDist.Sample();
Esta función está incluida en el SDK de Java. Su implementación está disponible como parte de la documentación y se traslada fácilmente a C # u otros lenguajes .NET.
Si busca velocidad pura, el algoritmo Zigorat generalmente se reconoce como el enfoque más rápido.
Mientras tanto, aquí hay una envoltura Randomque proporciona una implementación eficiente del método polar de 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;}}
Sin embargo, obtuve algunos valores -ve. ¿Alguien puede comprobar lo que está mal?
mk7
@ mk7, una función de probabilidad gaussiana centrada alrededor de cero es tan probable que dé valores negativos como positivos.
Drew Noakes
¡Tienes razón! Como me gustaría obtener una lista de peso en una población típica con PDF gaussiano, establezco mu en, digamos, 75 [en kg] y sigma en 10. ¿Necesito establecer una nueva instancia de GaussianRandom para generar cada peso aleatorio?
mk7
Puede seguir dibujando muestras de una instancia.
Drew Noakes
5
Math.NET Iridium también pretende implementar "generadores aleatorios no uniformes (normal, poisson, binomial, ...)".
Pero no está funcionando correctamente. Intenté trazarlo, dando uniforme aleatorio no.
Nikhil Chilwant
4
Aquí hay otra solución rápida y sucia para generar variables aleatorias que tienen una distribución normal . Dibuja algún punto aleatorio (x, y) y verifica si este punto se encuentra debajo de la curva de su función de densidad de probabilidad; de lo contrario, repita.
Bonificación: puede generar variables aleatorias para cualquier otra distribución (por ejemplo, la distribución exponencial o la distribución de Poisson ) simplemente reemplazando la función de densidad.
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*σ*σ));}
Importante: Seleccione el intervalo de y y los parámetros σ y μ para que la curva de la función no se corte en sus puntos máximo / mínimo (por ejemplo, en x = media). Piense en los intervalos de x y y como un cuadro delimitador, en el que la curva debe encajar.
Tangenial, pero esta es la primera vez que me doy cuenta de que puedes usar símbolos Unicode para variables en lugar de algo tonto como _sigma o _phi ...
Slothario
@Slothario Agradezco a los desarrolladores de todo el mundo por usar 'algo tonto': |
user2864740
2
Me gustaría ampliar la respuesta de @ yoyoyoyosef haciéndola aún más rápida y escribiendo una clase contenedora. Los gastos generales incurridos pueden no significar el doble de rápido, pero creo que debería ser casi el doble de rápido. Sin embargo, no es seguro para subprocesos.
Expandiéndome de las respuestas de @Noakes y @ Hameer, también he implementado una clase 'Gaussiana', pero para simplificar el espacio de memoria, la convertí en un elemento secundario de la clase Random para que también pueda llamar al básico Next (), NextDouble () , etc. de la clase Gaussiana sin tener que crear un objeto aleatorio adicional para manejarlo. También eliminé las propiedades de clase global _available y _nextgauss, ya que no las vi como necesarias ya que esta clase está basada en instancias, debería ser segura para subprocesos, si le da a cada subproceso su propio objeto gaussiano. También moví todas las variables asignadas en tiempo de ejecución fuera de la función y las convertí en propiedades de clase, esto reducirá la cantidad de llamadas al administrador de memoria, ya que teóricamente los 4 dobles nunca deberían desasignarse hasta que se destruya el objeto.
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));}}
Ampliando la respuesta de Drew Noakes, si necesita un mejor rendimiento que Box-Muller (alrededor de un 50-75% más rápido), Colin Green ha compartido una implementación del algoritmo Ziggurat en C #, que puede encontrar aquí:
Ziggurat utiliza una tabla de búsqueda para manejar valores que caen lo suficientemente lejos de la curva, que rápidamente aceptará o rechazará. Aproximadamente el 2,5% del tiempo, tiene que hacer más cálculos para determinar en qué lado de la curva se encuentra un número.
Puede probar Infer.NET. Sin embargo, aún no tiene licencia comercial. Aquí hay un enlace
Es un marco probabilístico para .NET desarrollado por mi investigación en Microsoft. Tienen tipos .NET para distribuciones de Bernoulli, Beta, Gamma, Gaussian, Poisson y probablemente algunos más los dejé fuera.
Esta es mi sencilla implementación inspirada en Box Muller. Puede aumentar la resolución para adaptarla a sus necesidades. Aunque esto funciona muy bien para mí, esta es una aproximación de rango limitado, así que tenga en cuenta que las colas son cerradas y finitas, pero ciertamente puede expandirlas según sea necesario.
Esta es mi sencilla implementación inspirada en Box Muller. Puede aumentar la resolución para adaptarla a sus necesidades. Esto es muy rápido, simple y funciona para mis aplicaciones de red neuronal que necesitan un tipo aproximado de función de densidad de probabilidad gaussiana para hacer el trabajo. Espero que ayude a alguien a ahorrar tiempo y ciclos de CPU. Aunque esto funciona muy bien para mí, esta es una aproximación de rango limitado, así que tenga en cuenta que las colas son cerradas y finitas, pero ciertamente puede expandirlas según sea necesario.
Daniel Howard
1
Hola Daniel, sugerí una edición que incorpora la descripción de tu comentario en la respuesta. También elimina el '//' que estaba comentando el código real en su respuesta. Puede hacer la edición usted mismo si lo desea / si se rechaza :)
mbrig
-1
No creo que lo haya. Y realmente espero que no lo haya, ya que el marco ya está lo suficientemente inflado, sin una funcionalidad tan especializada que lo llene aún más.
¿Desde cuándo la distribución gaussiana es "especializada"? Es mucho más general que, digamos, AJAX o DataTables.
TraumaPony
@TraumaPony: ¿estás intentando sugerir seriamente que más desarrolladores usen la distribución gaussiana que AJAX de forma regular?
David Arno
3
Posiblemente; lo que digo es que es mucho más especializado. Solo tiene un uso: aplicaciones web. Las distribuciones gaussianas tienen una increíble cantidad de usos no relacionados.
TraumaPony
@DavidArno, ¿estás sugiriendo en serio que una menor funcionalidad mejora un marco?
Jodrell
1
@Jodrell, para citar un ejemplo específico, creo que la decisión de hacer de MVC un marco separado, en lugar de parte del marco principal .NET, fue una buena decisión.
Random.NextDouble()
) en el inverso del CDF de CUALQUIER distribución, obtendrá números aleatorios que siguen ESA distribución. Si su aplicación no necesita variables exactamente distribuidas normalmente, entonces la Distribución Logística es una aproximación muy cercana a la normal y tiene un CDF fácilmente invertible.Random
uso de la transformación Box-Muller (mencionado en varias respuestas a continuación).Respuestas:
La sugerencia de Jarrett de usar una transformación Box-Muller es buena para una solución rápida y sucia. Una implementación simple:
fuente
stdDev
variable? Entiendo que esto se puede configurar para requisitos específicos, pero ¿hay algún límite (es decir, valores máximo / mínimo)?Esta pregunta parece haberse movido sobre Google para la generación gaussiana de .NET, así que pensé que publicaría una respuesta.
He creado algunos métodos de extensión para la clase .NET Random , incluida una implementación de la transformación Box-Muller. Dado que son extensiones, siempre que el proyecto esté incluido (o haga referencia a la DLL compilada), aún puede hacer
Espero que a nadie le importe el desvergonzado enchufe.
Ejemplo de histograma de resultados (se incluye una aplicación de demostración para dibujar esto):
fuente
Math.NET proporciona esta funcionalidad. Así es cómo:
Puede encontrar documentación aquí: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
fuente
Creé una solicitud para dicha función en Microsoft Connect. Si esto es algo que está buscando, vote por él y aumente su visibilidad.
https://connect.microsoft.com/VisualStudio/feedback/details/634346/guassian-normal-distribution-random-numbers
Esta función está incluida en el SDK de Java. Su implementación está disponible como parte de la documentación y se traslada fácilmente a C # u otros lenguajes .NET.
Si busca velocidad pura, el algoritmo Zigorat generalmente se reconoce como el enfoque más rápido.
Sin embargo, no soy un experto en este tema; me encontré con la necesidad de esto al implementar un filtro de partículas para mi biblioteca de fútbol robótico simulado RoboCup 3D y me sorprendió cuando esto no se incluyó en el marco.
Mientras tanto, aquí hay una envoltura
Random
que proporciona una implementación eficiente del método polar de Box Muller:fuente
Math.NET Iridium también pretende implementar "generadores aleatorios no uniformes (normal, poisson, binomial, ...)".
fuente
Aquí hay otra solución rápida y sucia para generar variables aleatorias que tienen una distribución normal . Dibuja algún punto aleatorio (x, y) y verifica si este punto se encuentra debajo de la curva de su función de densidad de probabilidad; de lo contrario, repita.
Bonificación: puede generar variables aleatorias para cualquier otra distribución (por ejemplo, la distribución exponencial o la distribución de Poisson ) simplemente reemplazando la función de densidad.
Importante: Seleccione el intervalo de y y los parámetros σ y μ para que la curva de la función no se corte en sus puntos máximo / mínimo (por ejemplo, en x = media). Piense en los intervalos de x y y como un cuadro delimitador, en el que la curva debe encajar.
fuente
Me gustaría ampliar la respuesta de @ yoyoyoyosef haciéndola aún más rápida y escribiendo una clase contenedora. Los gastos generales incurridos pueden no significar el doble de rápido, pero creo que debería ser casi el doble de rápido. Sin embargo, no es seguro para subprocesos.
fuente
Expandiéndome de las respuestas de @Noakes y @ Hameer, también he implementado una clase 'Gaussiana', pero para simplificar el espacio de memoria, la convertí en un elemento secundario de la clase Random para que también pueda llamar al básico Next (), NextDouble () , etc. de la clase Gaussiana sin tener que crear un objeto aleatorio adicional para manejarlo. También eliminé las propiedades de clase global _available y _nextgauss, ya que no las vi como necesarias ya que esta clase está basada en instancias, debería ser segura para subprocesos, si le da a cada subproceso su propio objeto gaussiano. También moví todas las variables asignadas en tiempo de ejecución fuera de la función y las convertí en propiedades de clase, esto reducirá la cantidad de llamadas al administrador de memoria, ya que teóricamente los 4 dobles nunca deberían desasignarse hasta que se destruya el objeto.
fuente
Ampliando la respuesta de Drew Noakes, si necesita un mejor rendimiento que Box-Muller (alrededor de un 50-75% más rápido), Colin Green ha compartido una implementación del algoritmo Ziggurat en C #, que puede encontrar aquí:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Ziggurat utiliza una tabla de búsqueda para manejar valores que caen lo suficientemente lejos de la curva, que rápidamente aceptará o rechazará. Aproximadamente el 2,5% del tiempo, tiene que hacer más cálculos para determinar en qué lado de la curva se encuentra un número.
fuente
Puede probar Infer.NET. Sin embargo, aún no tiene licencia comercial. Aquí hay un enlace
Es un marco probabilístico para .NET desarrollado por mi investigación en Microsoft. Tienen tipos .NET para distribuciones de Bernoulli, Beta, Gamma, Gaussian, Poisson y probablemente algunos más los dejé fuera.
Puede lograr lo que desea. Gracias.
fuente
Esta es mi sencilla implementación inspirada en Box Muller. Puede aumentar la resolución para adaptarla a sus necesidades. Aunque esto funciona muy bien para mí, esta es una aproximación de rango limitado, así que tenga en cuenta que las colas son cerradas y finitas, pero ciertamente puede expandirlas según sea necesario.
fuente
No creo que lo haya. Y realmente espero que no lo haya, ya que el marco ya está lo suficientemente inflado, sin una funcionalidad tan especializada que lo llene aún más.
Sin embargo, eche un vistazo a http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx y http://www.vbforums.com/showthread.php?t=488959 para obtener soluciones .NET de terceros.
fuente