2008-10-20 13 views
84

¿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?Variables gaussianas aleatorias

+0

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 de la CDF de CUALQUIER distribución, obtendrá números aleatorios que siguen esa distribución. Si su aplicación no necesita precisamente variables distribuidas normalmente, entonces la Distribución Logística es una aproximación muy cercana a la normal y tiene una CDF fácilmente invertible. – Ozzah

+1

El [paquete MedallionRandom NuGet] (https://github.com/madelson/MedallionUtilities/tree/master/MedallionRandom) contiene un método de extensión para recuperar valores distribuidos normalmente de un 'Random' utilizando la transformación Box-Muller (mencionado en varias respuestas a continuación). – ChaseMedallion

Respuesta

143

La sugerencia de Jarrett de utilizar una transformación Box-Muller es buena para una solución rápida y sucia. Una aplicación sencilla:

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) 
+1

Lo probé y 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"). –

+3

@Johann, si está buscando velocidad pura, entonces el [Algoritmo de Zigorat] (http://en.wikipedia.org/wiki/Ziggurat_algorithm) generalmente se reconoce como el enfoque más rápido. Además, el enfoque anterior puede hacerse más rápido llevando un valor de una llamada a la siguiente. –

+6

Consulte la [Implementación de Java] (http://download.oracle.com/javase/1.4.2/docs/api/java/util/Random.html#nextGaussian%28%29) para ver un ejemplo de cómo transportar valores entre llamadas . También evita la necesidad de operaciones trigonométricas. –

-1

No creo que exista. Y realmente espero que no exista, ya que el marco ya está lo suficientemente hinchado, sin que dicha funcionalidad especializada lo llene aún más.

Eche un vistazo a http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx y http://www.vbforums.com/showthread.php?t=488959 para soluciones de .NET de terceros.

+7

¿Desde cuándo la distribución gaussiana es 'especializada'? Es mucho más general que, por ejemplo, AJAX o DataTables. – TraumaPony

+0

@TraumaPony: ¿está tratando seriamente de sugerir que más desarrolladores usen la distribución gaussiana que usar AJAX de forma regular? –

+3

Posiblemente; lo que digo es que es mucho más especializado. Solo tiene un uso: aplicaciones web. Las distribuciones gaussianas tienen una cantidad increíble de usos no relacionados. – TraumaPony

5

Math.NET Iridium también reclama para implementar "generadores aleatorios no uniformes (normal, de Poisson, binomial, ...)".

+0

BUt no funciona correctamente. Intentó trazarlo, dando un uniforme no aleatorio. –

6

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 característica se incluye en el SDK de Java. Su implementación está disponible as part of the documentation y se puede portar fácilmente a C# u otros lenguajes .NET.

Si está buscando velocidad pura, entonces el Zigorat Algorithm es generalmente reconocido como el enfoque más rápido.

No soy un experto en este tema, sin embargo, me encontré con la necesidad de esto al implementar un particle filter para mi RoboCup 3D simulated robotic soccer library y me sorprendió cuando esto no estaba incluido en el marco.


Mientras tanto, aquí hay un contenedor para Random que proporciona una implementación eficiente del método polar Caja 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; 
    } 
} 
+0

Aunque obtuve algunos -ve valores. ¿alguien puede verificar lo que está mal? – mk7

+0

@ mk7, una función de probabilidad gaussiana centrada alrededor de cero tiene la misma probabilidad de dar valores negativos que de valores positivos. –

+0

¡Tienes razón! Como me gustaría obtener una lista de peso en una población típica con PDF gaussiano, estoy configurando mu a, digamos, 75 [en kg] y sigma a 10.¿Debo establecer una nueva instancia de GaussianRandom para generar cada peso aleatorio? – mk7

0

Usted podría intentar Infer.NET. Sin embargo, todavía no tiene licencia comercial. Aquí está link

Es un marco probabilístico para .NET desarrolló mi investigación de Microsoft. Tienen tipos de .NET para distribuciones de Bernoulli, Beta, Gamma, Gaussian, Poisson, y probablemente algunos más que omití.

