c# - Variables gaussianas aleatorias
.net random (13)
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 de Ziggurat en C #, que puede encontrar aquí:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Ziggurat usa una tabla de búsqueda para manejar los valores que están lo suficientemente alejados 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.
¿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?
Ampliando las respuestas de @Noakes y @ Hameer, también implementé una clase ''Gaussian'', pero para simplificar el espacio de la memoria, hice que fuera un elemento secundario de la clase Random para que también pueda llamar a Next (), NextDouble () básico. , etc. de la clase Gaussian también 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));
}
}
Aquí hay otra solución rápida y sucia para generar variables aleatorias que están distribuidas normalmente . 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, 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 σ y μ para que la curva de la función no se corte en sus puntos máximos / mínimos (por ejemplo, en x = media). Piense en los intervalos de xey como un cuadro delimitador, en el que la curva debe encajar.
Creé una solicitud para dicha función en Microsoft Connect. Si esto es algo que está buscando, vote por él y aumente su visibilidad.
Esta característica está incluida en el SDK de Java. Su implementación está disponible como parte de la documentación y se puede portar fácilmente a C # u otros lenguajes .NET.
Si está buscando velocidad pura, entonces el algoritmo Zigorat generalmente se reconoce como el enfoque más rápido.
Aunque no soy un experto en este tema, encontré la necesidad de esto mientras implementaba 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 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;
}
}
Esta es mi 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;
}
}
Esta pregunta parece haberse movido sobre Google para generar .NET Gaussian, así que pensé en publicar una respuesta.
He hecho algunos métodos de extensión para la clase .NET Random , incluida una implementación de la transformación Box-Muller. Como son extensiones, siempre que se incluya el proyecto (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 enchufe desvergonzado.
Ejemplo de histograma de resultados (se incluye una aplicación de demostración para dibujar esto):
La sugerencia de Jarrett de usar una transformada 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)
Me gustaría expandir la respuesta de @yoyoyoyosef haciéndolo aún más rápido y escribiendo 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();
}
}
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.
Podrías probar Infer.NET. Sin embargo, todavía no tiene licencia comercial. Aquí hay un 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 quieras. Gracias.
Math.NET Iridium también afirma implementar "generadores aleatorios no uniformes (normal, poisson, binomial, ...)".
http://mathworld.wolfram.com/Box-MullerTransformation.html
Usando dos variables aleatorias, puede generar valores aleatorios a lo largo de una distribución gaussiana. No es una tarea difícil en absoluto.
Math.NET proporciona esta funcionalidad. Así es cómo:
double mean = 100;
double stdDev = 10;
MathNet.Numerics.Distributions.Normal normalDist = new Normal(mean, stdDev);
double randomGaussianValue= normalDist.Sample();
Puede encontrar documentación aquí: http://numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm