.NET标准库中是否有一个类可以为我提供创建遵循高斯分布的随机变量的功能?
Random.NextDouble()
)到ANY分布的CDF的倒数中,您将获得遵循THAT分布的随机数。如果您的应用程序不需要精确的正态分布变量,则Logistic分布与正态分布非常接近,并且具有易于反转的CDF。
Random
使用框-穆勒变换(在下文若干答案中提到)。
.NET标准库中是否有一个类可以为我提供创建遵循高斯分布的随机变量的功能?
Random.NextDouble()
)到ANY分布的CDF的倒数中,您将获得遵循THAT分布的随机数。如果您的应用程序不需要精确的正态分布变量,则Logistic分布与正态分布非常接近,并且具有易于反转的CDF。
Random
使用框-穆勒变换(在下文若干答案中提到)。
Answers:
Jarrett建议使用Box-Muller变换非常适合快速而又肮脏的解决方案。一个简单的实现:
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)
stdDev
变量应设置为什么?我知道可以将其配置为特定要求,但是是否有任何界限(即最大/最小值)?
这个问题似乎已经超越了Google的.NET高斯一代,所以我想我会发布一个答案。
我为.NET Random类提供了一些扩展方法,包括Box-Muller转换的实现。由于它们是扩展名,因此只要包含项目(或引用已编译的DLL),您仍然可以
var r = new Random();
var x = r.NextGaussian();
希望没人介意这个无耻的插件。
结果样本直方图(包括用于绘制此结果的演示应用程序):
Math.NET提供了此功能。这是如何做:
double mean = 100;
double stdDev = 10;
MathNet.Numerics.Distributions.Normal normalDist = new Normal(mean, stdDev);
double randomGaussianValue= normalDist.Sample();
您可以在此处找到文档:http : //numerics.mathdotnet.com/api/MathNet.Numerics.Distributions/Normal.htm
我在Microsoft Connect上创建了对此功能的请求。如果这是您要找的东西,请投票并提高其知名度。
此功能包含在Java SDK中。它的实现作为文档的一部分提供,并且可以轻松移植到C#或其他.NET语言。
如果您正在寻找纯速度,那么Zigorat算法通常被认为是最快的方法。
我不是这个主题的专家,但是在为我的RoboCup 3D模拟机器人足球库实现粒子过滤器时遇到了对此的需求,当它没有包含在框架中时,我感到很惊讶。
同时,这里的包装器Random
提供了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;
}
}
这是用于生成正态分布的随机变量的另一个快速而肮脏的解决方案。它绘制一个随机点(x,y)并检查该点是否在概率密度函数的曲线下,否则重复。
奖励:您可以通过替换密度函数,为任何其他分布(例如指数分布或泊松分布)生成随机变量。
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 * σ * σ));
}
重要提示:选择y的间隔以及参数σ和μ,以使函数的曲线在最大/最小点处(例如x =平均值)不被截断。将x和y的间隔视为一个边界框,曲线必须适合该边界框。
我想通过加快@yoyoyoyosef的答案并编写包装器类来扩展它的答案。产生的开销可能并不意味着要快两倍,但我认为应该几乎快一倍。但是,它不是线程安全的。
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();
}
}
扩展了@Noakes和@Hameer的答案,我还实现了一个'Gaussian'类,但是为了简化内存空间,我将其设为Random类的子类,以便您也可以调用基本的Next(),NextDouble() ,也无需创建额外的Random对象来处理它。我还消除了_available和_nextgauss全局类属性,因为由于此类是基于实例的,所以我不认为它们是必需的,如果给每个线程分配自己的高斯对象,则它应该是线程安全的。我还将所有运行时分配的变量移出了函数,并使其成为类属性,这将减少对内存管理器的调用次数,因为从理论上讲,在对象被销毁之前,永远不应该取消分配4个双打。
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));
}
}
扩展Drew Noakes的答案,如果您需要比Box-Muller更好的性能(快50-75%),Colin Green已在C#中共享了Ziggurat算法的实现,您可以在这里找到:
http://heliosphan.org/zigguratalgorithm/zigguratalgorithm.html
Ziggurat使用查找表来处理与曲线相距足够远的值,它将迅速接受或拒绝该曲线。大约有2.5%的时间,它必须做进一步的计算才能确定数字在曲线的哪一边。
这是我受Box Muller启发的简单实现。您可以提高分辨率以满足您的需求。尽管这对我来说效果很好,但是这是一个有限范围的近似值,因此请记住尾部是闭合且有限的,但是可以确定的是您可以根据需要扩展它们。
//
// 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;
}
}
我认为没有。我真的希望没有,因为框架已经足够膨胀,而没有更多的专用功能。
请查看http://www.extremeoptimization.com/Statistics/UsersGuide/ContinuousDistributions/NormalDistribution.aspx和http://www.vbforums.com/showthread.php?t=488959,以获取第三方.NET解决方案。