离散别名罐 (DAU)#
需要:概率向量 (PV) 或 PMF 以及有限域
速度
设置:慢(随向量长度线性增长)
采样:非常快
DAU 从长度为 N 的任意但有限概率向量 (PV) 的分布中采样。该算法基于 A.J. Walker 的一个巧妙方法,需要一个大小至少为 N 的表。它需要一个随机数,并且每个生成的随机变量只需要进行一次比较。构建表的设置时间为 O(N)。
>>> import numpy as np
>>> from scipy.stats.sampling import DiscreteAliasUrn
>>>
>>> pv = [0.18, 0.02, 0.8]
>>> urng = np.random.default_rng()
>>> rng = DiscreteAliasUrn(pv, random_state=urng)
>>> rng.rvs()
0 # may vary
默认情况下,概率向量的索引从 0 开始。但是,这可以通过传递 domain
参数来更改。当 domain
与 PV 结合使用时,它会将分布从 (0, len(pv))
重新定位到 (domain[0]
, domain[0] + len(pv))
。在这种情况下,domain[1]
将被忽略。
>>> rng = DiscreteAliasUrn(pv, domain=(10, 13), random_state=urng)
>>> rng.rvs()
12 # may vary
该方法在没有概率向量但给定了 PMF 的情况下也可以工作。在这种情况下,必须通过显式传递 domain
参数或在分布对象中提供 support
方法来给出有限的域。
>>> class Distribution:
... def __init__(self, c):
... self.c = c
... def pmf(self, x):
... return x**self.c
... def support(self):
... return (0, 10)
...
>>> dist = Distribution(2)
>>> rng = DiscreteAliasUrn(dist, random_state=urng)
>>> rng.rvs()
10 # may vary
>>> import matplotlib.pyplot as plt
>>> from scipy.stats.sampling import DiscreteAliasUrn
>>> class Distribution:
... def __init__(self, c):
... self.c = c
... def pmf(self, x):
... return x**self.c
... def support(self):
... return (0, 10)
...
>>> dist = Distribution(2)
>>> urng = np.random.default_rng()
>>> rng = DiscreteAliasUrn(dist, random_state=urng)
>>> rvs = rng.rvs(1000)
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111)
>>> x = np.arange(1, 11)
>>> fx = dist.pmf(x)
>>> fx = fx / fx.sum()
>>> ax.plot(x, fx, 'bo', label='true distribution')
>>> ax.vlines(x, 0, fx, lw=2)
>>> ax.hist(rvs, bins=np.r_[x, 11]-0.5, density=True, alpha=0.5, color='r',
... label='samples')
>>> ax.set_xlabel('x')
>>> ax.set_ylabel('PMF(x)')
>>> ax.set_title('Discrete Alias Urn Samples')
>>> plt.legend()
>>> plt.show()
注意
由于 DiscreteAliasUrn
预计 PMF 的签名为 def pmf(self, x: float) -> float
,它首先使用 np.vectorize
将 PMF 向量化,然后在域中的所有点上进行评估。但是,如果 PMF 已经向量化,则只需在域中的每个点上对其进行评估并传递获得的 PV 以及域会快得多。例如,SciPy 离散分布的 pmf
方法是向量化的,可以通过以下方式获得 PV:
>>> from scipy.stats import binom
>>> from scipy.stats.sampling import DiscreteAliasUrn
>>> dist = binom(10, 0.2) # distribution object
>>> domain = dist.support() # the domain of your distribution
>>> x = np.arange(domain[0], domain[1] + 1)
>>> pv = dist.pmf(x)
>>> rng = DiscreteAliasUrn(pv, domain=domain)
这里需要域来重新定位分布。
通过传递 urn_factor
参数来更改所用表的尺寸,性能可能会略微受到影响。
>>> # use a table twice the length of PV.
>>> urn_factor = 2
>>> rng = DiscreteAliasUrn(pv, urn_factor=urn_factor, random_state=urng)
>>> rng.rvs()
2 # may vary
注意
建议将此参数保持在 2 以下。