如何優雅地產生一組符合正態分布的隨機數?


使用 Box Muller Transform,每產生一對正態分布隨機數需要使用兩個均勻分布的隨機數作為原料。

/// & /// 產生正態分布的隨機數
/// &
/// &

正態分布的平均值, 即 N(μ, σ^2) 中的 μ & /// &

正態分布的標準差, 即 N(μ, σ^2) 中的 σ & /// & 返回正態分布的隨機數. 理論值域是 μ±∞ &
public float Normal(float averageValue, float standardDeviation)
{
return averageValue + standardDeviation * (float)
(
System.Math.Sqrt(-2.0 * System.Math.Log(1.0 - Range01()))
* System.Math.Sin((System.Math.PI + System.Math.PI) * Range01())
);
}

上面的實現中 Range01() 即是產生 [0, 1) 範圍內的均勻分布隨機數;另一個成對的正態分布隨機數被拋棄。


看到C#這個tag之前我想說std::normal_distribution

看到之後。。。好像CLR里還真沒有,那自己搓一個吧!

我數學很鶸(跟 @霍姚遠 比),我選擇抄代碼(並沒有)

從LLVM的libcxx中,我們觀察它的頭文件中的std::normal_distribution實現:

template &
template&
_RealType
normal_distribution&<_RealType&>::operator()(_URNG __g, const param_type __p)
{
result_type _Up;
if (_V_hot_)
{
_V_hot_ = false;
_Up = _V_;
}
else
{
uniform_real_distribution& _Uni(-1, 1);
result_type __u;
result_type __v;
result_type __s;
do
{
__u = _Uni(__g);
__v = _Uni(__g);
__s = __u * __u + __v * __v;
} while (__s &> 1 || __s == 0);
result_type _Fp = _VSTD::sqrt(-2 * _VSTD::log(__s) / __s);
_V_ = __v * _Fp;
_V_hot_ = true;
_Up = __u * _Fp;
}
return _Up * __p.stddev() + __p.mean();
}

可以看出,這一實現當中,每次生成兩個符合標準正態分布的隨機數,它們來自在單位圓內均勻分布的二元隨機變數 (\_\_u, \_\_v) 。我們將它記作 (u,v) ,那麼 \_\_s 就是 u^2+v^2

於是 \_Fp=sqrt{-2frac{ln{(u^2+v^2)}}{u^2+v^2}} ,兩個符合正態分布的隨機數則分別是 \_Fp 	imes u\_Fp 	imes v

看到這想來去搓一個C#實現的正態分布隨機數發生器已經不難了,我就不展示自己的代碼有多醜了。

另外,數學大佬們請開始你們的表演;我是看不懂這玩意怎麼就正態了,太菜。

編輯:根據 @Yanbing Zhao 私下裡的提示,這個方法叫Box-Muller transform,有興趣的可以了解一下。感謝計算物理A課程學生的指點(逃走


根據中心極限定理,你可以使用多個均勻分布疊加作為正態分布。一般來說十二個就夠了。


正態分布是對某一固定的數據集來說的,你隨便給幾個數,我都能構建一個包含這幾個數的正態分布數據集,而且可以構建無數個.也就是數,任何n個數都可以同時屬於

m個正態分布數據集。


參考TAOCP第二卷第三章:隨機數


對於任意分布,都可以採用取0-1均勻分布後,再進行一個累積概率映射的方式,即x映射為使累積概率(-∞,y)為x的y值

然而正態分布的概率密度函數似乎並不初等可積,因此也就不太好應用這一方法了

(簡單使用還是查表吧)


推薦閱讀:

如何看待PHP成為.NET的一門編程語言?
怎麼看待.net core 2.0發布?
為什麼大名鼎鼎的Spring在.NET平台上無以為繼?

TAG:演算法 | C# | NETCore |