【问题标题】:How to choose an item in a list according to a specific probability?如何根据特定概率选择列表中的项目?
【发布时间】:2017-04-22 21:29:26
【问题描述】:

假设我们得到了如下列表:

list = [[A,10,3],[B,5,2],[C,8,1]]

对于列表中的每个项目,都有一个被选择的概率,可以通过 softmax 计算。例如,对于第一个元素 (A),我们有:

from math import exp

A_probability = exp(list[0][2]/list[0][1] /
                     (exp(list[0][2]/list[0][1]) +
                      exp(list[1][2]/list[1][1]) +
                      exp(list[2][2]/list[2][1])))

如何根据每一项的计算概率随机选择列表中的项?

【问题讨论】:

标签: python-3.x random probability-density


【解决方案1】:

我假设您对于列表中的每个索引(例如 data)都有一个预先计算的概率列表(例如 probs),您想从中进行选择。

此外,probsdata 显然必须具有相同的长度,并且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. &lt;= 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

我刚刚了解到numpynumpy.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))

【讨论】:

  • 使用numpy,你可以用np.cumsum预处理probs,然后对结果进行二分查找。
  • 事实上,如果您不介意,我想发布一个关于此的答案。或者您可以将其包含在您自己的中。
  • @Mad Physicist 请发表你的答案,我不介意。我不是 Python 程序员。只是在这里涉足:)
  • 老实说,我没想到有人会这么详细地回答我的问题。非常感谢。
  • @Masoud Masoumi Moghadam 总是乐于提供帮助。好吧,如果它解决了你的问题,那么请不要忘记接受并投票:)
猜你喜欢
  • 1970-01-01
  • 2011-02-15
  • 2012-09-14
  • 1970-01-01
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
相关资源
最近更新 更多