Python中的加權隨機

2021-06-28 00:39:13 字數 2606 閱讀 1182

我們平時比較多會遇到的一種情景是從一堆的資料中隨機選擇乙個, 大多數我們使用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方法可以幫我們查詢rndtotals裡面應該插入的位置, 兩個方法看起來差不多, 但是第二個會更快一些, 取決於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...