Puede lograr lo que desee. Gracias.

43

Esta pregunta parece haberse movido en la parte superior de Google para la generación de Gauss .NET, por lo que pensé que había puesto una respuesta.

He hecho algunos extension methods for the .NET Random class, incluida una implementación de la transformación Box-Muller. Ya que son extensiones, por lo que siempre que se incluya el proyecto (o hace referencia a la DLL compilada), todavía se puede hacer

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

esperanza a nadie le importa el enchufe descarado.

histograma de la muestra de los resultados (una aplicación de demostración para dibujar esto está incluido):

enter image description here

+0

¡Su clase de extensión tiene algunas cosas que estaba buscando! ¡Gracias! – Thomas

+0

tiene un pequeño error en su método NextGaussian. NextDouble() Devuelve un número aleatorio de punto flotante que es mayor o igual que 0.0 y menor que 1.0. Entonces debería tener u1 = 1.0 - NextDouble() .... other log (0) explotará –

2

me gustaría ampliar la respuesta de @ yoyoyoyosef por lo que es aún más rápido, y escribir una clase contenedora. La sobrecarga incurrida puede 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.

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

Esta es mi sencilla implementación inspirada en Box Muller. Puede aumentar la resolución para adaptarse a sus necesidades. Aunque esto funciona bien para mí, esta es una aproximación de rango limitado, así que ten en cuenta que las colas son cerradas y finitas, pero ciertamente puedes expandirlas según sea necesario.

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

Esta es mi implementación inspirada en Box Muller. Puede aumentar la resolución para adaptarse a sus necesidades. Esto es muy rápido, simple y funciona para mis aplicaciones de redes neuronales que necesitan un tipo de función de densidad de probabilidad Gaussiana aproximada para realizar el trabajo. Espero que ayude a alguien a ahorrar tiempo y ciclos de CPU. Aunque esto funciona bien para mí, esta es una aproximación de rango limitado, así que ten en cuenta que las colas son cerradas y finitas, pero ciertamente puedes expandirlas según sea necesario. –

+1

Hola, Daniel, he sugerido una edición que incorpore la descripción de tu comentario en la respuesta misma. También elimina el '//' que estaba comentando el código real en su respuesta. Puede editar usted mismo si lo desea/si se rechaza :) – mbrig

1

Ampliando la respuesta de Drew Noakes, si necesita un mejor rendimiento que Box-Muller (en torno al 50-75% más rápido), Colin Green ha compartido una implementación del algoritmo zigurat en C#, que se puede encontrar aquí:

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

Ziggurat utiliza una tabla de búsqueda para manejar los valores que caen suficientemente lejos de la curva, que se va a aceptar o rechazar rápidamente. Alrededor del 2.5% del tiempo, tiene que hacer más cálculos para determinar en qué lado de la curva se encuentra un número.

1

Aquí hay otra solución rápida y sucia para generar variables aleatorias que son normal distributed. Dibuja un punto aleatorio (x, y) y comprueba si este punto se encuentra debajo de la curva de la función de densidad de probabilidad; de lo contrario, repita.

Bonificación: Puede generar variables aleatorias para cualquier otra distribución (por ejemplo, exponential distribution o poisson distribution) simplemente reemplazando la función de densidad.

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

Importante: Seleccione el intervalo de y y los parámetros σ y μ modo que la curva de la función no es de corte en él es el 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.

0

La expansión fuera de @Noakes y @ respuestas de Hameer, también he implementado una clase 'Gauss', pero para simplificar el espacio de la memoria, lo hice un hijo de la clase Random para que también pueda llamar al Next(), NextDouble(), etc. básico de la clase Gaussian sin tener que crear un objeto Random adicional para manejarlo. También eliminé las propiedades de clase global _available y _nextgauss, ya que no las consideré 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 eliminé todas las variables asignadas en tiempo de ejecución de la función y las convertí en propiedades de clase, esto reducirá el número de llamadas al administrador de memoria ya que los 4 dobles nunca deberían desasignarse teóricamente hasta que se destruya el objeto.

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