這一篇研究的內容,是第 (1) 篇研究中的升級版,我們從 Golang 上進行這樣分佈生成的實作,並且嘗試使用統計學的方法來驗證指定機率的正確性。
進行機率的研究,最主要目的是了解機率學在博弈遊戲中的工程方法與數學方法,隨著研究的深入,也等於是從外行人的角度去了解博弈業的各種名詞、工程方法,所以,若我們從第 (1) 篇的研究繼續探討,其實有限個數、無限個數指的也是博弈遊戲中的【自然機率】、【非自然機率】。
回過頭來審視前幾篇研究在做的事情:
(1): 從幾個專案的程式碼中窺探中獎機率的設計
(2): 什麼是可測與不可測亂數,使用常見的線性同餘設計一個亂數生成函數
(3): 分佈對機率代表的意義與常被使用的解釋
(4): 工程上常見的機率分佈逆轉換,從均勻隨機分佈,做線性變換處理得到人為的機率生成函數
(5): 應用端常見的機率分佈生成,使用區間分配法製作亂數產生器本身,並且做分配驗證
進入正題,這一篇如敘述所說,是紀錄 Golang 如何做純應用面上的指定機率分配生成函數。
給定機率分配: [ 0.2, 0.6, 0.2 ] (這是合法的機率分配: 因為加總是 1)
按照順序 0, 1, 2 代表著選到 【0】 號數字的機率是 0.2、【1】 號數字的機率是 0.6、【3】 號數字的機率是 0.2。
在思路上,我們可以使用加總的方式來得到每個標號的區間 [1],直接列表如下:
- 【0】: [0~0.2)
- 【1】: [0.2~0.8)
- 【2】: [0.8~1)
不過,如果使用的亂數產生器是 rand int ,就必須用整數的做法,為了視覺化,所以以下的程式碼全部使用整數機率處理,也就是放大所有人的倍數 (包含 rand int 的倍數),放大區間後,因為線性變換並不會影響機率。
我們可以設定一個 accurcy = 100,表示使用 100 倍變成整數後,來處理亂數,得到新的區間表:
- 【0】: 0~20
- 【1】: 21~80
- 【2】: 81~100
rand.Intn(100) //實際上是 rand.Intn(accurcy)
然後,我們在第(1)篇研究中,是手動去累加 【0】【1】【2】 所在的機率,可能會像:
if 0 <= x && x <= 20{
...
}else if 21 <= x && x <= 80{
...
}
現在,我們希望他可以自動適應,自動計算,所以採用了這個方法:
func GENERATOR(p []float32) int {
//given
accuracy := 100
// probabilities 開出整數的 N 個機率事件空間,各自機率加起來要等於 accuracy
probabilities := []int{}
// 把輸入進來的 p 機率陣列,放大 accuracy 倍後轉成整數,丟進機率事件空間
for _, _p := range p {
probabilities = append(probabilities, int(_p*float32(accuracy)))
}
// 設定起始計算機率點
var start, end int = 0, 0
// 因為放大過,所以 rand 隨機數大小就會隨著 accuracy 大小變換
rand := rand.Intn(accuracy)
// 開始迭代每一個區間,每個區間都有上限下限,只是要看丟出來的機率在第幾個區間 (i)
for i, probability := range probabilities {
end += probability // 每一次都會增加區間上限,也就是區間浮動的概念
// 如果這個隨機數,的確落在這個區間,就表示選到這個區間了
if start <= rand && end > rand {
//fmt.Println("選擇區間數: ", i, "; 區間起點:",start,";區間終點:", end, "; 機率值: ", rand)
return i
}
start = end // 下一個 i 的下限就被提高了,也就等於區間浮動的概念
}
return -1
}
func main() {
forZero := 0.0 //統計 【0】 發生次數機率
forONE := 0.0 //統計 【1】 發生次數機率
forTwo := 0.0 //統計 【2】 發生次數機率
// 直接跑 1000000 次
for a := 0; a < 1000000; a++ {
p := Algorithm([]float32{0.2, 0.6, 0.2})
//fmt.Println(p)
//如果結果是 【0】、【1】或是【2】,就統計他們發生的次數
if p == 0 {
forZero += 1.0
}
if p == 1 {
forONE += 1.0
}
if p == 2 {
forTwo += 1.0
}
}
// 古典機率就告訴我們發生的次數除上母數,就是該事件的機率:
fmt.Println(forZero / 1000000.0)
fmt.Println(forONE / 1000000.0)
fmt.Println(forTwo / 1000000.0)
}
var r1 *rand.Rand
func init(){
s1 := rand.NewSource(time.Now().UnixNano())
r1 = rand.New(s1)
}
func GENERATOR_WITH_SEED(p []float32) int {
// 不要把 randon seed 在這邊產生,因為每
// 一次都要重新 new 新的 seed 會很容易早成效能瓶頸
// NO NO NO NO: s1 := rand.NewSource(time.Now().UnixNano())
// NO NO NO NO: r1 = rand.New(s1)
//given
accuracy := 100
// probabilities 開出整數的 N 個機率事件空間,各自機率加起來要等於 accuracy
probabilities := []int{}
// 把輸入進來的 p 機率陣列,放大 accuracy 倍後轉成整數,丟進機率事件空間
for _, _p := range p {
probabilities = append(probabilities, int(_p*float32(accuracy)))
}
// 設定起始計算機率點
var start, end int = 0, 0
// 因為放大過,所以 rand 隨機數大小就會隨著 accuracy 大小變換
rand := r1.Intn(accuracy)
// 開始迭代每一個區間,每個區間都有上限下限,只是要看丟出來的機率在第幾個區間 (i)
for i, probability := range probabilities {
end += probability // 每一次都會增加區間上限,也就是區間浮動的概念
// 如果這個隨機數,的確落在這個區間,就表示選到這個區間了
if start <= rand && end > rand {
//fmt.Println("選擇區間數: ", i, "; 區間起點:",start,";區間終點:", end, "; 機率值: ", rand)
return i
}
start = end // 下一個 i 的下限就被提高了,也就等於區間浮動的概念
}
return -1
}
Reference:
[1]: https://studygolang.com/articles/23067
[2]: https://blog.csdn.net/m0_37710023/article/details/108061125?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduend~default-1-108061125.nonecase&utm_term=go%20%E7%94%9F%E6%88%90%E6%AD%A3%E6%80%81%E5%88%86%E5%B8%83%E7%9A%84%E9%9A%8F%E6%9C%BA%E6%95%B0&spm=1000.2123.3001.4430
[3]: https://blog.csdn.net/aaaadong/article/details/92839996
[4]: https://blog.sqrtthree.com/articles/random-number-in-golang/
[5]: https://ezslotdesign.com/%e8%87%aa%e7%84%b6%e8%88%87%e9%9d%9e%e8%87%aa%e7%84%b6%e6%a9%9f%e7%8e%87/
[6]: https://ch-hsieh.blogspot.com/2016/01/bernoulli-binomial.html
[7]: https://fu-sheng-wang.blogspot.com/2017/01/statistics15-binomial-distribution.html
[8]: http://web.ntnu.edu.tw/~algo/Estimation.html
[9]: https://www.taodabai.com/how/641240802.html
[10]: https://github.com/golang/go/issues/11871
[11]: https://github.com/golang/go/issues/4509
沒有留言:
張貼留言