Mark一個按照權重生成隨機數方法


因為別人問了我一個問題當時一兩分鍾沒想上來,后面搜索了下,找到了一個文章寫的很全。搬過來記一下。原問題是想設計一個算法在一個集合中隨便選一個數,但是選出來這個數的概率要和這個數的大小成正比。也就是說希望越大的數被大概率的選出來。

這個問題更清晰點兒描述是,有一組數字,他們都帶有不同的權重,現在要從中“隨機”抽一個數字,但是抽到某個數字的概率要正比於他的權重。假設這個集合中的元素和其對應權重為{‘A’:50,‘B’:10,‘C’:100,‘D’:3,‘E’:60,‘F’:25}。

方法一

如果隨機選的話,在一個list中出現的次數多被選中的概率就會大。所以可以按照權重組合一個list,在這個list中A有50個,B有10個,C有100個,等等。有了list之后,從中隨機選一個。這樣選取的時間負責度是O(1)。但是如果數字很多,權重又很大的話,那么空間負責度會很高。

import random

data = {'A': 50, 'B': 10, 'C': 100, 'D': 3, 'E': 60, 'F': 25}
value_list = []
for key, value in data.items():
    value_list += value*[key]
pick_value = random.choice(value_list)

進行100萬次實驗,計數每個字母被選中的次數。結果如下圖所示

可以看出多次實驗的結果,每個字符選中的次數確實與權重成正比。

方法二(常用)

對每個元素的權重進行加和記為sum,在1到sum之間隨機選一個數。之后遍歷整個集合,統計遍歷到的項的權重之和,如果大於sum,則選擇這個數。該方法需要遍歷集合,時間復雜度為O(n)

import random

data = {'A': 50, 'B': 10, 'C': 100, 'D': 3, 'E': 60, 'F': 25}
value_sum = sum(data.values())
for i in range(1000000):
    t = random.randint(0, value_sum - 1)
    for key, value in data.items():
        t -= value
        if t < 0:
            pick_value = key
            break

多次實驗統計被選中的元素的次數如下圖

方法三

上面方法最壞的情況是遍歷完所有的元素,但是如果權重大的元素能夠先被遍歷的話可以減少遍歷次數。基於此,可以先對元素按照權重排序。但是這樣排序的話也會比較花時間。可以計算一個前項和序列,記錄到每一項時的權重是多少。然后隨機生成一個數,可以用二分查找找這個數對應的索引,也就可以找到對應的元素。但是這樣就要求數據不能存成字典,因為字典的遍歷是無序的。

import bisect
import random

data = {'A': 50, 'B': 10, 'C': 100, 'D': 3, 'E': 60, 'F': 25}
key_list = list(data.keys())
value_list = [data[key] for key in key_list]
prefix_sum = []
tmp_sum = 0
for value in value_list:
    tmp_sum += value
    prefix_sum.append(tmp_sum)
t = random.randint(0, tmp_sum - 1)
pick_value = key_list[bisect.bisect_right(prefix_sum, t)]

同樣統計結果如下

參考:

【1】http://www.cnblogs.com/zywscq/p/5469661.html

 


免責聲明!

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



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