1.问题描述:
随机抽样问题表示如下:
要求从N个元素中随机的抽取k个元素,其中N无法确定。
这种应用的场景一般是数据流的情况下,由于数据只能被读取一次,而且数据量很大,并不能全部保存,因此数据量N是无法在抽样开始时确定的;但又要保持随机性,于是有了这个问题。所以搜索网站有时候会问这样的问题。
2. k =1的情况:
解法:我们总是选择第一个对象,以1/2的概率选择第二个,以1/3的概率选择第三个,以此类推,以1/m的概率选择第m个对象。当该过程结束时,每一个对象具有相同的选中概率,即1/n,证明如下。
证明:第m个对象最终被选中的概率P=选择m的概率*其后面所有对象不被选择的概率,即:
3. k != 1 && n > k的情况:
对应蓄水池抽样问题,可以类似的思路解决。先把读到的前k个对象放入“水库”,对于第k+1个对象开始,以k/(k+1)的概率选择该对象,以k/(k+2)的概率选择第k+2个对象,以此类推,以k/m的概率选择第m个对象(m>k)。如果m被选中,则随机替换水库中的一个对象。最终每个对象被选中的概率均为k/n,证明如下。
证明:第m个对象被选中的概率=选择m的概率*(其后元素不被选择的概率+其后元素被选择的概率*不替换第m个对象的概率),即
4. 代码:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;
typedef vector<int> IntVec;
typedef typename IntVec::iterator Iter;
typedef typename IntVec::const_iterator Const_Iter;
// generate a random number between i and k,
// both i and k are inclusive.
int randint(int i, int k)
{
if (i > k)
{
int t = i; i = k; k = t; // swap
}
int ret = i + rand() % (k - i + 1);
return ret;
}
// take 1 sample to result from input of unknown n items.
bool reservoir_sampling(const IntVec &input, int &result)
{
srand(time(NULL));
if (input.size() <= 0)
return false;
Const_Iter iter = input.begin();
result = *iter++;
for (int i = 1; iter != input.end(); ++iter, ++i)
{
int j = randint(0, i);
if (j < 1)
result = *iter;
}
return true;
}
int main()
{
const int n = 10;
IntVec input(n);
int result = 0;
for (int i = 0; i != n; ++i)
input[i] = i;
if (reservoir_sampling(input, result))
cout << result << endl;
return 0;
}