蓄水池算法
情景:
给一堆数(不知道个数,就是不能随机一个下标取数),从中选择一个数,要求每个数的概率为 1 / n 1/n 1/n。
算法:
对于第 i i i个数,有 1 / i 1/i 1/i的概率替换最终答案。此时每个数,设第m个数,其作为最终答案的概率P为:
选这个数,且后面所有数都不选。得出每个数的概率为
1
/
n
1/n
1/n。
扩展
情景:
给一堆数(不知道个数,就是不能随机一个下标取数),从中选择K个数,要求每个数的概率为 K / n K/n K/n。
算法:
先选前K个,对于之后的第
i
i
i个数,有
K
/
i
K/i
K/i的概率取(随机替换原来的数),最后第m个数取的概率P为:
分析一下,对于每个数的选与不选进行考虑,首先选的概率是
K
/
i
K/i
K/i,这个符合
i
i
i选
K
K
K的概率;选完后,考虑其它数,由于我选之前每个数留下的概率都是
K
/
(
i
−
1
)
K/(i-1)
K/(i−1),那是就是
K
/
(
i
−
1
)
⋅
(
i
−
K
i
+
K
i
⋅
K
−
1
K
)
K/(i-1)\cdot(\dfrac{i-K}{i}+\dfrac{K}{i}\cdot\dfrac{K-1}{K})
K/(i−1)⋅(ii−K+iK⋅KK−1),也是
K
/
i
K/i
K/i。
这个式子是不选第i个数,或者选了但是替代其它k-1个数
可以推导出:每个数的概率为 K / n K/n K/n。
代码
/*
* 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*/