繁体   English   中英

python - 如何在Python中给定连续PDF生成单个随机数?

[英]How to generate a single random number given a continuous PDF in Python?

我有一个需要生成的数字的给定 PDF: p(z) = N (1 + (1-z)^2) /z

其中z被限制在某个最小值z01和适当的N以进行归一化。

我需要生成遵循此分布的单个数字,我什至不知道从哪里开始。 我四处搜索,但只找到了 PDF 用于离散值而不是连续值的解决方案。

我已经在numpyscipy中搜索了已经实现的方法,但我没有找到任何有用的东西,但我可能错过了一些东西。

在这里,您有一个在闭合区间(即 [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],然后再将它们传递给unnormpdfcdf函数。 同样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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM