随机抽样与概率(Alias抽样,蓄水池抽样,洗牌算法)

1、Alias采样

leetcode 528:按权重随机选择

学习来源:

1、时间复杂度O(1)的离散采样算法——Alias method

2、Alias采样算法

程序实现

import numpy as np

def alias_setup(probs):
    K = len(probs)
    q = np.zeros(K)
    J = np.zeros(K, dtype=np.int)

    smaller = []
    larger = []
    for kk, prob in enumerate(probs):
        q[kk] = K * prob         # 保存面积块
        if q[kk] < 1.0:
            smaller.append(kk)   # 面积小于1的元素下标
        else:
            larger.append(kk)    # 面积大于1的元素下标

    while len(smaller) > 0 and len(larger) > 0:
        small = smaller.pop()
        large = larger.pop()
        # J 记录的是填充这一列的下标
        J[small] = large  
        # q 记录的是原来事件的占比
        q[large] = q[large] - (1 - q[small])
        if q[large] < 1.0:
            smaller.append(large)
        else:
            larger.append(large)
    
    return J, q

def alias_draw(J, q):
    K = len(J)
    kk = int(np.floor(np.random.rand() * K))  # 随机选一列
    if np.random.rand() < q[kk]:
        return kk
    else:
        return J[kk]
    
if __name__  == "__main__":
    J, q = alias_setup([1/2, 1/3, 1/12, 1/12])
    print(J, q)   # [0 0 0 1] [1.         0.66666667 0.33333333 0.33333333]
    ret = alias_draw(J, q)   
Alias抽样算法

构造表的时间复杂度O(n),采样的时间复杂度O(1)

 

2、蓄水池抽样

问题描述:给定一个数据量很大的整数数组(不能一次全部读取到内存中)和一个目标数(保证出现在数组中),目标数在数组中可能出现多次,要求对与目标数相等的数的下标随机抽样,使得每个下标等概率出现。

方法:设定一个种子为0,假设有m个下标符合,每次遇到第i个,则从[0,i)中随机抽取一个数,如果这个数等于种子0,就记录当前的下标;

抽取的下标为第k个概率 = 1/k * (1 - 1/(k+1)) * ... *(1 - 1/m) = 1/m 

leetcode 398:随机数索引

leetcode 382:链表随机节点

 

3、洗牌算法

问题描述:随机打乱一个数组,使得每种排列都等概率的出现

方法:第i个数与[i, n-1]中的随机一个数进行交换

leetcode 384:打乱数组

 

posted @ 2021-08-30 22:46  菠萝机  阅读(261)  评论(0编辑  收藏  举报