在高斯范围内生成随机数?

7

我希望使用一个随机数生成器,它可以创建指定中位数的高斯分布的随机数。我已经在这里提出了类似的问题,并且现在我正在使用以下代码:

class RandomGaussian
    {

        private static Random random = new Random();
        private static bool haveNextNextGaussian;
        private static double nextNextGaussian;

        public static double gaussianInRange(double from, double mean, double to)
        {
            if (!(from < mean && mean < to))
                throw new ArgumentOutOfRangeException();

            int p = Convert.ToInt32(random.NextDouble() * 100);
            double retval;
            if (p < (mean * Math.Abs(from - to)))
            {
                double interval1 = (NextGaussian() * (mean - from));
                retval = from + (float)(interval1);
            }
            else
            {
                double interval2 = (NextGaussian() * (to - mean));
                retval = mean + (float)(interval2);
            }
            while (retval < from || retval > to)
            {
                if (retval < from)
                    retval = (from - retval) + from;
                if (retval > to)
                    retval = to - (retval - to);
            }
            return retval;
        }

        private static double NextGaussian()
        {
            if (haveNextNextGaussian)
            {
                haveNextNextGaussian = false;
                return nextNextGaussian;
            }
            else
            {
                double v1, v2, s;
                do
                {
                    v1 = 2 * random.NextDouble() - 1;
                    v2 = 2 * random.NextDouble() - 1;
                    s = v1 * v1 + v2 * v2;
                } while (s >= 1 || s == 0);
                double multiplier = Math.Sqrt(-2 * Math.Log(s) / s);
                nextNextGaussian = v2 * multiplier;
                haveNextNextGaussian = true;
                return v1 * multiplier;
            }
        }
    }

然后,为了验证结果,我使用高斯分布函数gaussianInRange(0, 0.5, 1)绘制了n=100000000的图表。 enter image description here 从图中可以看出,中位数确实在0.5,但是没有看到曲线。那么我做错了什么呢?
编辑
我想要的是像这样的东西,我可以通过传递一个值来设置最高概率。 enter image description here

3
你说中位数,但代码使用的是平均数。你说高斯分布(可以取任意实数值),但又将其限制在有限范围内。我认为,在我能帮助你处理代码之前,我需要知道规格说明是什么。 - David Heffernan
http://www.taygeta.com/random/gaussian.html - Mitch Wheat
http://www.bearcave.com/misl/misl_tech/wavelets/hurst/random.html - Mitch Wheat
@Rofl 我们怎么知道呢?你没有告诉我们你的问题是什么。 - David Heffernan
@David Heffernan 我更新了问题 - anon
显示剩余4条评论
2个回答

3
最简单的在特定范围内绘制正态分布的方法是使用拒绝抽样:
do {
    retval = NextGaussian() * stdev + mean;
} while (retval < from || to < retval);

当你在无条件正态生成器中画一个坐标(v1v2)的时候,使用相同的方法。
简单地折叠范围之外的值并不能产生相同的分布。
此外,如果您有良好的误差函数和其逆的实现,您可以使用逆CDF直接计算值。正态分布的CDF为:
F(retval) = (1 + erf((retval-mean) / (stdev*sqrt(2)))) / 2

一个被截断分布的累积分布函数是
C(retval) = (F(retval) - F(from)) / (F(to) - F(from)), from ≤ x < to

使用累积分布函数绘制随机数,您需要从[0,1]上的均匀分布中抽取v并解决C(retval) = v。这将得到
double v = random.NextDouble();
double t1 = erf((from - mean) / (stdev*sqrt(2)));
       t2 = erf((to   - mean) / (stdev*sqrt(2)));
double retval = mean + stdev * sqrt(2) * erf_inv(t1*(1-v) + t2*v);

您可以针对特定参数预先计算t1t2。这种方法的优点在于没有拒绝抽样,因此每次绘制只需要一个NextDouble()。如果[from, to]间隔较小,则速度会更快。

不过,看起来你可能需要二项分布


谢谢,看起来还不错,但是当我使用gaussianInRange(0, 500, 1000)进行测试时,我只得到了494到506之间的值。 - RoflcoptrException
@Roflcoptr - stdev 在你的图表中表示为 _σ_。它不一定与 [from; to] 范围有关。 - aaz

3

我在我的图形生成器中有类似的方法(需要稍微修改一下):

使用具有特定范围的生成器函数返回随机浮点数:

private double NextFunctional(Func<double, double> func, double from, double to, double height, out double x)
{
    double halfWidth = (to - from) / 2;
    double distance = halfWidth + from;

    x = this.rand.NextDouble() * 2 - 1;// -1 .. 1

    double y = func(x);

    x = halfWidth * x + distance;
    y *= height;

    return y;
}

高斯函数:
private double Gauss(double x)
{
    // Graph should look better with double-x scale.
    x *= 2;

    double σ = 1 / Math.Sqrt(2 * Math.PI);
    double variance = Math.Pow(σ, 2);
    double exp = -0.5 * Math.Pow(x, 2) / variance;

    double y = 1 / Math.Sqrt(2 * Math.PI * variance) * Math.Pow(Math.E, exp);

    return y;
}

使用随机数生成图形的方法:
private void PlotGraph(Graphics g, Pen p, double from, double to, double height)
{
    for (int i = 0; i < 1000; i++)
    {
        double x;
        double y = this.NextFunctional(this.Gauss, from, to, height, out x);

        this.DrawPoint(g, p, x, y);
    }
}

我更倾向于使用余弦函数 - 它速度更快,并且对于您的需求非常接近高斯函数:

double x;
double y = this.NextFunctional(a => Math.Cos(a * Math.PI), from, to, height, out x);
< p >在NextFunctional()方法中,out double x参数的存在是为了您可以轻松地在您的图形上进行测试(我在我的方法中使用迭代器)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接