随机化

蓄水池抽样算法(Reservoir Sampling)

给定一个数据流,数据流长度N很大,且N直到处理完所有数据之前都不可知,请问如何在只遍历一遍数据(O(N))的情况下,能够随机选取出m个不重复的数据。

题意中有三个背景:

  • 1.数据流长度N很大且不可知,所以不能一次性存入内存。

  • 2.时间复杂度为O(N)。

  • 3.随机选取m个数,每个数被选中的概率为m/N。

第1点限制了不能直接取N内的m个随机数,然后按索引取出数据。第2点限制了不能先遍历一遍,然后分块存储数据,再随机选取。第3点是数据选取绝对随机的保证。讲真,在不知道蓄水池算法前,我想破脑袋也不知道该题做何解

核心代码

int[] reservoir = new int[m];

// init
for (int i = 0; i < reservoir.length; i++)
{
    reservoir[i] = dataStream[i];
}

for (int i = m; i < dataStream.length; i++)
{
    // 随机获得一个[0, i]内的随机整数
    int d = rand.nextInt(i + 1);
    // 如果随机整数落在[0, m-1]范围内,则替换蓄水池中的元素
    if (d < m)
    {
        reservoir[d] = dataStream[i];
    }
}

方法1:蓄水池抽样

方法1:蓄水池抽样

方法2:HashMap

方法1:拒绝采样

方法2:极坐标

ρ= random ∗r

极坐标的的角度也是随机的 θ = 2∗π∗random

x = x_center + ρ * cos(θ)

y = y_center + ρ * sin(θ)

方法1:二分+前缀和

方法2:蓄水池抽样

Reference

Last updated