需求:从正整数1-100中随机抽取20个数。
以下算法均来自与《编程珠玑》
1.精妙的算法,运用概率的知识。
#include<stdio.h> #include<stdlib.h> /*rand()*/ #include<time.h> /*time()*/ int main(){ int m = 5, n= 20; srand(time(NULL)); for(int i = 0; i< n; ++i) if(rand()%(n-i)<m){ printf("%d ",i); m--; } }
或许你不理解rand()%(n-i)<m这一句,我开始也不理解,请教了高人之后才理解的,所以就写了这篇博客,以免忘了。
从n中选m个数,每个数被选中概率为m/n。
第一个数被选中的概率是m/n。
第二数被选中概率的计算如下:如果第一数被选中了,(m-1)/(n-1); 如果第一个数没有被选中,m/(n-1)。所以总体概率为m/n * (m-1)/(n-1) +(n-m)/n * m/(n-1) = m/n
以此类推。。。每个数被选中的概率都是m/n。
假设一个随机数生成器R,生成的数属于[0,Z-1],且生成每个数的概率相等。
对任意一个小于Z的数Q,那么R()<Q的概率即Q/Z。所以rand()%(n-i) < m 即表示m/n-i的概率,也就是当前数字被选中的概率。
严格来说,rand()%(n-i)并不是一个等概率生成器,因为rand()显然不是对每个i都是n-i的整数倍。
2.往一个初始为空的集合中插入随机整数,直到填入足够的整数。需要额外的m*size(int)的空间,最大的问题是需要查询重复元素。
void gensets(int m, int n){ set<int> S; srand(time(NULL)); while (S.size() < m) S.insert(rand()%n); set<int>::iterator i; for(i = S.begin();i != S.end();++i) cout << *i <<" " ; }
3.弄乱n个元素,时间复杂度是O(m),但需要做交换操作。
int randint(int start,int end){ srand (time(NULL)); int m; m = start + 1 +rand()%(end - start); return m; } void genshuf(int m, int n){ int i,j; int *x = new int[n]; for(i=0;i<n;i++) x[i]=i; for(i=0;i<m;i++){ //从x[i]后面的数中随机娶一个与x[i] j= randint(i,n-1); int t = x[j];x[i]=x[j];x[j]=t; } //sort(x,x+m); for(i=0;i<m;i++) cout<<x[i]<<" "; }