您的位置:首页 > 编程语言

《编程珠玑》第12章 抽样问题笔记 生成m个0~n间的随机数

2014-02-15 15:00 573 查看

问题:

n个整数中随机选择m个整数(m<n),随机数的范围为[0,n-1].

要求:每个整数最多出现一次;每个子集出现的概率相同。

假设:函数bigrand(),返回一个大的随机整数(比m,n大很多)

      函数randint(i,j)返回[i,j]间均匀的随机整数

方法一:

Knuth《计算机程序设计艺术》中3.4.2节提出的算法



 C++实现

  


方法二:

解决方法:

在一个初始为空的集合中插入随机整数,直到插入足够的整数.

伪代码:

   

代码



缺点:空间消耗增大,m=1700000,128MB内存不够用

优点:时间复杂度减小

时间复杂度

插入:O(logm)

集合内部迭代:O(m)

总时间:O(m*logm)[和n相比,m较小]

方法三

思路

1,弄乱一个n个元素数组,这个数组包含数值范围为[0,n-1]【3.4.2节Knuth的算法P作用:弄乱数组x[0...n-1]】;

2,排序前m个元素并输出

   


空间复杂度:O(n)

时间复杂度:O(n+m*logm)

《STL源码剖析》random_shuffle的实现:
      



即相当于:swap(i,rand()%(i+1)),和Knuth的算法P不同。P算法是swap(i,randint(i,n-1))

习题:

 







习题答案


















代码和概率分析:摘录自

http://blog.csdn.net/morewindows/article/details/7659532



概率分析:

  

分析:对代码进行下讲解,以三行数据为例,首先对文本的第一行,rand()% 1,结果必然为0。所以第一行已被选中了,然后对第二行,rand()%2,结果要么为0,要么为1。故第二行有 50的可能性被选中,然后对第三行,rand()%3,显然被选中的概率为1/3。故有:
选中第一行的概率为1 * 1/2(第2行没被选中概率) * 2/3(第三行没被选中概率) = 1/3。

选中第二行的概率为1/2(第1行没被选中概率)  * 2/3 = 1/3。

选中第三行的概率为1/3。

故每一行被选中是等概率的。

补充-完整的证明过程:原链接:http://blog.csdn.net/v_july_v/article/details/6712171

   顺序遍历,当前遍历的元素为第L个元素,变量e表示之前选取了的某一个元素,此时生成一个随机数r,如果r%L == 0(当然0也可以是0~L-1中的任何一个,概率都是一样的), 我们将e的值替换为当前值,否则扫描下一个元素直到文件结束。

证明

     在遍历到第1个元素的时候,即L为1,那么r%L必然为0,所以e为第一个元素,p=100%,

    遍历到第2个元素时,L为2,r%L==0的概率为1/2, 这个时候,第1个元素不被替换的概率为1X(1-1/2)=1/2,

     第1个元素被替换,也就是第2个元素被选中的概率为1/2 = 1/2,你可以看到,只有2时,这两个元素是等概率的机会被选中的。

    继续,遍历到第3个元素的时候,r%L==0的概率为1/3,前面被选中的元素不被替换的概率为1/2 X (1-1/3)=1/3,前面被选中的元素被替换的概率,即第3个元素被选中的概率为1/3

    归纳法证明,这样走到第L个元素时,这L个元素中任一被选中的概率都是1/L,那么走到L+1时,第L+1个元素选中的概率为1/(L+1), 之前选中的元素不被替换,即继续被选中的概率为1/L X ( 1-1/(L+1) ) = 1/(L+1)。证毕。

    也就是说,走到文件最后,每一个元素最终被选出的概率为1/n, n为文件中元素的总数。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: