编程珠玑 12 取样问题

问题:从0到n-1的n个数中,随机选取m个数字,并且顺序打印出来,可以使用标准库的rand()函数

方法:使用Knuth方法,使用rand() % n < m表示概率m/n,在抽样中,加入m = 2, n = 5,那么第一次抽取0,我们有2/5的概率选择0,如果选择了0,那么在剩余的4个数中选取1的概率应该改为1/4,如果没有选取0,那么以2/4的概率选取1,这样5个数中选取1的概率还是2/5 * 1/4 + 3/5  * 2/4 = 2/5,这样选取1的概率还是2/5。

代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime>

using namespace std;

void genknuth(int m, int n)
{
        clock_t start, end;

        start = clock();
        srand(time(NULL));
        for(int i = 0; i < n; i++)
        {
                if(rand() % (n - i) < m)
                {
                        cout<< i << endl;
                        if(!(--m))
                                break;
                }
        }

        end = clock();
        std::cout << "Process took " << (double(end - start) / CLOCKS_PER_SEC) << "seconds" << endl;
        return ;
}

int main()
{
        genknuth(20, 1<<30);
        return 0;
}

在n较小时,我们的效率比较高,但是当n非常大,接近2^32时,花费时间越来越多,但是代码非常简洁,高效!

-----------------------------------------------

编程珠玑12章后的第10题:

如何在事先不知道文本文件行数的情况下读取该文件,从中随机选择并输出一行?

解法:首先以概率1选择第一行,如果有更多行,比如第i行,我们以1/i的概率选择第i行,最后输出我们选择的行

伪代码:

-----------------------------------------------

-----------------------------------------------

p = 0

i = 0

while more input lines

with the conditional p = 1.0/++i select this line

choice = the i-th line

end while

print choice

-----------------------------------------------

-----------------------------------------------

最后输出结果,其中p = 1.0 / i++ 可以通过rand() % ++i < 1得到,如果满足条件,我们选择第i行。


【转】

扩展:原问题可简化为:如何从n个有序对象中等概率地任意抽取1个,简记为sample(n,1),其中n未知;

           若将该问题改为:如何从n个有序对象中等概率地任意抽取m个,简记为sample(n,m),其中n未知;

分析:若n已知,sample(n,m)是普通的抽样问题;当n未知时,可否根据上述算法进行相应的转化求解?

解决方案:将sample(n,m)问题转化为m个sample(n*,1)问题,更具体一点是,转化为sample(n,1);sample(n-1,1);sample(n-2,1)....;sample(n-m+1,1)问题。仍然以一篇6行文档为例,任取其中2行,做法如下:第一遍,以如下概率选中一行:1(1)   2(1/2)  3(1/3)  4(1/4)  5(1/5)  6(1/6)假设选中第2行,接着概率修改如下:3(1)  4(1/2)  5(1/3)  6(1/4)  1(1/5)

【说明】:当选中第2行,从第3行开始修改概率,并将第2行排除在外,继续扫描,这样能保证在剩下的5个数中仍然以等概率抽取其中的一个。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值