AliasTable
典型的多项式分布的复杂度为 O ( k ) O(k) O(k), k k k为多项式分布的大小,那么能否将 O ( k ) O(k) O(k)的复杂度降为 O ( 1 ) O(1) O(1)呢?Alias方法就是这样的一种方法。
原理
对于概率分布
p
i
p_i
pi其中
i
∈
{
1
,
⋯
,
l
}
i\in\{1,\cdots,l\}
i∈{1,⋯,l},Alias方法要达到什么目的呢?Alias的目的是通过对原有的
l
l
l个概率值
p
i
p_i
pi进行分解和合并,使得新形成的
l
l
l个概率值均等于
1
/
l
1/l
1/l,且每个概率值最多来自原来的两个不同的概率值。用形象的话来描述的话,最后我们会得到
l
l
l个桶,每个桶内最多有两个颜色。得到转换后的结果,那么采样的话只需要两次就可以达到原来的效果,第一次采样得到一个桶的编号,第二次采样得到该桶内的颜色。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGakd7xt-1602586237651)(http://ot6t5dysc.bkt.clouddn.com/20170716223747.png)]
举个例子,如图所示,上述概率分布的均值为
1
/
4
1/4
1/4,那么Alias方法通过对概率值得分解和组合,将1图转换为4图,如果采样得到第一个桶的话,然后产生一个
[
0
,
1
/
4
]
[0,1/4]
[0,1/4]的随机数,当落到红色范围内,采样得到的值为
1
1
1,否则为
4
4
4。
伪代码
产生Alias表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emCSFbS5-1602586237654)(http://ot6t5dysc.bkt.clouddn.com/GenerateAlias.png)]
采样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0FosDVVe-1602586237656)(http://ot6t5dysc.bkt.clouddn.com/SampleAlias.png)]
代码
# -*- encoding:utf8 -*-
from random import randrange,random
import numpy as np
from datetime import datetime
class AliasTable():
def __init__(self,probs):
self.probs=probs
probs=np.array(probs)
self.bins=len(probs)
probs=probs*self.bins/np.sum(probs)
self.p_table=np.ones(self.bins,dtype=np.float64)
self.b_table=np.zeros(self.bins,dtype=np.int64)
p=1/self.bins
L,H=[],[]
for i in range(self.bins):
if probs[i]<1:
L.append(i)
else:
H.append(i)
while len(L)>0 and len(H)>0:
l=L.pop()
h=H.pop()
self.p_table[l]=probs[l]
self.b_table[l]=h
probs[h]=probs[h]-(1-probs[l])
if probs[h]<1:
L.append(h)
else:
H.append(h)
while len(L)>0:
l=L.pop()
self.p_table[l]=1
while len(H)>0:
h=H.pop()
self.p_table[H]=1
def sample(self):
b=randrange(self.bins)
if random()<self.p_table[b]:
return b
else:
return self.b_table[b]
if __name__=='__main__':
test=[0,1,2]
at=AliasTable(test)
t=at.sample()
参考文献
- Li, A. Q., Ahmed, A., Ravi, S., & Smola, A. J. (2014). Reducing the sampling complexity of topic models. 891-900.