目录
1.基本概念
(1)介绍
水塘抽样(Reservoir Sampling)是一种随机算法,用于从集合S中随机并等概率抽取k个元素(|S|可以是未知的,这尤其适用于不能把所有元素一次性加载到内存的情况)。
(2)算法步骤
首先从S中抽取前k个样本放入水塘reservior
然后顺序遍历S,对每一个元素S[i](i>=k),都根据其下标i生成一个随机数randnum∈[0,i]。若randnum<k,就用S[i]替换水塘中已有的元素reservior[randnum]
最后水塘里的所有元素即为最终抽样结果,且每个元素对应的概率均为k/|S|。
(3)算法证明
对每个S[i]:
P(S[i]位于最终的水塘中)
= P(S[i]替换掉水塘中某个元素) × P(S[i+1]没有替换掉水塘中的S[i]) × P(S[i+2]没有替换掉水塘中的S[i]) ×...× P(S[|S|-1]没有替换掉水塘中的S[i])
= P(rand()%(i+1)<k) × P(rand()%(i+2)!=i) × P(rand()%(i+3)!=i) ×...× P(rand()%(|S|)!=i)
= k/(i+1)×(1-1/(i+2))×(1-1/(i+3))×...×(1-1/|S|)
= k/(i+1)×((i+1)/(i+2))×((i+2)/(i+3))×...×((|S|-1)/|S|)
= k/|S|
2.算法实现
(1)数据结构
#include <iostream>
#include <cstdlib> // rand()
class ReserviorSampling{
public:
static std::vector<int> sampling(std::vector<int>& S, int k);
};
(2)抽样
static std::vector<int> ReserviorSampling::sampling(std::vector<int>& S, int k){
std::vector<int> reservior(k); // 初始化水塘
for(int i=0; i<k; i++){
reservior[i] = S[i];
}
int i=k;
for(auto &e:S){
int randnum = rand()%(i+1);// 从i>=k开始,对每个i都随机获得一个[0~i]的整数
if(randnum < k){
reservior[randnum] = e;
}
i++;
}
return reservior;
}