knuth(int n, int m) { for (int i=0; i<n; i++) { if ( rand_n()<m ) // rand_n()为生成[0,n)这个区间内的随机数 // rand_n()<m的概率就是m/n { cout<<i<<endl; } } }
knuth(int n, int m) { srand((unsigned int)time(0)); for (int i=0; i<n; i++) { //假设还要从后面n-i个数中选m个元素 //那么每个元素被选中的概率为rand()%(n-i)<m //因此每个元素的选中概率是等价的 if ( rand()%(n-i)<m ) { cout<<i<<endl; m--; } } }
由于数据量大小未知,因此无法使用上面的算法。我们能做的仅仅是保证所有已经输入的数据(假设已经有N个了)已k/N的等概率被选出,从而满足需求。算法如下:
S为元素数据流 初始化: 初始化一个大小为K的元素空间buf for i =1 to k buf[i] = S[i] for i= k+1 to N { //第i个元素以 k/i 的概率留下 M=random(1, i);//生成一个1到i的随机数 if( M <= k) buf[M]= S[i] }
归纳证明:k < i <=N
第k+1个元素被选中的概率为k/(k+1)。
从K个位置中随机选出一个位置放入新数据。有1/k的概率被选出。
需要该操作的概率是k/(k+1),因此有1/k * k/(k+1) = 1/(k+1)的概率被选出。
故k/(k+1)的概率保留在缓存中。即就是前面i个元素和第i+1个元素等概率的存在于缓存中,出现的概率为k/i。
元素j留下的概率为k/j = k/i+1;
从K个缓存中为元素j找一个位置放入K个元素有1/k的概率被替换
因此 本次操作 K个元素被替换的概率为(k/i+1) *(1/k) = 1/(i+1),
本次操作K个元素能保留下来的概率为 1 - 1/(i+1) = i/(i+1
)此前,我们已经从i个元素中等概率k/i的选了k个元素,
经过这次操作这K个元素有i/(i+1)保留
因此有k/i * i/(i+1) = k/(i+1)
综上所述,证明成立。
for i =[0,N) swap(x[i],x[rand(i,n-1)];有人证明,只要扰乱前m个就可以。
void sample_shuf(const int N,const int m) { int i, j; int *x = new int[N]; for(i = 0 ; i <N ; i++) x[i]=i+1;
for(i = 0 ; i < m ; i ++) { j = rand(i,N-1); swap(x[i],x[j]); } sort(x,x+m); Print(x,m); delete []x;
x= NULL; }
for k=N:1 j = rand(1,k) swap(j,k) end
for k = 1:N j = rand(k,N) swap(j,k) end
参考资料