要从0~n-1的整数中取出来m(m<n)个整数。
有几种算法,第一种算法是严格按照概率得到,满足每个数取得的概率相同。原理和抓阄时候一样,先取和后取得并没有概率上的差别,第一个数字0取得的概率为m/n,当rand()%n<m的时候取得0。以后要调整m和n的值,才可以使得取得的概率相同,这种方法容易理解,并且严格满足m/n的条件。
第二种算法和以前一篇的洗盘算法比较相似,但是并不是严格的每个数字取到的概率为m/n,算法是这样的,先生成一个n维的整数数组,a值为0~n-1,然后生成m个n内的随机整数rand,然后交换a[i]和a[rand](i=0...m-1)。至于是否是满足m/n证明好像有点难。
第三种算法更加直接,用一个set容器存放产生的随机整数,一直产生n内的随机整数填入容器,一直到set的size为m的时候即可。
对于三种算法,如果要求产生的整数为有序的,那么第二第三中算法还有排序,第一种算法是自然有序的,但是当n较大的时候,第一种算法运行的时间可能比较长。对于第一种算法,当n较大,m较小的时候,可以判断下m是否为0,如果为零可以跳出循环。还有别的情况,如果n和m都很大,而且m很接近n的时候,那么可以产生n-m个n内的随机整数,然后输出没有产生的随机整数。还有当n为2的32次方,m为1000万的时候,这种情况,可以直接生成1100万个随机的整数,然后去除重复的整数得到1000w个随机整数。注意RAND_MAX可能小于2的32次方,这时候就要对rand()函数进行改写,假设RAND_MAX为2的31次方,那么可以写个bigRand(){return rand()+rand()};
#include <iostream> #include <set> using namespace std; //rand()函数生成一个0到RAND_MAX(stdlib.h中定义的值为2147483647(有符号整数的最大值))之间的整数 void rand_select1(int n, int m);//在0~n内选择出m个整数 void rand_select2(int n, int m); void rand_select3(int n, int m); void exchange(int *a, int i, int j); int main() { //rand_select1(30, 2); //rand_select2(30, 2); rand_select3(30, 2); return 0; } void rand_select1(int n, int m) { srand(time(0)); for(int i=0; i<n; i++) { if(rand()%(n-i) < m) { printf("%d\n", i); m--; } } } void rand_select2(int n, int m) { srand(time(0)); int *a = new int[n]; for(int i=0; i<n; i++) a[i] = i; for(int i=0; i<m; i++) exchange(a, i, rand()%n); for(int i=0; i<m; i++) printf("%d\n", a[i]); } void rand_select3(int n, int m) { srand(time(0)); set<int> S; while(S.size()<m) S.insert(rand()%n); set<int>::iterator i; for(i=S.begin(); i!=S.end(); ++i) printf("%d\n", *i); } void exchange(int *a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }