取样问题

7 篇文章 0 订阅
7 篇文章 0 订阅

编程珠玑第12章的一个问题:

程序的输入包含两个整数m和n(m<n).输出0~n-1范围内m个随机整数的有序列表,且不允许重复。从概率角度,得到的是没有重复的有序选择,其中每个选择出现的概率相等。

书中陈述了3种方法:

1 Knuth的方法,按概率输出整数,算法考虑从0,1,2,...n-1的数。并通过一个适当的随机测试对每个整数进行选择。通过有序访问整数,输出结果是有序的。

假设5个当中选择2个,

选择0的概率是2/5,语句就是if (rand()%5 < 2),

而选择1的概率则分两种情况,已经选择0,则会以1/4的概率选择1;没有选择0,则以2/4的概率选择1;总结就是从剩下的4个元素中选择剩余应该选择的个数。

总结,其实就是从r个剩余的整数中选择s个,则以s/r的概率选择下一个数。

代码:

//解法1:依次考虑0,1,...,n-1的整数,通过随机测试队每个整数进行选择,
//选择的标准是从n个整数中选择m个整数,则每个被选中的数字的概率是m/n
//从r个剩余的整数中选择s个整数,概率是r/s
void getknuth(int m, int n)
{
    int i;
    srand(time(NULL));
    for (i = 0; i < n; i++)
    {
        //从n-i个元素中选择m元素的概率
        if ((rand()%(n-i)) < m)
        {
            cout << i << " ";
            m--;
        }
    }
    cout << endl;
}
以上算法复杂度是O(n)

2 借助C++的set集合,set保证元素是有序,且是不重复的,所以重复插入小于n的任意一个数,直到集合的大小是m就可以了。如下:

//解法2:用set实现
void gensets(int m, int n)
{
    set<int> s;
    while (s.size() <= m)
        s.insert(rand()%n);
    set<int>::iterator it;
    for (it = s.begin(); it != s.end(); it++)
        cout << *it << " ";
    cout << endl;
}

时间复杂度,set的插入复杂度是O(logm),至少插入m次,所以插入部分时间O(mlogm),遍历O(m),总共O(mlogm)

3 将包含整数0~n-1的数组按顺序打乱,然后把前m个元素排序输出。因为这个选择方法,每次都是从未选择的元素中选择元素,所以显示保证了元素的不重复。

代码:

//解法3:把包含0到n-1的有序数组顺序打乱,然后将前m个元素排序输出
void genshuf(int m, int n)
{
    int i, j;
    int *arr = new int[n];
    for (i = 0; i < n; i++)
        arr[i] = i;
    for (i = 0; i < m; i++)
    {
       //从n-i个元素中随机选择一个元素, 每次都是从未选择的元素中选择元素
       //所以保证了元素的不重复性
       j = rand() % (n-i);
       swap(arr[i], arr[j]);
    }
    sort(arr, arr+m);
    for (i = 0; i < m; i++)
        cout << arr[i] << " ";
    cout << endl;
    delete [] arr;
}

时间复杂度O(n)加上排序的时间,O(mlogm),总共O(n+mlogm),有一个空间复杂度O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值