生成具有给定(数字)分布的随机数

paf*_*fcu 108 python random module

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

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2
Run Code Online (Sandbox Code Playgroud)

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

我需要这个,因为我想生成一个生日列表(不遵循标准random模块中的任何分发).

Sve*_*ach 100

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])
Run Code Online (Sandbox Code Playgroud)

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

  • 它与原始问题完全相同.例如:`numpy.random.choice(numpy.arange(1,7),p = [0.1,0.05,0.05,0.2,0.4,0.2])` (8认同)
  • 在我的机器上``numpy.random.choice()`快了近20倍. (7认同)
  • 如果您正在使用**Python 3.6**或更新版本,那么[**另一个答案**](/sf/answers/2929658651/)不需要任何插件包. (3认同)
  • 令人惊讶的是,rv_discrete.rvs()在O(len(p)*size)时间和内存中工作!虽然choice()似乎以最佳O(len(p)+ log(len(p))*size)时间运行. (2认同)

Mar*_*son 95

从Python 3.6开始,Python的标准库就有了解决方案,即random.choices.

用法示例:让我们设置一个与OP问题匹配的总体和权重:

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
Run Code Online (Sandbox Code Playgroud)

现在choices(population, weights)生成一个样本:

>>> choices(population, weights)
4
Run Code Online (Sandbox Code Playgroud)

可选的仅关键字参数k允许一次请求多个样本.这很有价值,因为random.choices在生成任何样本之前,每次调用时都需要做一些准备工作; 通过一次生成许多样本,我们只需要做一次准备工作.在这里,我们生成了一百万个样本,并用于collections.Counter检查我们得到的分布大致与我们给出的权重相匹配.

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})
Run Code Online (Sandbox Code Playgroud)


sdc*_*vvc 27

使用CDF生成列表的一个优点是您可以使用二进制搜索.虽然您需要O(n)时间和空间进行预处理,但您可以在O(k log n)中获得k个数字.由于普通的Python列表效率低下,因此可以使用array模块.

如果你坚持不变的空间,你可以做以下事情; 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
Run Code Online (Sandbox Code Playgroud)


Ram*_*nez 14

也许有点晚了.但你可以使用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])
Run Code Online (Sandbox Code Playgroud)

  • `numpy.random.choice()`与`random.choice()`完全不同,支持概率分布. (4认同)

Mar*_*tos 13

(好吧,我知道你要求收缩包装,但也许那些本土解决方案根本不够简洁,不符合你的喜好.:-)

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)
Run Code Online (Sandbox Code Playgroud)

我伪确认这是通过观察这个表达式的输出来起作用的:

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))
Run Code Online (Sandbox Code Playgroud)


Mar*_*hke 8

我编写了一个从自定义连续分布中抽取随机样本的解决方案。

我需要这个用于与您类似的用例(即生成具有给定概率分布的随机日期)。

你只需要功能random_custDist和线samples=random_custDist(x0,x1,custDist=custDist,size=1000)。剩下的就是装饰^^。

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label='sample distribution')
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label='custom distribustion (custDist)', color='C1', linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()
Run Code Online (Sandbox Code Playgroud)

连续自定义分布和离散样本分布

这个解决方案的性能肯定可以提高,但我更喜欢可读性。