从[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。