我們平時比較多會遇到的一種情景是從一堆的資料中隨機選擇乙個, 大多數我們使用random
就夠了, 但是假如我們要選取的這堆資料分別有自己的權重, 也就是他們被選擇的概率是不一樣的, 在這種情況下, 就需要使用加權隨機
來處理這些資料
下面是一種簡單的方案, 傳入權重的列表(weights
), 然後會返回隨機結果的索引值(index), 比如我們傳入[2, 3, 5]
, 那麼就會隨機的返回0(概率0.2), 1(概率0.3), 2(概率0.5)
簡單的思路就是把所有的權重加和, 然後隨機乙個數, 看看落在哪個區間
import random
def weighted_choice(weights):
totals =
running_total = 0
for w in weights:
running_total += w
rnd = random.random() * running_total
for i, total in enumerate(totals):
if rnd < total:
return i
上面這個方法看起來非常簡單, 已經可以完成我們所要的加權隨機, 然是最後的這個for
迴圈貌似有些囉嗦,python
有個內建方法bisect
可以幫我們加速這一步
import random
import bisect
def weighted_choice(weights):
totals =
running_total = 0
for w in weights:
running_total += w
rnd = random.random() * running_total
return bisect.bisect_right(totals, rnd)
bisect
方法可以幫我們查詢rnd
在totals
裡面應該插入的位置, 兩個方法看起來差不多, 但是第二個會更快一些, 取決於weights
這個陣列的長度, 如果長度大於1000, 大約會快30%左右
其實在這個方法裡面totals
這個陣列並不是必要的, 我們調整下策略, 就可以判斷出weights
中的位置
def weighted_choice(weights):
rnd = random.random() * sum(weights)
for i, w in enumerate(weights):
rnd -= w
if rnd < 0:
return i
這個方法比第二種方法竟然快了一倍, 當然, 從演算法角度角度, 複雜度是一樣的, 只不過我們把賦值臨時變數的功夫省下來了, 其實如果傳進來的weights
是已經按照從大到小排序好的話, 速度會更快, 因為rnd遞減的速度最快(先減去最大的數)
如果我們使用同乙個權重陣列weights
, 但是要多次得到隨機結果, 多次的呼叫weighted_choice
方法,totals
變數還是有必要的, 提前計算好它, 每次獲取隨機數的消耗會變得小很多
class weightedrandomgenerator(object):
def __init__(self, weights):
self.totals =
running_total = 0
for w in weights:
running_total += w
def next(self):
rnd = random.random() * self.totals[-1]
return bisect.bisect_right(self.totals, rnd)
def __call__(self):
return self.next()
在呼叫次數超過1000次的時候,weightedrandomgenerator
的速度是weighted_choice
的100倍
所以我們在對同一組權重列表進行多次計算的時候選擇方法4, 如果少於100次, 則使用方法3
在python3.2
之後, 提供了乙個itertools.accumulate
方法, 可以快速的給weights
求累積和
>>>> from itertools import accumulate
>>>> data = [2, 3, 5, 10]
>>>> list(accumulate(data))
[2, 5, 10, 20]
參考文章: weighted random generation in python
本文發表在致趣技術團隊部落格, 加入致趣
加權隨機演算法
本文參考 加權平均演算法一般應用於某個場景,有乙個集合s,裡面有a,b,c,d這四項。這時我們想隨機從中抽取一項,但是抽取的概率不同。比如我們希望抽到a的概率是50 抽到b和c的概率是20 d的概率是10 一般來說,我們可以給各項附乙個權重,抽取的概率正比於這個權重。那麼上述集合就成了 方法一 然後...
加權隨機演算法
public class weightedrandom 計算權重總和 int totalweights 0 for int i 0 i list.count i 隨機賦值權重 system.random ran new system.random getrandomseed getrandomsee...
加權輪詢和加權隨機演算法
今天在看 大型分布式 架構設計與實踐 一書中,看到了一種比較簡單的加權的演算法,在這裡記下來 var serverweightmap new dictionary serverweightmap.add 192.168.1.100 1 serverweightmap.add 192.168.1.10...