帶權輪詢算法

問題

有Q一、Q二、……、Qn n個隊列,每一個隊列有一個權值W一、W二、……、Wn,須要每次從其中一個隊列取出一個元素,使得從不一樣隊列取出的元素數量比例服從權值的比例。html

解釋

這正是網絡流量調度場景中的「帶權輪詢調度」(Weighted Round-Robin Scheduling,WRR),有現成的算法可用。python

爲了簡單起見,先考慮最簡單的狀況,令 W1 = W2 = ... = Wn,那麼「帶權輪詢調度」退化成「輪詢調度」(Round-Robin Scheduling,RR),RR實現很簡單,而後考慮權值不一樣的狀況。算法

實現(python代碼)

RR

# count
N = 3

# Round-Robin Scheduling
def rr_select():
    last = N - 1
    while True:
        current = (last + 1) % N
        last = current
        yield current

rr_test = rr_select()
for i in range(1000):
    print(rr_test.__next__())

N是隊列的個數,0到N-1數字表明這N個隊列。微信

RR會依次從每一個隊列取出元素,很簡單無需過多敘述。網絡

WRR

# count
N = 3

weight = (60, 30, 10)

# 最大公約數
def gcd(nums):
    m = nums[0]
    for n in nums[1:]:
        while n != 0:
            m, n = n, m % n
    return m

# Weighted Round-Robin Scheduling
def wrr_select():
    current = N - 1
    current_weight = 0

    while True:
        current = (current + 1) % N
        if current == 0:
            current_weight -= gcd(weight)
            if current_weight <= 0:
                current_weight = max(weight)
        if weight[current] >= current_weight:
            yield current

wrr_test = wrr_select()
for i in range(1000):
    print(wrr_test.__next__())

這個算法須要解釋一下。code

先看一下取前10個元素的結果:htm

current_weight     從哪些隊列取出了元素
60                 0
50                 0
40                 0
30                 0 1
20                 0 1
10                 0 1 2

也就是每次for i in (0, 1, 2)的小週期內,當current_weight > weight[i]時,就把i選出來。當current_weight等於0了,就再從頭開始,這算一個大週期。一個大週期包含max(weight)/gcd(weight)個小週期。blog

那如何證實這樣取是符合權值比例的?隊列

能夠看到每一個小週期中,都是要從權值最大的隊列裏拿走一個元素的,能夠看做拿權值最大的那個做爲基準,而後權值較小的直接拿它對比。那僅看權值爲10的即可,10是60的1/6,把60分6分,只有1份是應該給10的,因此60知道降到10才知足10的條件。權值30的同理。ip

其實max(weight)和gcd(weight)均可以選擇別的,但選它們兩個能夠知足最細粒度的平均,即每取出任意10個連續的中間結果,就必然服從權值比例,能夠認爲是最優的。

隨機性考慮

WRR的運行結果是固定的,若是須要考慮隨機性的話,須要再作一些額外工做。簡單的話能夠先對隊列的順序作隨機,但這樣實際的順序仍是固定的。能夠按實際須要頻繁暫存必定(隨機)數量的結果,再隨機處理後依次輸出。

參考

付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活訂價,歡迎諮詢,微信 ly50247。

相關文章
相關標籤/搜索