生成正態/高斯數字
我將在本月專欄中演示的第四種方法是從鐘形分布(通常稱為正態或高斯分布)中生成數字。
假設您要生成一些與一組人身高相對應的測試用例輸入數據??赏ㄟ^一種稱為 Box-Muller 算法的非常巧妙的方法來生成正態分布的偽隨機數字。用于創建圖 1 中所示輸出的代碼的開頭如下所示:
Gaussian g = new Gaussian(); double ht; int outliers = 0; Console.WriteLine("從平均值為 68.0 英寸、標準偏差為 6.0 英寸的" + "正態/高斯分布生成 " + "100 個隨機身高 ->"); |
我以一個程序定義的高斯對象為例。此對象執行所有工作并使用 Box-Muller 算法??勺兩砀邔⑹芤粋€正態分布值的約束。我還會初始化一個計數器以跟蹤非正常值,即那些遠高于或遠低于平均身高的值。跟蹤非正常值使我起碼可以驗證我的隨機身高事實上是正態分布的。圖 5 列出了生成并顯示我的隨機身高的代碼。
我每隔 10 個值就用模數 (%) 運算符打印一個新行,只是為了保持輸出整齊。來自平均值為 68.0 英寸、標準偏差為 6.0 英寸的正態分布隨機身高通過 Gaussian.NextGaussian2 方法的調用返回,我稍后將對此詳細說明。我通過監控小于 56 英寸或大于 80 英寸的值來跟蹤非正常值。這些值是高于或低于平均值 68.0 英寸兩個標準偏差(6.0 * 2 = 12.0 英寸)的值。據統計,隨機生成值超過平均值兩個正(或負)標準偏差的概率大約有 5%。因此,如果生成 100 個隨機身高(就像我現在這樣),則可以預期約有 5 個非正常值。如果得出的非正常值遠多于或遠少于 5 個,則就需要仔細檢查代碼。請注意,在圖 1 所示的運行示例中,我剛好得到 5 個非正常值,這使我更加確信我的隨機生成的身高數據點實際上是正態分布的。
Box-Muller 算法隱含的原理非常深奧,但結果卻是相當簡單。如果您在 [0,1) 值域內有兩個一致的隨機數字 U1 和 U2(如本專欄中第一部分所述),則您可以使用以下兩個等式中的任一個算出一個正態分布的隨機數字 Z:
Z = R * cos( θ ) 其中,
R = sqrt(-2 * ln(U2)) |
正態值 Z 有一個等于 0 的平均值和一個等于 1 的標準偏差,您可使用以下等式將 Z 映射到一個平均值為 m、標準偏差為 sd 的統計量 X:
X = m + (Z * sd) |
以 NextGaussian 方法實現高斯類的最簡單方法由圖 6 中的代碼表示。
我使用的是 Math.Cos 版本,但我本完全可以輕松地使用 Math.Sin 版本。該實現代碼雖然可行,但效率很低。由于 Box-Muller 算法可利用 sin 或 cos 中的任一個函數計算正態分布的 Z 值,因此我倒不如同時計算兩個 Z 值,保存第二個 Z 值,然后在第二次調用 NextGaussian 方法時可以檢索所保存的值。此類實現方法如圖 7 所示。
盡管該方法可行性很好,但也存在一些低效性。使用 Math.Sin、Math.Cos 和 Math.Log 方法進行計算會降低性能。一種提高效率的巧妙方法是使用數學技巧。如果您檢查一下 R 和 θ 的定義,會發現它們與某單位圓內某隨機點的極坐標相對應。該數學技巧就是計算單位正方形內某隨機點的坐標(避免了調用 Math.Sin 和 Math.Cos 方法)并確定該隨機點是否在單位圓范圍內。如果是這樣,我們就可以使用這組坐標;如果不是這樣,則我們可計算一組新的隨機坐標,然后重試一次。約有 78% 的隨機生成坐標都在單位圓范圍內,這提供了更好的性能,但顯然要影響到清晰性。
圖 8 中例示了單位正方形技巧。Box-Muller 基本算法將選擇一個極坐標為 (R, θ) 并保證在單位圓范圍內的點。您也可以在包圍單位圓的單位正方形內選擇矩形坐標;點 (x1, y1) 在單位圓范圍內,但是點 (x2, y2) 則在單位圓范圍之外。圖 9 說明了單位正方形方法的實現。
原文轉自:http://www.uml.org.cn/Test/200611225.htm