蓄水池算法
情景:
给一堆数(不知道个数,就是不能随机一个下标取数),从中选择一个数,要求每个数的概率为 。
算法:
对于第 个数,有 的概率替换最终答案。此时每个数,设第m个数,其作为最终答案的概率P为:
选这个数,且后面所有数都不选。得出每个数的概率为
。
扩展
情景:
给一堆数(不知道个数,就是不能随机一个下标取数),从中选择K个数,要求每个数的概率为 。
算法:
先选前K个,对于之后的第
个数,有
的概率取(随机替换原来的数),最后第m个数取的概率P为:
选这个数,且后面所有数要么不选,要么选且没有替换这个数。得出每个数的概率为
。
代码
/*
* Author : Jk_Chen
* Date : 2020-04-08-11.30.23
*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
int randChoose(vector<int>&V){
int idx=0;
int ans=V[0];
for(auto P:V){
// 1/idx
if(rand()%(idx+1)==0)ans=P;
idx+=1;
}
return ans;
}
vector<int> randChoose(vector<int>&V,int k){
assert(V.size()>=k);
vector<int>res(k);
rep(i,0,k-1)res[i]=V[i];
rep(i,k,V.size()-1){
// k/idx
if(rand()%(i+1)<k){
int pos=rand()%k;
res[pos]=V[i];
}
}
return res;
}
void testRandChooseOne(){
srand(time(0));
vector<int>V(100);
rep(i,0,99)V[i]=i;
int cnt[100];
mmm(cnt,0);
rep(i,1,10000){
cnt[randChoose(V)]++;
}
rep(i,0,99)printf("%d\n",cnt[i]);
}
void testRandChooseK(){
srand(time(0));
vector<int>V(100);
rep(i,0,99)V[i]=i;
int cnt[100];
mmm(cnt,0);
rep(i,1,10000){
auto res=randChoose(V,3);
for(auto P:res)cnt[P]++;
}
rep(i,0,99)printf("%d\n",cnt[i]);
}
int main(){
testRandChooseOne();
testRandChooseK();
return 0;
}
/*_________________________________________________________end*/