python中从权重数组获取随机索引的快速方法

python中从权重数组获取随机索引的快速方法,python,algorithm,random,Python,Algorithm,Random,我经常发现自己需要一个数组或列表的随机索引,其中索引的概率不是均匀分布的,而是根据一定的正权重。什么是获得它们的快速方法?我知道我可以将权重传递给numpy.random.choice作为可选参数p,但该函数似乎相当慢,并且构建arange来传递它也不理想。权重之和可以是任意正数,并且不能保证为1,这使得在(0,1)中生成随机数,然后减去权重项直到结果为0或更小的方法不可能 虽然对于如何以简单的方式实现类似的事情(主要不是获取数组索引,而是相应的元素)有一些答案,例如,我正在寻找一个快速的解决方

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

虽然对于如何以简单的方式实现类似的事情(主要不是获取数组索引,而是相应的元素)有一些答案,例如,我正在寻找一个快速的解决方案,因为适当的函数经常被执行。我的权重经常变化,因此构建别名掩码之类的东西的开销很大(上有详细介绍)应被视为计算时间的一部分。

累积求和和对分 在任何一般情况下,都建议计算权重的累积和,并使用对分模块中的对分来查找结果排序数组中的随机点

def weighted_choice(weights):
    cs = numpy.cumsum(weights)
    return bisect.bisect(cs, numpy.random.random() * cs[-1])
如果速度是一个问题,下面给出更详细的分析

注意:如果数组不是平面的,
numpy.unravel\u index
可用于将平面索引转换为形状索引,如中所示

实验分析 使用
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
方法也比类型naive
bisect
变体慢。(这些结果是使用Python3.3.5和numpy 1.8.1获得的,因此其他版本的情况可能不同。)基于
numpy.random.multinomial
的函数对于大权重的效率低于基于累积求和的方法。argmax必须迭代整个数组并运行比较,这一事实在每一步中都起着重要作用,从递增和递增之间的四秒差也可以看出这一点减少权重列表。

由于效率限制,我将收回我的收盘价,但相关:,