Pregunta

¿Hay una clase en la biblioteca estándar de .NET que me brinde la funcionalidad para crear variables aleatorias que sigan la distribución gaussiana?

¿Fue útil?

Solución

La sugerencia de Jarrett de usar una transformada de Box-Muller es buena para una solución rápida y sucia. Una implementación 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)

Otros consejos

Esta pregunta parece haberse movido sobre Google para la generación Gaussiana .NET, así que pensé que publicaría una respuesta.

He creado algunos métodos de extensión para la clase aleatoria .NET , incluida una implementación de La transformada de Box-Muller. Como son extensiones, siempre que el proyecto esté incluido (o haga referencia a la DLL compilada), aún puede hacer

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

Espero que a nadie le importe el descarado enchufe.

Ejemplo de histograma de resultados (se incluye una aplicación de demostración para dibujar esto):

ingrese la descripción de la imagen aquí

Math.NET proporciona esta funcionalidad. Aquí es cómo:

double mean = 100;
double stdDev = 10;

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

Puedes encontrar documentación aquí: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm

Creé una solicitud para dicha función en Microsoft Connect. Si esto es algo que está buscando, vote y aumente su visibilidad.

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

Esta característica está incluida en el SDK de Java. Su implementación está disponible como parte de La documentación y es fácil de portar a C # u otros lenguajes .NET.

Si está buscando velocidad pura, entonces el Algoritmo de Zigorat generalmente se reconoce como el enfoque más rápido.

Sin embargo, no soy un experto en este tema. Encontré la necesidad de esto al implementar un filtro de partículas para mi biblioteca de fútbol robótica simulada RoboCup 3D 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 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 también pretende implementar " generadores aleatorios no uniformes (normal, poisson, binomial , ...) " ;.

Aquí hay otra solución rápida y sucia para generar variables aleatorias que son normal distribuido . Dibuja un punto aleatorio (x, y) y comprueba 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 distribución de poisson ) 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 & # 963; y & # 956; para que la curva de la función no está cortado 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 debe encajar la curva.

Me gustaría ampliar la respuesta de @yoyoyoyosef al hacerlo aún más rápido y escribir una clase de envoltorio. Los gastos generales incurridos pueden no significar el doble de rápido, pero creo que debería ser casi dos veces más 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();
    }
}

Ampliando la respuesta de Drew Noakes, si necesita un mejor rendimiento que Box-Muller (alrededor del 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 usa una tabla de búsqueda para manejar valores que caen lo suficientemente lejos de la curva, que 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.

Ampliando las respuestas de @Noakes y @ Hameer, también he implementado una clase 'Gaussiana', pero para simplificar el espacio de memoria, lo convertí en un hijo de la clase Aleatoria para que también puedas llamar al Siguiente básico (), NextDouble (), etc. de la clase gaussiana también 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á el número de llamadas al administrador de memoria ya que los 4 dobles en teoría nunca deberían ser desasignados hasta que el objeto sea destruido.

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

Puedes probar Infer.NET. Sin embargo, aún no tiene licencia comercial. Aquí está enlace

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

Puede lograr lo que quieras. Gracias.

Esta es mi implementación sencilla inspirada en Box Muller. Puede aumentar la resolución para satisfacer 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.

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

No creo que haya. Y realmente espero que no, ya que el marco ya está lo suficientemente hinchado, sin una funcionalidad tan especializada que 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top