產生高斯曲線分布的亂數產生方法,以投球分布為例

之前有讀到一篇國外獨立開發者的部落文章,談到如何使用亂數器產生高斯曲線分布

先讀這篇,了解何謂高斯分布 :

 http://www.alanzucconi.com/2015/09/09/understanding-the-gaussian-distribution/

再讀這篇,如何使用  :

 http://www.alanzucconi.com/2015/09/16/how-to-sample-from-a-gaussian-distribution/

(註:這位作者也有很多值得一讀的文章,並固定在 Patreon 上發布, 當下就點下去成為他的贊助者了)

值得一讀的好文章:

http://www.redblobgames.com/articles/probability/damage-rolls.html

 

我就一直很想運用在我目前開發的遊戲上, 以下是讀後並運用心得分享,並用自己開發的結果當總結.

Unity 本身就有亂數產生的function -Random.Range(min, max).

這個可以在 min 到 max間產生很"平均"的亂數值

甚麼叫很平均呢?  就是產生亂數的密度(density) 很平均, 不會在某個區間很明顯的出現

但這有時在寫遊戲時,不會是我們想要的樣子, 我們需要的是集中式的分布

這邊部落格的作者有一張易懂的圖

bean mechine

解釋一下圖所代表的含意, 豆子從頂端往下丟時,碰到每一個節點有1/2機率往左,1/2機率往右. 下面有一個容器接住豆子. 當丟下n顆豆子時,容器的豆子分布就會呈現自然分佈. 這條分布曲線可以被數學公式推導出來. 詳細推導不多說,有興趣的可以參考原部落作者文章或維基.

而這邊要運用的是如何做出一二維平面的高斯分布 

左邊是高斯分布,右邊是單一random function 產生的結果

文章中的一連串公式推導, 以我的理解簡單講就是

1. 二維高斯分布就是兩個一維的高斯曲線的 Joint (相乘的概念), 所以得到了相乘後的公式

2. 而分布的座標點(x,y) 轉成極座標的概念後=> x = R*cos(theda) , y = R*sin(theda) 

    所以可以想成 R (半徑), theda(角度) 兩值可想成我們要的亂數值.

3. 因 Joint probility 的概念,  R值可以轉成指數機率公式算出, theda值則是0~2PI 間的random 值

   此時 R 和theda 角都可以被我們用亂數產生和公式帶算出了,等於是座標值已經出來了

   這樣的概念就是  Box-Muller transform

4. 為簡化運算 (開根號, cos , sin..), 將亂數結果縮(project)在一個單位圓(R=1)內,就可以用複數平面(expi(z) = cos(z) + i*sin(z)) 來化簡公式 ,  這就是 Marsaglia polar method.

5. 最後, 導入了平均跟標準差, 可決定這個範圍內集中在哪一點和取捨多少標準差的範圍內

 

作者很佛心的把source code release出來,可在他網站上抓

以下是我的應用程式碼和結果 :

設定將投手投在好球帶正中紅心位置, 投手一律投直球. 白框是亂數產生的範圍

首先用 Random.Range 產生的亂數值當作標

結果如預期的亂數落點平均在平面上,而且是一個方框平面範圍

接著使用作者的高斯分布亂數產生function
(註 : 碼中看到RandomGuassion 被call兩次,其實整個公式第一次就做完了,第二次是拿取第一次計算的另一個值出來,詳細請看原作者程式)

產生的結果如圖, 在範圍內的一點集中. 看起來比較是一個投手投球分布.