遊戲伺服器開發還真會常遇到,策劃需求根據權重作概率發獎勵,比如獎勵和權重分別是:a10、b20、c70,這時候出現a的概率就要是10%,b就是20%,c是70%,就是出現的概率是當前權重 / 總權重。該怎麼設計演算法呢?抽多個獎勵id並且每個獎勵id只能出現一次時候改怎樣實現呢?
通過總權重隨機值,再線性掃瞄,通過權重隨機值去查詢所在的權重區間。
時間複雜度:o(n)
過程:1、先計算出所有道具的權重總和s
2、然後呼叫隨機函式得到乙個區間在[1, s]的隨機值n
3、掃瞄列表,如果n小於當前的權重,則返回當前道具
4、若n大於當前權重,則把n減去當前權重
# 測試資料
# [(獎勵id: 權重)]
pool_data = [
(1, 50),
(2, 20),
(3, 40),
(4, 10),
]def linear_random(pool_data):
sum_weight = sum(a[1] for a in pool_data)
n = random.randint(1, sum_weight)
for key, weight in pool_data:
if n <= weight:
return key
else:
n -= weight
在方法一的基礎上做優化,即權重大的(概率大的)排序在前面,即線性查詢時先從概率大的開始查詢,命中率會更高。
時間複雜度:o(n)
過程:1、將獎池根據權重從大到小排序
2、方法同上
# 測試資料
# [(獎勵id: 權重)]
pool_data = [
(1, 50),
(2, 20),
(3, 40),
(4, 10),
]def sort_linear_random(pool_data):
pool = sorted(pool_data, key=lambda a: a[1], reverse=true)
sum_weight = sum(a[1] for a in pool)
n = random.randint(1, sum_weight)
for key, weight in pool:
if n <= weight:
return key
else:
n -= weight
既然是在序的權重中查到隨機權重值,那就可以使用二分查詢
時間複雜度:o(logn)
過程:1、先計算出所有道具的權重總和s
2、然後呼叫隨機函式得到乙個區間在[1, s]的隨機值n
3、重建權重表,列表中每個權重是前面所有權重值的總和,得到有序權重表
3、根據n在有序權重表二分查詢
# 測試資料
# [(獎勵id: 權重)]
pool_data = [
(1, 50),
(2, 20),
(3, 40),
(4, 10),
]def binary_random(pool_data):
add = 0
pool =
for k, w in pool_data:
add += w
sum_weight = add
n = random.randint(1, sum_weight)
mid, left, right, = 0, 0, len(pool) - 1
while left < right: # 二分查詢
mid = (right + left) // 2
key, mid_num = pool[mid]
if mid_num < n:
left = mid + 1
elif mid_num > n:
right = mid
else:
return key
return pool[mid][0]
基於方法一線性掃瞄的優化,線性掃瞄既然每次只移動乙個位置,既然權重表是從大到小排序,後面的權重值就一定小於等於當前值,就可以一次移動多個位置。隨機權重值 / 當前權重值 = 3,下次可以直接移動3個位置。
時間複雜度:o(n)
過程:1、先計算出所有道具的權重總和s
2、然後呼叫隨機函式得到乙個區間在[1, s]的隨機值n
3、重建權重表,列表中每個權重是前面所有權重值的總和,得到有序權重表
3、通過n / 當前權重,得到的值j大於0,則是j是要跳躍得階級數(因為有序,後面的權重肯定比當前小)
# 測試資料
# [(獎勵id: 權重)]
pool_data = [
(1, 50),
(2, 20),
(3, 40),
(4, 10),
]def jump_random(pool_data):
sort_pool = sorted(pool_data, key=lambda a: a[1], reverse=true)
add = 0
pool =
for key, weight in sort_pool:
add += weight
sum_weight = add
pool_length = len(pool)
n = random.randint(1, sum_weight)
i = 0
while i < pool_length - 1:
key, weight = pool[i]
if weight > n:
return key
else:
multiple = n // weight
i += multiple
return pool[i][0]
時間複雜度o(1)
從乙個獎池多次抽取獎勵,並且個獎勵id只能出現一次。
很簡單,每次抽完就把總權重減去當前抽中id的權重,再在新的總權重中去隨機生成權重值。
可以用字典來實現刪減操作。
#
pool_dict =
def times_linear(pool_dict, cnt):
pool = pool_dict.copy()
sum_weight = sum(pool.values())
rst =
for _ in range(cnt):
n = random.randint(1, sum_weight)
for key, weight in pool.items():
if n <= weight:
sum_weight -= weight
del pool[key]
break
else:
n -= weight
return rst
總結:抽取量大、權重較多的時候做優化還是非常必要的。
從乙個檔案中隨機抽取N行方法
從m行的檔案隨機抽取n行 可以假定m n 這是需要對資料進行抽樣處理時很長常見的需求。首先想到的方法是每讀取一行,扔乙個0到m 1的隨機數,如果隨機數小於n,則輸出該行,否則不輸出。perl源 如下 usr bin perl subset.pl usage sub set.pl file sampl...
關於生成乙個隨機數組
生成隨機數的最基本 是 random rand new random int i rand.next 0,100 next函式的引數可以為空,也可以是乙個值的範圍。像這種方法通常生成乙個隨機數不會有問題,但是如果要生成乙個隨機數組的話就不見得好用了,比如 listlstrnd new list ra...
關於生成乙個隨機數組
生成隨機數的最基本 是 random rand new random int i rand.next 0,100 next函式的引數可以為空,也可以是乙個值的範圍。像這種方法通常生成乙個隨機數不會有問題,但是如果要生成乙個隨機數組的話就不見得好用了,比如 listlstrnd new list ra...