等可能地n选m问题

如何等可能地n选m

给你一个长度为n的数组a[1..n],要你等可能地选m个元素出来。

先说一个最简单的做法。
就是从这n个数里随机选一个,譬如说选到的数是a[i],然后我们把a[i]和a[1]交换
然后问题转变成从a[2..n]这n-1个数的数组里选m-1个数
以此类推,最后我们就成功地选出了m个数,他们被放在了a[1..m]

这个方法是如此显然,但是还是让我们来看看它的正确性。

先确定一下样本空间,这种方法产生的每个样本点分别是
假设我们最后得到的样本点为b[1..m],这个样本点出现的概率是
    P(b[1..m]出现) = P(b[1]在第1次被选中) * P(b[2]在第2次被选中 | b[1]出现) * P(b[3]在第3次被选中 | b[1..2]出现) * ... * P(b[m]在第m次被选中 | b[1..m-1]出现)
由独立性可以知道, 
    P(b[j]在第j次被选中 | b[1..j-1]出现) = P(b[j]在第j次被选中) = 1 / (n - (j-1))
所以
    P(b[1..m]出现) = (1 / n) * (1 / n - 1) * ... * (1 / n - (n - m + 1))

对于样本空间的每个样本点,上面的分析都是一样的,所以,这个方法抽出来的样本点,是等可能的。证明完毕。

这个方法有一个缺点,那就是,它会破坏原来的数组的元素的顺序。


现在来看一下另一个有趣的方法,这个方法是基于递归的思路的(或者说是基于序贯树模型的)
我们现在要从n个数里选出m个数,考察a[1],基于类似于01背包的思想,样本点可以分为两类,一类是包含a[1]的,一类是不包含a[1]的。
这个启发我们,能不能以一定的概率来决定a[1]是否包含进来呢?
设想一下,我们以概率p把[1]包含进来,概率(1-p)把a[1]淘汰。

先看a[1]被包含进来的情况。
假设我们现在已经决定要把a[1]包含进来了,那我们接下来的工作就是,在剩下的a[2..n]里面,等概率地选取m-1个数。看到了吗,同构的子问题出现了!
所以,我们可以先假定,我们已经有办法可以解决“从j个数里的概率地选择i个数,j小于n”这类问题。
对于这种情况,产生的每个样本点的概率是 p * ( 1 / C(n-1, m - 1) )


再来看a[1]不包含在内的情况。
假设我们现在决定不要a[1]了,那么,我们接下来的工作就是,在剩下的a[2..n]里面,等概率地选取m个数。ok,子问题再次出现。
没关系,因为我们已经假设了我们有能力解决规模更小的子问题了。
对于这种情况,产生的每个样本点的概率是 (1-p) * ( 1/C(n,m) )

现在的关键就是,那个p到底等于多少才合理呢?
所谓合理的p,也就是说,上面两种情况,产生的样本点的概率是一样的,也就是说,满足这个方程:
 p * ( 1 / C(n-1, m - 1) ) = (1-p) * ( 1/C(n,m) )

解之得:
p = m / (n + m)

问题就解决了。

转载于:https://my.oschina.net/mustang/blog/79626

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值