生成加权随机数

5

嗨,我正在为一个基因组学课程编写一些代码,但在某个部分遇到了困难。

我有一组互斥事件 event1, event2, ... eventn,其概率为 p1, p2, ... pn

我想要随机模拟给定概率下对一个事件进行n次采样。

输入:概率 = {0.3, 0.2, 0.5} 事件{e1,e2,e3} n=100

输出:e3大约应该有50个结果,e2有大约20个结果,e1有大约30个结果。请注意,这些可能不是完全准确的50、20、30,因为实际值与理论值不同...


如果probabilitiesevents输入都是集合,就像你展示的那样,那么就没有办法将概率与事件匹配起来。 - abarnert
你只是想生成 n 个随机数吗? - Leigh
3
请见Eli Bendersky的页面,其中讨论了多种实现加权随机抽样的方法。链接为:http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/。 - DSM
3
请参见Python问题18844,其中测试了几个加权选择实现,以便将其中一个添加到“random”模块中。 - Blckknght
2个回答

5

Python本身没有内置任何加权抽样功能(NumPy/SciPy提供了此功能),但对于这样一个非常简单的情况,实现起来相当容易:

import itertools
import random

probabilities = [0.3, 0.2, 0.5]
totals = list(itertools.accumulate(probabilities))

def sample():
    n = random.uniform(0, totals[-1])
    for i, total in enumerate(totals):
        if n <= total:
            return i

如果您没有Python 3.2以上版本,则无法使用accumulate函数;如果列表确实很短,您可以使用低效的一行代码方法来模拟它:

totals = [sum(probabilities[:i+1]) for i in range(len(probabilities))]

...或者您可以编写显式的循环,或者一个丑陋的reduce调用,或者从文档中复制相应的Python函数。


另外,请注意,如果您确信数字总和为1.0,则random.uniform(0, totals[-1])只是写random.random()的一种更复杂的方式。


测试此方法的快速方式:

>>> samples = [sample() for _ in range(100000)]
>>> samples.count(0)
29878
>>> samples.count(1)
19908
>>> samples.count(2)
50214

这些数字分别接近于总数100000的30%,20%和50%。

我尝试过这个,但它总是给出最后一项的索引。为什么会这样? - user2812970
@user2812970:我刚刚将这段代码复制粘贴到我的解释器中(并添加了缺失的import random),并运行了100K次进行测试,它只有大约一半的时间会给出最后一个项的地址,这正是应该的。我已经编辑了答案以显示测试结果。如果它真的总是给你2,那么要么你复制粘贴错了,要么你在代码的其他部分做错了什么,要么你应该立即去拉斯维加斯利用你的突变概率影响力量。 :) - abarnert

3
假设我们有三个事件,每个事件的概率分别为0.3、0.2和0.5。对于每个生成的样本,我们在范围[0,1)内生成一个数字,称之为“rand”。如果“rand”小于0.3,则生成事件1;如果0.3 <= “rand” < 0.5,则生成事件2;否则生成事件3。这可以使用random()函数实现,该函数确实生成在范围[0,1)内的数字。请参考random()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接