加权版的random.choice

401 投票
28 回答
363062 浏览
提问于 2025-04-16 03:55

我需要写一个加权版的随机选择函数(列表中的每个元素被选中的概率不同)。这是我想到的:

def weightedChoice(choices):
    """Like random.choice, but each element can have a different chance of
    being selected.

    choices can be any iterable containing iterables with two items each.
    Technically, they can have more than two items, the rest will just be
    ignored.  The first item is the thing being chosen, the second item is
    its weight.  The weights can be any numeric values, what matters is the
    relative differences between them.
    """
    space = {}
    current = 0
    for choice, weight in choices:
        if weight > 0:
            space[current] = choice
            current += weight
    rand = random.uniform(0, current)
    for key in sorted(space.keys() + [current]):
        if rand < key:
            return choice
        choice = space[key]
    return None

我觉得这个函数有点复杂,而且看起来不太好。我希望大家能给我一些建议,看看怎么改进它或者有没有其他的做法。对我来说,代码的整洁和可读性比效率更重要。

28 个回答

144

在编程中,有时候我们需要处理一些数据,比如从一个地方获取数据,然后在程序中使用它。这个过程可能会涉及到不同的步骤,比如读取文件、处理数据、然后输出结果。

有些时候,我们会遇到错误或者问题,这时候就需要调试。调试就是找出程序哪里出错,并修正它。可以通过打印一些信息来帮助我们理解程序的运行情况。

另外,编程语言有很多种,每种语言都有自己的特点和用法。学习一门语言的基础知识是非常重要的,这样才能更好地理解如何编写程序。

总之,编程就像是解决一个个小谜题,通过不断尝试和学习,最终能够写出功能强大的程序。

def weighted_choice(choices):
   total = sum(w for c, w in choices)
   r = random.uniform(0, total)
   upto = 0
   for c, w in choices:
      if upto + w >= r:
         return c
      upto += w
   assert False, "Shouldn't get here"
397

从Python 3.6开始,random模块里有一个叫做 choices 的方法。

In [1]: import random

In [2]: random.choices(
...:     population=[['a','b'], ['b','a'], ['c','b']],
...:     weights=[0.2, 0.2, 0.6],
...:     k=10
...: )

Out[2]:
[['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b']]

需要注意的是,random.choices 这个方法是进行 有放回抽样,根据文档的说明:

返回一个大小为 k 的列表,列表中的元素是从总体中抽取的,并且是有放回的。

为了让回答更完整:

当从一个有限的总体中抽取一个样本,并在记录了它的特征后,将其放回总体中,然后再抽取下一个样本,这种抽样方式称为“有放回抽样”。这基本上意味着每个元素可能会被选择多次。

如果你需要进行 无放回抽样,那么正如@ronan-paixão的精彩回答所说,你可以使用numpy.choice,这个方法的 replace 参数可以控制这种行为。

424

从1.7.0版本开始,NumPy有了一个叫做choice的函数,它可以支持概率分布。

from numpy.random import choice
draw = choice(list_of_candidates, number_of_items_to_pick,
              p=probability_distribution)

需要注意的是,probability_distribution这个参数的顺序要和list_of_candidates保持一致。你还可以使用关键字replace=False来改变默认行为,这样抽取的项目就不会被替换。

撰写回答