不包含重复数字的抽样算法

/kingkai

编程珠玑上关于抽样问题的章节,提出了很多随机从N个数中抽取M个数(不重复)的方法。这里一一进行分析,并给出部分推导。

抽样广泛应用于工程实践中,在样本空间非常大时,性能的因素会显得非常明显。比如,总每日的检索Query中抽样不重复的100个。对于这个命题,如果不精心设计。很可能演变成很多粗糙的实现。

Loop N

       该抽样的原理是顺序遍历N中的每一个数字。以5个数中抽取2个数为例。

例如对于1,其被选中的概率是2/5

对于2,其被选中的概率是条件概率之和:即1被选中条件下(2/5)同时2被选中(1/4)的概率,与1未被选中条件下(1-2/5)同时2被选中(2/4)的概率,(2/5)*(1/4) + (1-2/5)*(2/4) = 2/5

以此类推。最终每个数字被选中的概率均为2/5。即N/M

算法即是模拟这个抽样过程。对于数字i,假设之前已经抽样出m个数字,则可供抽取的样本空间为(N-i),在其中要选出(M-m)个数字,i被抽样选中的该率即为(M-m)/(N-i)。对于第一次抽样,i=0m=0

      但是这个算法的效率在N远大于M时非常低。Bigrand算法被执行了N次。

 

Set

       这个算法利用Set的特性成功地将计算缩小至了M的规模上。但有两个缺陷:

1.       Set占用的内存在M较大时较为显著。

2.       MN接近时,while循环的执行效率明显降低,因为出现与Set集合中非重复数字的概率降低了。

 

Set改进

这个算法的关键在于对于每次循环中备选队列中的最后一个数字(j),有两种概率被抽中。一是自身被选中(1/j),二是由于再次选中Set中的数字(共计j-1),而被抽中。

M=2 N=5为例。(0,1,2,3,4)

第一次Shuffle的备选集合是(0,1,2,3)。每个数字被选中的概率均为1/4

第二次Shuffle的备选集合是(0,1,2,3,4)

对于(0,1,2,3),如果第一次未被选中,还有一次被选中的机会,为条件概率(1-1/4)*(1/5) 经过两次Shuffle,被选中的概率为1/4+(1-1/4)*(1/5)= 2/5

对于(4),其被选中的概率为Set中已有的集合(1/5)和其自身(1/5) = 2/5

经过这个改进算法,可以解决上述M接近N时的效率问题。

Shuffle

         

         举例说明,对于N个数,以第1个数n1为例。

         在第一次Shuffle过程中,n1落在第一个int[0]位置的概率为1/N。而一旦落入int[0],就不再受后续Shuffle的影响了。

         n1落在第二个位置int[1]上的概率。它由条件概率之和构成,即n1在第一次Shuffle中落入int[1]且第二次Shuffle保持不变的概率与n1在第一次Shuffle中落入int[2..n-1]且第二次Shuffle被换回至int[1]的概率。计算式为(1/N)*(1/N-1) + (N-2/N)*(1/N-1) = 1/N

         以此类推,n1落在int[0…n-1]任意一个位置的概率均为1/N,是一个标准的随机概率。这样经过Shuffle后,选取前M个结果,即满足需求。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值