Javascript 隨機數函數 學習之二:產生服從正態分布隨機數


 

一、為什么需要服從正態分布的隨機函數

一般我們經常使用的隨機數函數 Math.random() 產生的是服從均勻分布的隨機數,能夠模擬等概率出現的情況,例如 扔一個骰子,1到6點的概率應該相等,但現實生活中更多的隨機現象是符合正態分布的,例如20歲成年人的體重分布等。

 

假如我們在制作一個游戲,要隨機設定許許多多 NPC 的身高,如果還用Math.random(),生成從140 到 220 之間的數字,就會發現每個身高段的人數是一樣多的,這是比較無趣的,這樣的世界也與我們習慣不同,現實應該是特別高和特別矮的都很少,處於中間的人數最多,這就要求隨機函數符合正態分布。

 

二、正態分布復習

 

圖片來自:http://zh.wikipedia.org/zh-cn/%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83

具體性質也請查閱上面鏈接,描述正態分布的主要特征是均值和方差,如上圖,最左的倒鍾形圖的均值為-2, 其余為0 ;

 

方差越大,鍾形越扁平,方差越小越陡;

  • 密度函數圖像關於均值對稱。
  • 在x=μ±σ處,曲線有拐點。
  • 函數曲線下68.26%的面積在平均數左右的一個標准差σ的區間內。
  • 95.44%的面積在平均數左右兩個標准差2σ的區間內。
  • 99.74%的面積在平均數左右三個標准差3σ的區間內。

當均值為0, 方差為 1 時稱為標准正態分布;

 

 

三、由均勻分布經 “Box-Muller法” 轉換為正態分布

 

通過查閱文獻可知(請參見:http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform),有一個稱為 Box-Muller (1958) 轉換的算法能夠將兩個在區間(0,1] 的均勻分布轉化為標准正態分布,其公式為:

y1 = sqrt( - 2 ln(u) ) cos( 2 pi v )

y2 = sqrt( - 2 ln(u) ) sin( 2 pi v )

 

 

因為三角函數計算較慢,我們可以通過上述公式的一個 polar form(極坐標形式)能夠簡化計算,

算法描述如下:

function getNumberInNormalDistribution(mean,std_dev){
    return mean+(randomNormalDistribution()*std_dev);
}

function randomNormalDistribution(){
    var u=0.0, v=0.0, w=0.0, c=0.0;
    do{
        //獲得兩個(-1,1)的獨立隨機變量
        u=Math.random()*2-1.0;
        v=Math.random()*2-1.0;
        w=u*u+v*v;
    }while(w==0.0||w>=1.0)
    //這里就是 Box-Muller轉換
    c=Math.sqrt((-2*Math.log(w))/w);
    //返回2個標准正態分布的隨機數,封裝進一個數組返回
    //當然,因為這個函數運行較快,也可以扔掉一個
    //return [u*c,v*c];
    return u*c;
}

 

因此,假如我們要獲得均值為180,要68.26%左右的NPC身高都在[170,190]之內,即1個標准差范圍內,因此標准差為10, 可以通過getNumberInNormalDistribution(180,10) 調用,我們實驗1000000詞,得到結果如下:

 

// 身高:頻率
128:1
132:1
133:1
134:1
135:1
136:2
137:4
138:8
139:11
140:14
141:19
142:28
143:41
144:54
145:80
146:133
147:153
148:235
149:333
150:429
151:598
152:764
153:1059
154:1314
155:1776
156:2290
157:2835
158:3503
159:4373
160:5513
161:6475
162:7809
163:9437
164:11189
165:13282
166:15020
167:17239
168:19215
169:21597
170:24336
171:26684
172:29000
173:31413
174:33179
175:35027
176:37084
177:38047
178:38968
179:39635
180:39700
181:39548
182:38960
183:38674
184:36948
185:35220
186:33224
187:31038
188:29198
189:26668
190:23893
191:21662
192:19476
193:16898
194:15056
195:13046
196:10971
197:9456
198:7928
199:6697
200:5370
201:4334
202:3548
203:2810
204:2330
205:1765
206:1350
207:1093
208:797
209:595
210:371
211:328
212:255
213:165
214:121
215:91
216:71
217:29
218:32
219:28
220:20
221:6
222:7
223:7
224:3
225:2
228:1

 

繪制成柱狀圖如下:

 

可見,這是有着非常明顯的正態分布圖像特征。

 

 

四、由均勻分布疊加獲得正態分布

 

我們需要祭出萬能的中心極限定理。

 

根據獨立同分布的中心極限定理:設隨機變量X1,X2,...Xn,...相互獨立,服從同一分布,且數學期望為μ,標准差為σ (σ>0),則隨機變量之和的標准化變量:

Y=((X1+X2+...+Xn)-nμ)/(sqrt(n)*sqrt(σ)) 近似服從標准正態分布 N(0,1)

 

如果我們將足夠多個均勻分布隨機變量相加,相加之和將服從正態分布。但是,我們需要累加多少個均勻分布才能較好低近似正態分布呢?

由於 X~U(0, 1) , 可得 μ=1/2, σ=sqrt(1/12),代入上面的式子即可近似模擬隨機變量之和的概率密度函數(p.d.f).

 

下圖是由2個服從 U(0,1) 分布的隨機變量相加得到的 p.d.f 圖像:

 

如果我們增加累加的均勻分布的數量會怎樣呢?

 

上圖是 n=3 時的圖像,可以看到正態分布的形狀出來了,但頂端還略為平緩。

 

特別低,當n=12時 (隨機變量(X1+X2+...+Xn)的均值為6,方差為1)  這時有一個很好的特點,公式 Y=((X1+X2+...+Xn)-nμ)/(sqrt(n)*sqrt(σ)) 的分母正好為1,因此簡化成了 Y=((X1+X2+...+Xn)-nμ),非常便於編程計算,並且已經非常接近於標准正態分布,請見下圖:

 

也就是說均值為μ,標准差為σ 的獨立同分布變量 X1,X2, ..., Xn 的算數平均數  T=(X1+X2+ ...+ Xn)/n,當n充分大時,近似地服從均值為μ,方差為σ*σ/n 的正態分布。

 

最后,代碼如下:

function getNumberInNormalDistribution(mean,std_dev){    
    return mean+(uniform2NormalDistribution()*std_dev);
}

function uniform2NormalDistribution(){
    var sum=0.0;
    for(var i=0; i<12; i++){
        sum=sum+Math.random();
    }
    return sum-6.0;
}

 

同樣,將產生100萬個隨機數按頻率畫出直方圖如下:

 

 

如何產生服從均勻分布的隨機數呢?

 

請見上篇:

Javascript 隨機數函數 學習之一:產生服從均勻分布隨機數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM