我假设您对于列表中的每个索引(例如 data)都有一个预先计算的概率列表(例如 probs),您想从中进行选择。
此外,probs 和data 显然必须具有相同的长度,并且probs 的条目必须是非负数和1。
有一种简洁而简单的技术可以根据probs 中的分布随机选择data 中的索引,称为轮盘赌。在 Python 中,我相信它应该看起来像这样
import random
data = ['A', 'B', 'C', 'D']
probs = [0.2, 0.4, 0.3, 0.1]
def roulette_wheel(probs):
rand = random.random()
for slot, prob in enumerate(probs):
rand -= prob
if rand < 0.0:
return slot
请注意,通过将 rand 乘以术语 sum(weights),可以将其推广到非负权重列表(不必相加到 1)。我相信,我在很久以前的一本关于 Pascal 编程的书中第一次看到这个可爱的想法。
编辑:
正如 MadPhysicist 在 comment 中建议的那样,如果需要从相同的数据中重复绘制,这可以提高效率。在这种情况下,可以预先计算累积分布函数,然后对索引进行二分搜索,例如cumulative prob. <= rand ~ U(0, 1)。例如,在 Python 中,这可能类似于以下内容
from random import random
from bisect import bisect_right
def cdf(probs):
cdf = []
total = 0.0
for p in probs:
total += p
cdf.append(total)
return cdf
def roulette_wheel_bisect(cdf):
return bisect_right(cdf, random())
# compute cdf
cumsum = cdf(probs)
# randomly draw 10 indexes
for i in range(0, 10):
print(roulette_wheel_bisect(cumsum))
免责声明:我不是一个专业的 Python 程序员,所以上面的代码应该只是说明一般的想法。对于实际用途,它可能不是很健壮。如果可以的话,您应该始终使用经过良好测试的标准库,例如numpy。
编辑2:
我刚刚了解到numpy 有numpy.random.choice,这正是您所需要的。示例:
from numpy import random
data = ['A', 'B', 'C', 'D']
probs = [0.2, 0.4, 0.3, 0.1]
# randomly draw 10 list elements with replacement
for i in range(0, 10):
print(random.choice(data, p=probs))