Alias sampling(根据概率的一种高效采样方法)

最近学习node2vec接触到了这个算法,在此简单记录下,详细可见该博客https://blog.csdn.net/haolexiao/article/details/65157026
我这里直接以上述博客的例子结合代码简单整理下

假如有四个事件,概率分别是1/2,1/3,1/12,1/12(和为 1)。

构建Alias Table

  1. 将四个事件排成一列,分为1,2,3,4。Alias sampling(根据概率的一种高效采样方法)_第1张图片
  2. 每个概率乘以四(事件数)。
    Alias sampling(根据概率的一种高效采样方法)_第2张图片
  3. 然后拼凑使每列值为 1,并保证每列最多只包含两个事件。Alias sampling(根据概率的一种高效采样方法)_第3张图片
    即Alias Table构建完成,得到两个数组,alias:保存着每列中加入的事件,如图即为【2,null,1,1】;prab:保存着每列事件的概率,如图即为【2/3,1,1/3,1/3】。

代码实现思想:乘以事件数之后先将事件分成两部分,一个小于1(smaller),一个大于1(larger),然后分别从这两部分中取数据,用大的补小的使其为 1,如果大的剩余值小于1,加入smaller中,否则还是放回larger中,直到smaller或larger为空。

def alias_setup(probs):
	'''
	Compute utility lists for non-uniform sampling from discrete distributions.
	Refer to https://hips.seas.harvard.edu/blog/2013/03/03/the-alias-method-efficient-sampling-with-many-discrete-outcomes/
	for details
	'''
	K = len(probs)
	q = np.zeros(K)    #保存样本概率
	J = np.zeros(K, dtype=np.int)  #保存补1的事件

	smaller = []
	larger = []
	for kk, prob in enumerate(probs):
	    q[kk] = K*prob
	    if q[kk] < 1.0:
	        smaller.append(kk)
	    else:
	        larger.append(kk)

	while len(smaller) > 0 and len(larger) > 0:
	    small = smaller.pop()
	    large = larger.pop()

	    J[small] = large
	    q[large] = q[large] + q[small] - 1.0  #q[large]-(1-q[small])
	    if q[large] < 1.0:
	        smaller.append(large)
	    else:
	        larger.append(large)

	return J, q    #(alias,prab)

采样

根据Alias TableAlias sampling(根据概率的一种高效采样方法)_第4张图片
随机生成 1—N(事件数) 一个数,确定选中哪列,然后在随机生成一个 0—1 的数,根据概率判断是取样该列的事件还是该列用来补 1 的事件。如选中3列后产生的随机数小于 1/3 则选黄色事件3,否则选蓝色事件1。

def alias_draw(J, q):
	'''
	Draw sample from a non-uniform discrete distribution using alias sampling.
	'''
	K = len(J)

	kk = int(np.floor(np.random.rand()*K))
	if np.random.rand() < q[kk]:
	    return kk
	else:
	    return J[kk]

你可能感兴趣的:(算法)