【问题标题】:Fast way to obtain a random index from an array of weights in python从python中的权重数组获取随机索引的快速方法
【发布时间】:2014-07-31 03:56:29
【问题描述】:

我经常发现自己需要对数组或列表进行随机索引,其中索引的概率不是均匀分布的,而是根据某些正权重。获得它们的快速方法是什么?我知道我可以将权重作为可选参数p 传递给numpy.random.choice,但是该函数似乎很慢,并且构建一个arange 来传递它也不理想。权重之和可以是任意正数且不保证为 1,这使得在 (0,1] 中生成随机数然后减去权重条目直到结果为 0 或更小的方法是不可能的。

虽然有关于如何以简单的方式实现类似事情的答案(主要不是获取数组索引,而是获取相应的元素),例如Weighted choice short and simple,但我正在寻找一个快速的解决方案,因为适当的函数经常执行。我的权重经常变化,因此构建别名掩码之类的开销(详细介绍可以在http://www.keithschwarz.com/darts-dice-coins/ 找到)应该被视为计算时间的一部分。

【问题讨论】:

标签: python algorithm random


【解决方案1】:

累加和平分

在任何一般情况下,似乎建议计算权重的累积总和,并使用 bisect 模块中的 bisect 在生成的排序数组中找到一个随机点

def weighted_choice(weights):
    cs = numpy.cumsum(weights)
    return bisect.bisect(cs, numpy.random.random() * cs[-1])

如果速度是一个问题。下面给出更详细的分析。

注意:如果数组不是平面的,numpy.unravel_index 可以用来将平面索引转换为整形索引,见https://stackoverflow.com/a/19760118/1274613

实验分析

使用numpy 内置函数有四种或多或少明显的解决方案。使用timeit 比较它们会得到以下结果:

import timeit

weighted_choice_functions = [
"""import numpy
wc = lambda weights: numpy.random.choice(
    range(len(weights)),
    p=weights/weights.sum())
""",
"""import numpy
# Adapted from https://stackoverflow.com/a/19760118/1274613
def wc(weights):
    cs = numpy.cumsum(weights)
    return cs.searchsorted(numpy.random.random() * cs[-1], 'right')
""",
"""import numpy, bisect
# Using bisect mentioned in https://stackoverflow.com/a/13052108/1274613
def wc(weights):
    cs = numpy.cumsum(weights)
    return bisect.bisect(cs, numpy.random.random() * cs[-1])
""",
"""import numpy
wc = lambda weights: numpy.random.multinomial(
    1,
    weights/weights.sum()).argmax()
"""]

for setup in weighted_choice_functions:
    for ps in ["numpy.ones(40)",
               "numpy.arange(10)",
               "numpy.arange(200)",
               "numpy.arange(199,-1,-1)",
               "numpy.arange(4000)"]:
        timeit.timeit("wc(%s)"%ps, setup=setup)
    print()

结果输出是

178.45797914802097
161.72161589498864
223.53492237901082
224.80936180002755
1901.6298267539823

15.197789980040397
19.985687876993325
20.795070077001583
20.919113760988694
41.6509403079981

14.240949985047337
17.335801470966544
19.433710905024782
19.52205040602712
35.60536142199999

26.6195822560112
20.501282756973524
31.271995796996634
27.20013752405066
243.09768892999273

这意味着 numpy.random.choice 非常慢,甚至专用的 numpy searchsorted 方法也比 type-naive bisect 变体慢。 (这些结果是使用 Python 3.3.5 和 numpy 1.8.1 获得的,因此对于其他版本可能会有所不同。)基于 numpy.random.multinomial 的函数对于大权重的效率低于基于累积求和的方法。据推测,argmax 必须遍历整个数组并在每一步运行比较这一事实起着重要作用,这也可以从增加和减少权重列表之间的 4 秒差异中看出。

【讨论】:

    猜你喜欢
    • 2010-11-12
    • 2019-09-21
    • 2011-03-30
    • 2010-10-31
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    相关资源
    最近更新 更多