从[0,n)中选取不重复的m个数

一般算法

void mRandn(int n, int m) {
    for (int i = 0; i < n && m > 0; ++i) {
        if (rand() % (n - i) < m) {
            cout << i << ends;
            --m;
        }
    }
}

选取数目满后,可以提前终止。

蓄水池抽样算法(Reservoir Sampling)

有时候不知道n,如何实现?流数据、大数据集等场景。

auto sample(vector<int> N, int m) {
    vector<int> v;
    int i = 0;
    for (auto x : N) {
        ++i;
        if (i <= m) {
            v.push_back(x);
        } else {
            int d = rand() % i;
            if (d < m) {
                v[d] = x;
            }
        }
    }
    return v;
}

分布式蓄水池抽样(Distributed/Parallel Reservoir Sampling)

如果遇到超大的数据量,即使是O(N)的时间复杂度,蓄水池抽样程序完成抽样任务也将耗时很久。因此分布式的蓄水池抽样算法应运而生。

运作原理如下:

假设有K台机器,将大数据集分成K个数据流,每台机器使用单机版蓄水池抽样处理一个数据流,抽样m个数据,并最后记录处理的数据量为N1, N2, …, Nk(假设m<Nk)。N1+N2+…+Nk=N。

取[1, N]一个随机数d,

若d<N1,则在第一台机器的蓄水池中等概率不放回地(1/m)选取一个数据;

若N1<=d<(N1+N2),则在第二台机器的蓄水池中等概率不放回地选取一个数据;

依次类推,重复m次,则最终从N大数据集中选出m个数据。

m/N的概率验证如下:

第k台机器中的蓄水池数据被选取的概率为m/Nk。
从第k台机器的蓄水池中选取一个数据放进最终蓄水池的概率为Nk/N。
第k台机器蓄水池的一个数据被选中的概率为1/m。(不放回选取是等概率的)
重复m次选取,则每个数据被选中的概率为m*(m/Nk*Nk/N*1/m)=m/N。

posted @ 2022-05-19 11:45  天下太平  阅读(125)  评论(0编辑  收藏  举报