抽獎問題分析

weixin_34127717發表於2017-09-05

本文原創文章,轉載註明出處,部落格地址 https://segmentfault.com/u/to... 第一時間看後續精彩文章。覺得好的話,順手分享到朋友圈吧,感謝支援。

普通抽獎問題

問題描述

使用者隨機抽獎,資料如下:

// map中,key代表使用者名稱,value代表成使用者下單數
var users map[string]int64 = map[string]int64{
  "a": 10,
  "b": 6,
  "c": 3,
  "d": 12,
  "f": 1,
}

思路

隨機問題,一般就是通過隨機函式從某個範圍內隨機取出某個數值,則該數值對應的就是中獎使用者

在這裡,如果我們能給map中每個元素設定對應的索引,即轉化為陣列,是不是就可以解決問題了呢?

程式碼實現

func GetAwardUserName(users map[string]int64) (name string) {
    size := len(users)
    awardIndex := rand.Intn(size)

    i := 0
    for userName, _ := range users {
        if i == awardIndex {
            name = userName
            return
        }
        i++
    }
    return
}

單元測試

func Test_GetAwardUserName(t *testing.T) {
    var users map[string]int64 = map[string]int64{
        "a": 10,
        "b": 6,
        "c": 3,
        "d": 12,
        "f": 1,
    }

    rand.Seed(time.Now().Unix())
    awardCount := make(map[string]int)
    for i := 0; i <= 1000000; i++ {
        awardName := GetAwardUserName(users)
        if count, ok := awardCount[awardName]; ok {
            awardCount[awardName] = count + 1
        } else {
            awardCount[awardName] = 0
        }
    }
    for n, c := range awardCount {
        fmt.Printf("%v:%v\n",n,c)
    }
}

測試結果:

為了驗證獲獎概率的正確性,迴圈執行100萬次,每個使用者獲獎的次數基本在20萬左右,每個使用者的獲獎概率相等

c:200102
f:199853
b:198942
a:200395
d:200704

權重抽獎

問題描述:

資料結構和上面抽獎問題一致,只是這裡,要求中獎概率和使用者的訂單數成正比

思路

本質還是隨機函式獲得一個數值,數值對應的使用者即獲獎使用者;這裡要實現訂單數對獲獎概率的影響問題,即訂單數對應隨機數的某個範圍,訂單數越大,範圍越大,隨機數落在範圍內的概率越大

程式碼實現

func getAwardUser_weight(users map[string]int64) (name string) {
    userArr := make([]string, len(users),len(users))
    var sumCount int64 = 0
    index := 0
    for n, c := range users {
        //整理所有使用者的count資料為數軸
        userArr[index] = n
        index++
        sumCount += c
    }

    awardIndex := rand.Int63n(sumCount)

    var offset int64
    for _, n := range userArr {
        //判斷獲獎index落在那個使用者區間內
        offset += users[n]
        if offset > awardIndex {
            name = n
            return
        }
    }
    return
}

單元測試

func Test_getAwardUser_weight(t *testing.T) {
    var users map[string]int64 = map[string]int64{
        "a": 10,
        "b": 6,
        "c": 3,
        "d": 12,
        "f": 1,
    }

    rand.Seed(time.Now().Unix())
    awardCount := make(map[string]int)
    for i := 0; i <= 100000; i++ {
        awardName := getAwardUser_weight(users)
        if count, ok := awardCount[awardName]; ok {
            awardCount[awardName] = count + 1
        } else {
            awardCount[awardName] = 0
        }
    }
    for n,c := range awardCount {
        fmt.Printf("%v:%v \n",n,c)
    }
}

測試結果:

迴圈遍歷了100萬次,獲獎的次數,與使用者的訂單數成正比

c:93479 
f:31206 
d:375614 
b:186933 
a:312764 

總結

解決實際問題,往往都有數學模型去對應,比如抽獎問題,就可以轉化為初中所學習的數軸知識,畫個草圖,簡單易理解,也不需要多高深的數學知識

問題本身並不難,重要的是轉換思路,將抽象問題簡化為具體的數學問題,然後去解決
image.png

相關文章