从N个元素中随机抽取k个数,其中N无法确定
在数据流的情况下,每个数据有且仅有被读取一次,而且数据流很大,保存的量相比抽取的量会小得多
应用场景
输入:一组数据,其大小未知
输出:这组数据的k个均匀抽样(在非常多的数据里面每个数据被抽到的概率都是一致的)
要求:
仅仅只能扫描数据一次
时间复杂度为0(k)
扫描到数据的前n个数字时(n>k),保存当前已扫描数据的k个抽样
水库抽样算法
1.申请一个长度为k的数组A保存抽样
2.保存首先接受到的k个元素
3,当接受到第i个新元素t时,以k/i的概率随机替换A中的元素(及生成[1,i]间随机数j,若j<=k,则以t替换A[j])
性质一:该采样是均匀的
k/i*(1--1/i+1)*(1--1/i+2)*............*(1--1/n)=k/n
当接受到第i个数的时候,那么第i个数被替换的概率为k/i。
当接受到第i+1个数的时候,第i+1个数字被替换 的概率为k/i+1,数组中每个数字被替换的概率是一样的都是1*k,两者相乘,所以第i个数被替换的概率为1/i+1 ,即第i个数不被替换的概率为(1--1/i+1)
所以可以得出到接受n个数时,第i个数更改的概率为k/i 以此类推i+1个数更改
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int random(int min ,int max)
{
return (min+(rand()%(max-min+1)));
}
int main()
{
srand(unsigned(time(0)));
int k;
int i;
cout << "Input k:" ;
cin >> k;
double *ans = new double[k+1];
double input;
cout << "Input k numbers:" << endl;
for(i = 1;i <= k; ++i)
{
cin >> ans[i];
}
cout << "Input stream numbers:(q to quit)" << endl;
while(true)
{
int j = random(1,i);
if(!(cin >> input)) break;
if(j <= k)
ans[j] = input;
//output
cout << "Ans :" ;
for(int p = 1;p < k; ++p)
cout << ans[p] << ",";
cout << ans[k] << endl;
i++;
}
delete [] ans;
return 0;
}