编程珠玑(三)取样问题

要从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;
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值