[英]How to generate a single random number given a continuous PDF in Python?
我有一个需要生成的数字的给定 PDF: p(z) = N (1 + (1-z)^2) /z
其中z被限制在某个最小值z0到1和适当的N以进行归一化。
我需要生成遵循此分布的单个数字,我什至不知道从哪里开始。 我四处搜索,但只找到了 PDF 用于离散值而不是连续值的解决方案。
我已经在numpy和scipy中搜索了已经实现的方法,但我没有找到任何有用的东西,但我可能错过了一些东西。
在这里,您有一个在闭合区间(即 [z0, 1])上递减的 PDF。 此外,PDF 的范围随着 z0 的减小而增加,从而使诸如以下的简单拒绝采样器效率低下:
def rejection(z0):
pdfz0=pdf(z0,z0) # For pdf, see below
while True:
x=random.uniform(z0,1)
if random.random()*pdfz0 < pdf(x,z0):
return x
不过,有一种方法可以解决这个问题,这称为反转拒绝方法,在 Devroye, L., "Non-Uniform Random Variate Generation" (1986), 第 7 章第 4 节中有描述。这种方法需要找到分布的 PDF 和 CDF(并且还要求 PDF 在闭合区间上递减,就像这里的情况一样)。 以下代码使用 SymPy 库来查找 PDF 和 CDF。
#
# Using SymPy to find the CDF
#
pdfunnorm=((1 - z)**2 + 1)/z
# 1 divided by the `N` in your question (a normalization constant)
pdfnorm=integrate(pdfunnorm,(z,y,1))
ppdf=(pdfunnorm/pdfnorm).simplify()
pprint(ppdf)
pcdf=integrate(ppdf.subs(z,t),(t,y,z)).simplify()
pprint(pcdf)
因此,PDF 和 CDF 可以写成如下。 还显示了未规范化的 PDF,因为无论 PDF 是否被规范化(在其域上积分为 1),以下算法都有效:
def unnormpdf(z,y):
return (1+(1-z)**2)/z
def pdf(z,y):
return -(2*(z - 1)**2 + 2)/(z*(y**2 - 4*y + 4*math.log(y) + 3))
def cdf(z,y):
return (y**2 - 4*y - z**2 + 4*z + 4*math.log(y) - 4*math.log(z))/(y**2 - 4*y + 4*math.log(y) + 3)
现在我们使用反转拒绝算法来减少 [0, 1] 上的 PDF。 请注意,该方法将一些变量从 [0, 1] 转移到 [z0, 1],然后再将它们传递给unnormpdf
和cdf
函数。 同样unnormpdf
不需要在其域上集成到 1。
def inversionRejection(unnormpdf, cdf, z0):
# Devroye, L., "Non-Uniform Random
# Variate Generation", p. 336.
u=random.random()
r=2
x=1/r
# NOTE: Here, x is scaled
# and shifted from [0, 1] to [z0, 1]
# before passing to CDF
while u<cdf(z0+x*(1-z0),z0):
x/=r
# Version of x that is scaled
# and shifted from [0, 1] to [z0, 1]
shiftedx=z0+x*(1-z0)
while True:
v=random.uniform(1,r)
w=random.random()
y=x*v
shiftedy=z0+y*(1-z0)
if w<=unnormpdf(shiftedy,z0)/unnormpdf(shiftedx,z0): # (**)
return shiftedy
对于您的 PDF,即使使用规范化 PDF, (**)
右侧的某些术语也可以抵消,留下一个没有log
s 和z0
的表达式:
if w<=shiftedx*((shiftedy - 1)**2 + 1)/(shiftedy*((shiftedx - 1)**2 + 1)):
以下是我机器上的时间:
In [209]: timeit inversionRejection(pdf,cdf,0.001)
6.7 µs ± 31.5 ns per loop (mean ± std. Dev. Of 7 runs, 100000 loops each)
In [210]: timeit inversionRejection(unnormpdf,cdf,0.001)
6.8 µs ± 103 ns per loop (mean ± std. Dev. Of 7 runs, 100000 loops each)
In [211]: timeit rejection(0.001)
129 µs ± 1.77 µs per loop (mean ± std. Dev. Of 7 runs, 10000 loops each)
或者,以下算法(来自 Devroye 的书第 312 页)利用了您的密度正在减少并在闭合区间上定义的事实。 此外,它只需要(标准化)PDF,而不需要 CDF。
def monbounded(pdf, z0):
# For decreasing densities on [0, 1]
m = pdf(0, z0)
while True:
u=random.random()
if u<1/(1+math.log(m)):
x=random.uniform(0,1/(m))
if random.random()*m <= pdf(x,z0): return x
else:
x=math.exp(u*(1+math.log(m))-1)/m
if random.random() <= x*pdf(x,z0): return x
def transpdf(x, z0):
xpos=z0+x*(1-z0)
return pdf(xpos,z0)*(1-z0)
def transmonbounded(z0):
# For the PDF in your question.
# Transform [z0,1] to [0,1].
return z0+monbounded(transpdf, z0)*(1-z0)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.