现在的位置: 首页 > 综合 > 正文

编程珠玑 12 取样问题

2014年02月12日 ⁄ 综合 ⁄ 共 1954字 ⁄ 字号 评论关闭

问题:从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。

代码:

  1. #include <iostream>  
  2. #include <cstdlib>  
  3. #include <cstdio>  
  4. #include <ctime>  
  5.   
  6. using namespace std;  
  7.   
  8. void genknuth(int m, int n)  
  9. {  
  10.         clock_t start, end;  
  11.   
  12.         start = clock();  
  13.         srand(time(NULL));  
  14.         for(int i = 0; i < n; i++)  
  15.         {  
  16.                 if(rand() % (n - i) < m)  
  17.                 {  
  18.                         cout<< i << endl;  
  19.                         if(!(--m))  
  20.                                 break;  
  21.                 }  
  22.         }  
  23.   
  24.         end = clock();  
  25.         std::cout << "Process took " << (double(end - start) / CLOCKS_PER_SEC) << "seconds" << endl;  
  26.         return ;  
  27. }  
  28.   
  29. int main()  
  30. {  
  31.         genknuth(20, 1<<30);  
  32.         return 0;  
  33. }  


在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个数中仍然以等概率抽取其中的一个。

抱歉!评论已关闭.