Unity与高斯分布——Part2:生成服从高斯分布的数

原文链接:https://www.alanzucconi.com/2015/09/16/how-to-sample-from-a-gaussian-distribution/


  • 第一步:从平均分布到高斯分布

假设有两个独立随机变量X, Y服从高斯分布:

\[X \sim \mathcal{N} \left(0,1 \right)\]

\[Y \sim \mathcal{N} \left(0,1\right)\]

若在二维平面上采样横纵坐标分别为(X, Y)的点,得到的图为:

Gaussian - Copy - Copy

点(X, Y)在该平面上的联合概率密度函数为:\[P(x,y) = P(x)P(y) =\]

\[ =\frac{1}{\sqrt{2\pi }}e^{-{\frac{x^2}{2}}}\frac{1}{\sqrt{2\pi }}e^{-{\frac{y^2}{2}}} =\frac{1}{2\pi}e^{-{\frac{x^2+y^2}{2}}}\]

将平面直角坐标系转换为极坐标系:

Gaussian - Copy - Copy (2)

\[R=\sqrt{x^2+y^2}\]

\[\theta=arctan\left (\frac{y}{x}\right )\]

则点的横纵坐标可记做:

\[x = R cos\left(\theta\right)\]

\[y = R sin\left(\theta\right)\]

则联合概率密度函数可写作:\[\frac{1}{2\pi }e^{-{\frac{x^2+y^2}{2}}}=\frac{1}{2\pi}e^{-{\frac{R^2}{2}}}=\left (\frac{1}{2\pi }  \right )\left (e^{-{\frac{R^2}{2}}}  \right )\]

这又可以看成是两个独立的概率分布的联合:

\[R^2 \sim Exp\left(\frac{1}{2}\right)\]

\[\theta \sim Unif(0,2\pi) = 2\pi Unif(0,1)\]

这里的\theta已经服从均匀分布了,回忆一下质数分布的定义:

\[Exp(\lambda)=\frac{-log(Unif(0,1))}{\lambda}\]

发现R也服从均匀分布:\[R \sim \sqrt{-2log\left( Unif(0,1)\right)}\]

这样,两个变量就都服从均匀分布了,因此高斯分布可以用两个均匀分布来生成。

  • 第二步

现在反推第一步的流程,得到生成高斯分布的算法:

(1)生成两个服从U[0, 1]均匀分布的变量u_1,u_2 \sim Unif(0,1)

(2)由这两个变量生成R和\thetaR = \sqrt{-2log\left(u_1\right)}\theta = 2\pi u_2

(3)将(R, \theta)从极坐标系转换成笛卡尔坐标系(R cos\theta, R sin\theta)

这就是 Box-Muller transform。下图显示了在单位正方形里用Box-Muller transform将均匀分布的点映射成高斯分布的方式。

Box-Muller_transform_visualisation.svg

  • 第三步:Marsaglia polar method

Box-Muller transform的问题是:它本身使用了复杂的三角函数,这很影响运算速度。为解决该问题,Marsaglia polar method出现了。它是由一个服从二维(-1,1)的均匀分布的点中产生的。这个点必须在单位圆中出现,且不能是原点(0,0)。如果不满足上述条件,则选择下一个点。代码如下:

public static float NextGaussian() {
    float v1, v2, s;
    do {
        v1 = 2.0f * Random.Range(0f,1f) - 1.0f;
        v2 = 2.0f * Random.Range(0f,1f) - 1.0f;
        s = v1 * v1 + v2 * v2;
    } while (s >= 1.0f || s == 0f);

    s = Mathf.Sqrt((-2.0f * Mathf.Log(s)) / s);
 
    return v1 * s;
}

该方法中大约21%的点会被排除。

  • 第四步:映射到任意高斯曲线

第三步中的算法提供了一种从\mathcal{N} \left(0,1 \right)中采样的方法。我们可以用如下方法将其转换为任意的正态分布\mathcal{N} \left(\mu,\sigma^2 \right)

public static float NextGaussian(float mean, float standard_deviation)
{
    return mean + NextGaussian() * standard_deviation;
}

还有一个问题是,高斯分布可能会产生偏离均值很远的极值,这不是我们想要的结果。如何生成有着最值限制的,服从高斯分布的随机函数呢?代码如下所示:

public static float NextGaussian (float mean, float standard_deviation, float min, float max) {
	float x;
	do {
		x = NextGaussian(mean, standard_deviation);
	} while (x < min || x > max);
	retun x;
}
发布了22 篇原创文章 · 获赞 0 · 访问量 7399

猜你喜欢

转载自blog.csdn.net/georgeandgeorge/article/details/89480224