我有一个文件,不同的值的一些概率,例如:

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

我想用这个分布生成随机数。是否存在处理此问题的现有模块?自己编写代码是相当简单的(构建累积密度函数,生成一个随机值[0,1]并选择相应的值),但这似乎应该是一个常见的问题,可能有人已经为它创建了一个函数/模块。

我需要这个,因为我想生成一个生日列表(它不遵循标准随机模块中的任何分布)。


当前回答

scipy.stats。Rv_discrete可能是您想要的。您可以通过values参数提供您的概率。然后,您可以使用分布对象的rvs()方法来生成随机数。

正如Eugene Pakhomov在评论中指出的那样,你也可以将p关键字参数传递给numpy.random.choice(),例如:

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

如果你使用的是Python 3.6或更高版本,你可以使用标准库中的random.choices() -请参阅Mark Dickinson的回答。

其他回答

from __future__ import division
import random
from collections import Counter


def num_gen(num_probs):
    # calculate minimum probability to normalize
    min_prob = min(prob for num, prob in num_probs)
    lst = []
    for num, prob in num_probs:
        # keep appending num to lst, proportional to its probability in the distribution
        for _ in range(int(prob/min_prob)):
            lst.append(num)
    # all elems in lst occur proportional to their distribution probablities
    while True:
        # pick a random index from lst
        ind = random.randint(0, len(lst)-1)
        yield lst[ind]

验证:

gen = num_gen([(1, 0.1),
               (2, 0.05),
               (3, 0.05),
               (4, 0.2),
               (5, 0.4),
               (6, 0.2)])
lst = []
times = 10000
for _ in range(times):
    lst.append(next(gen))
# Verify the created distribution:
for item, count in Counter(lst).iteritems():
    print '%d has %f probability' % (item, count/times)

1 has 0.099737 probability
2 has 0.050022 probability
3 has 0.049996 probability 
4 has 0.200154 probability
5 has 0.399791 probability
6 has 0.200300 probability

也许有点晚了。但是你可以使用numpy.random.choice(),传递p参数:

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

scipy.stats。Rv_discrete可能是您想要的。您可以通过values参数提供您的概率。然后,您可以使用分布对象的rvs()方法来生成随机数。

正如Eugene Pakhomov在评论中指出的那样,你也可以将p关键字参数传递给numpy.random.choice(),例如:

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

如果你使用的是Python 3.6或更高版本,你可以使用标准库中的random.choices() -请参阅Mark Dickinson的回答。

另一个答案,可能更快:)

distribution = [(1, 0.2), (2, 0.3), (3, 0.5)]  
# init distribution  
dlist = []  
sumchance = 0  
for value, chance in distribution:  
    sumchance += chance  
    dlist.append((value, sumchance))  
assert sumchance == 1.0 # not good assert because of float equality  

# get random value  
r = random.random()  
# for small distributions use lineair search  
if len(distribution) < 64: # don't know exact speed limit  
    for value, sumchance in dlist:  
        if r < sumchance:  
            return value  
else:  
    # else (not implemented) binary search algorithm  

使用CDF生成列表的一个优点是可以使用二分搜索。当你需要O(n)个时间和空间进行预处理时,你可以得到O(k log n)个数字。由于普通的Python列表效率很低,你可以使用数组模块。

如果你坚持空间不变,你可以做到以下几点;O(n)时间,O(1)空间。

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies