我有两个相同大小的列表,我们称之为elements
和weights
。我想选择elements
列表的一个元素,其中weights
给出了离散概率分布。 weight[i]
对应于选择elements[i]
的概率。 elements
永远不会更改,但在每次采样后,weights
都会更改(仅限值,而不是大小)。
我需要一种高效方式来执行大型列表。
我在Python中使用numpy.random.choice(elements, p=weights)
实现了一个实现,但是从一组k
中获取大小为n
的样本,其中k << n
的效率非常低。欢迎使用任何语言实现,但我主要使用Python。
(这用于使用 networkx的社交网络模拟。我有一个加权图和一个节点i
,我想从{{选择一个节点j
1}}邻居,其中每个节点的概率与i
和给定节点之间边缘的权重成比例。如果我将非邻居的概率设置为0,我不必生成每次邻居列表,我只需要一个所有节点的列表。)
它将像这样使用:
i
答案 0 :(得分:0)
我使用了以下内容。使用cumsum
将权重形成为累积分布函数,然后从反向cdf中进行采样。
wcs = weights.cumsum()
wcs = wcs / wcs[-1] # non-decreasing in (0:1]
u = np.random.uniform()
chosen = weights[(u < wcs).argmax()] # the first index above u
答案 1 :(得分:0)
@MarkBorgerding的方法很好,但可以改进:
getNumber
此外,它最终取决于实际数字,但不是将非邻居的概率归零,否则删除这些概率可能更有效;见下面的Timings第2部分。
时序:
W = weights.cumsum()
W.searchsorted(np.random.uniform(0, W[-1], nsamples))
选项,单个样本:
1000000
>>> from timeit import timeit
>>> kwds = dict(globals=globals(), number=100)
>>> weights = np.random.random(1000000)
>>>
>>> timeit("np.random.choice(1000000, 1, p=weights/weights.sum())", **kwds)
1.606048938119784
>>> timeit("W = weights.cumsum(); W/=W[-1]; (np.random.uniform()<W).argmax()", **kwds)
0.6634919850621372
>>> timeit("W = weights.cumsum(); W.searchsorted(np.random.uniform(0, W[-1]))", **kwds)
0.30993065400980413
个选项,1000000
个样本:
10
计时第2部分:
>>> timeit("np.random.choice(1000000, 10, p=weights/weights.sum())", **kwds)
1.606177378911525
>>> timeit("W = weights.cumsum(); W/=W[-1]; (np.random.uniform(0, 1, (10, 1))<W).argmax(axis=1)", **kwds)
1.4421172500588
>>> timeit("W = weights.cumsum(); W.searchsorted(np.random.uniform(0, W[-1], 10))", **kwds)
0.3154504559934139