生成具有抛物线分布的随机值

generate random values with a parabolic distribution

我想在 x 和 y 之间生成随机延迟。我希望输出更常见于 x 和 y 的中间而不是 x 或 y。

例如,如果我的 x 是 10,y 是 20,我希望最常见的输出是 15。

我一直在尝试在纸上解决这个问题,但不幸的是我不是数学天才。

我用 Random.nextGuassian() 尝试了一些公式,但我无法理解它给出的奇怪输出。

Java 的 Random.nextGaussian() return 的值以零平均值为中心。如果您希望值以 15 为中心,则将 return 由 Random.nextGaussian() 编辑的值加上 15。更一般地说,如果您希望值在 xy 之间居中,则执行:

    delay = Random.nextGaussian + ( (x + y) * 0.5 );

在正态分布中,99.7% 的数据样本位于均值上下 3 个标准差范围内。 Random.nextGaussian() 产生标准偏差为 1.0 的值,因此如果平均值为 15,则 99.7% 的值 return 将介于 12 和 18 之间。但是,它偶尔会 return 值小于 x 或大于 y,因此您需要检查这些情况。简单地强制值小于 xx 和大于 yy 可能足以满足您的用例:

    if (delay < x) { 
        delay = x;
    } else if (delay > y) {
        delay = y;
    }

参考文献:

在实现具有抛物线分布的随机数生成器之前,您需要定义其含义。这是一种尝试:

分布在 0 到 1 区间上定义为二次多项式。在此区间内,曲线下方的面积必须为 1。通过改变参数 KM,您可以调整分布。 K 确定抛物线中心在区间中的位置,M 确定抛物线向下到 "pull" 的距离。在您的问题中,K 是 ½,因为您希望顶点位于区间的中间。您没有指定 M.

抛物线由函数指定

f(x) = ax2 + bx + c

假设 KM 并且 0 到 1 区间曲线下方的面积应该是 1 你需要计算 abc.

如果K = ½ 且M = ½ 精确解为

f(x) = 6x2 - 6x + 2.

如果这个解决方案不充分(因为你想要 K and/or M 的其他值)那么你将有创建一组方程式。根据抛物线的放置方式,已知 a 必须为正,b 为负,c > M.

抛物线的顶点应该触及 (K, M)。这意味着 f(K) = Mf(K) - M = 0.

要使顶点触及此点,修正二次方程的判别式必须为 0:

d' = b2 - 4a(c - M)

只要知道ac就可以计算出b(已经已知 b 必须为负才能创建所需的抛物线):

b = -√(4a(c - M)) [equation A]

顶点的方程是:

f(K) - M = aK2 + bK + c - M = 0 [equation B]

抛物线下方的面积是根据 f(x)[=20 的区间 0 到 1 上的定积分计算的=]

01 f(x) dx = a/3 + b/2 + c = 1 [equation C]

您可以将等式 A 中的 b 代入等式 B 和 C,得到两个具有两个未知数的等式 a c。不幸的是,这些方程是非线性的,我的数学越来越生疏,所以我选择了 "easy path" 并使用 Excel 中的求解器加载项来找到 K 不同值的近似解M。如果你采用这种方法,你可能应该在求解器中添加一个约束,即 d' 不能为负以及对 a 和 [=56 的约束=]c 如上所述。

现在您知道了所需抛物线方程的 abc创建具有抛物线分布的随机数的方法如下:

  • 在均匀分布的抛物线方程域上生成一个随机数(视觉上你可以认为这是生成一个y值或一个f(x) 值)
  • 通过使用 f(x) 的倒数计算相应的 x 值 -请注意,有两种可能的解决方案
  • 如果其中一个解决方案在区间 0 到 1 之外,则选择另一个解决方案
  • 如果两个解都在0到1的区间内,随机取一个

选择的这个解是一个服从抛物线分布的随机数。

f(x)的倒数是

x = (-b ± √(b2 - 4a(c - y)))/2a

结合所有这些,我创建了一个 C# class。您必须用所需的值替换参数 KMabc。或者您可以扩展此代码以从 K 计算 abc M 使用数值算法。

class ParabolicRandom
{
    const double k = 0.5D;
    const double m = 0.5D;

    const double a = 6;
    const double b = -6;
    const double c = 2;

    readonly double yMin = m;
    readonly double yMax = Math.Max(F(0D), F(1D));

    Random random;

    public ParabolicRandom() => random = new Random();

    public ParabolicRandom(int seed) => random = new Random(seed);

    public double Next()
    {
        var randomY = (yMax - yMin) * random.NextDouble() + yMin;
        var randomX1 = ReverseF1(randomY);
        var randomX2 = ReverseF2(randomY);

        if (randomX1 < 0D || randomX1 > 1D)
            return randomX2;
        if (randomX2 < 0D || randomX2 > 1D)
            return randomX1;
        return random.Next()%2 == 0 ? randomX1 : randomX2;
    }

    static double F(double x) => a * x * x + b * x + c;
    double ReverseF1(double y) => (-b + Math.Sqrt(b * b - 4 * a * (c - y))) / (2 * a);
    double ReverseF2(double y) => (-b - Math.Sqrt(b * b - 4 * a * (c - y))) / (2 * a);
}

要生成 10 到 20 之间的随机数,其中 15 是 10 和 20 的一半,你可以这样使用它:

var lowerInclusive = 10;
var upperInclusive = 20;
var value = (int) Math.Floor(
    (upperInclusive - lowerInclusive + 1)*parabolicRandom.Next() + lowerInclusive);