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

问题:

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为文件中元素的总数。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多则惑少则明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值