问题如上。

这是我被面试的一个题目。

我的第一反应给出的解决的方法是。开启  n 个线程并标记序号,各个线程打印出它的序号。直到有 m 个线程被调度时,停止全部线程。

打印出的序号即是 m 个等概率出现的数字。

面试官听到这个解决的方法,吸了一口凉气。预计心里在想,这小伙疯了!我当时自知这个解决方式不是面试官想要的。于是说了,假设这个 n 非常大,那么就要另

想办法了,由于不可能在一个进程里产生随意多个线程。

想啊想,过了两分钟。还是没有找到解决的方法。面试官非常 nice 让我回去后再想一想。

事实上这个题细想。有些难度。你可能有一种思路:假设能一次性取出 m 个数字。再保证各数字是随机的。则能够满足等概率。但。

。。

怎样一次性取出 m 个数字呢?

一次性生成多个随机数?假设生成的数字有同样则不是等概率的。由于这个数字比其他的数字出现的次数多,则概率大些(虽然你忽略了它出现多次)。

或者还有思路:

每次出一个,保证取 m 次得到的各数字概率相等。听起来。似乎这样的思路要难些。

[解法一]

我们来模拟这个过程。设有一个长度为 m 的辅助数组 B,用来装选中的数字。数组末满时,依次从长度为 n 的数组 A 中每次取一个数字顺序放入 B 中。直到 B 满了。

设对于兴许的每个元素,其装入 B 中的概率为 x。此元素装入 B 中的操作是将它与 B 中的某个元素置换。

则 B 中已经存在的某个元素,继续在 B 中存在的概率为:(1-x) + x*(m-1)/m。即当前取出的元素不进入 B。被直接舍弃,或者当前取出的元素进入 B ,但置换不发生在这个元素身上。

当前 A 中取出的元素。在 B 中存在的概率为 x。即,仅仅要这个元素被选中,就一定会进入 B。

因为要使用每一个元素的概率相等。则有:

x = (1-x) + x*(m-1)/m。故 x = m/(m+1)。

也即。按上面的操作便能保证每个被留下来的元素的概率相等且为 x ,即 m/(m+1)。

[解法二]

事实上。我们考虑一下,这个模型不就是抽奖的模型吗,有 n 张彩票,n 个人每人一张,怎样选出 m 个人出来中奖。即。我们仅仅须要模拟一个公正的抽奖过程便能得到等概率的 m 个人。

我们都知道,抽奖不分先后。每一个人中奖的机率都一样。因此。最简单的做法是将 n 个人随机化排成一列,再取前 m 个人中奖就可以。

那么,我们借助洗牌算法便能做到。那么,怎样得到一个好的洗牌算法呢?一个能够证明是均匀的方法例如以下:

对于第 i 张牌,它以 i/(i+1) 的概率与前面 i 张牌交换,实际操作时,能够生成一个 0 ~ i 之间的随机数。当其不为 i 时运行交换。交换的操作是:将此牌与前面 i 张牌随机交换。

于是,能够证明。第 i 张牌在位置 i ,也即。它没有发生交换的概率为 1 - i/(i + 1)= 1/(i+1)。

第 i 张牌在前面不论什么一个位置的概率为 i/(i+1)*1/i = 1/(i+1) 。可是,我们还须要证明,前 i 张牌中的随意一张在前面
i 个位置中的随意一个位置的概率为 1/(i+1) 才算是证明全然。假设直接入手。这个证明能够想象是相当复杂的。我们使用数学归纳法证明,按上面的操作,随意第 i 张牌被操作完毕后,总共的 i + 1 张牌中的随意一张在0 ~ i 的随意一个位置上的概率为 1/(i + 1)。

证明:

当仅仅有一张牌(第 0 张牌)时,在位置 0 上的概率为 1。

如果第 i 张牌被操作完毕后。总共的 i + 1 张牌中的随意一张在0 ~ i 的随意一个位置上的概率为 1/(i + 1)。

则对于第 i + 1 张牌被操作后:

前面已经证明过。第 i+1 张牌放置在 0 ~ i+1 中的任何位置的概率为 1/(i+2)。

对于 0 ~ i 中的随意一张牌 x,它原先在 0 ~ i 上任何位置的概率为 1/(i + 1)。

x 被换到第 i+1 位置的概率为 (i+1)/(i+2) * 1/(i+1) = 1/(i+2)。x 如今还在 0 ~ i 位置的概率即 1 减去前者,为 : 1 - 1/(i+2)。

而 0 ~ i 共同拥有 i + 1 个位置,故 x 在随意一个位置的概率为:

(1 - 1/(i+2))*1/(i+1) 结果为 1/(i+2)。

于是就证明原结论。因此。这是一个平衡的洗牌算法。

[解法三]

假设我们每次在面临第 i 个元素,不是像解法一中的,维持一个固定的概率去决定该元素是否留下,而是与当前已处理过的元素个数相关,是否能得到一个解法呢?

设总共已处理的个数为 N。当前正要处理的是第 i 个元素。辅助数组为 B。源数组为 A。操作例如以下:

1.当 i <= m 时。元素留下。

2.当 i > m 时,使用概率 m/N 决定元素的去留。假设元素留下。则它随机与 B 中某个元素 x 置换(丢弃x,保留该元素)

证明等概率:

1.当 i = m + 1 时,此元素留下的概率为 m/(m+1)。B 中随意一个元素留下的概率为:1-m/(m+1) + m/(m+1)*(m-1)/m = m/(m+1)。故此时,B 中全部元素的概率为 m/(m+1)。

2.设当已处理 N 个元素时,B 中的元素的概率为 m/N。

则当已处理 N+1 个元素时,当前元素留下的概率为 m/(N+1)。B 中随意一个元素留下的概率为:m/N*(1-m/(N+1) + m/(N+1)*(m-1)/m) = m/(N+1)。最前面的 m/N 表示此元素在 B 中,否则不在 B 中。

故使用上面的操作方法。留下的元素的概率是相等的。且与总共处理的元素的个数是相关的。

这一模型和解法一中的模型是不同样的。注意差别:

解法一中的模型适用于保留下来的元素概率相等。且永远不变。

解法二中的模型适用于保留下来的元素概率相等。但随着处理的元素的个数添加而改变,这也意味着,当须要从未知数目的数据源中取 m 个数字,使其等概率,这样的方法是很适用的。

[解法四]

我们已经有一个心得了,解法方案好像类似于:面临当前元素时。使用一个概率(这个概率可能是动态变化的。或者不变的)决定去留,若留,则与某个已选择的元素置换。以下再给出一种方法。

设 A 为源数组。B 为辅助数组(装入已选择的元素)。A 长度为 n。B 长度为 m。须要从 A 中取 m 个数字放入 B。使它们等概率。

遍历 A,在面临第 i 个元素 x 时,记 p 为还须要从 A 中选出的元素个数。q 为从 x 向后数,将 A 数完的个数。包含 x。决定 x 被选中的概率设置为 p/q。这也能够达到等概率。

1:第 0 个元素被选中的概率为 :m/n

2:第 1 个元素被选中的概率为 :m/n*(m-1)/(n-1) + (1-m/n)*m/(n-1) = m/n

3:第 2 个元素被选中的概率为:... = m/n

....

依此类推,不管哪个元素被选中的概率都为 m/n。以下,我们证明随意一个元素被选中的概率都为 m/n。

假设按上面的思路去证明将非常复杂。可是有一个非常巧妙的证明方法。

我们看这个问题的模型,实际上,它就是一个抽奖模型,如今有一个箱子里面装着 n 张奖券,写着“中”。或“不中”,当中。写着“中”的有 m 张,如今问。第 k 次抽奖,中奖的概率为

多少?这显然为 m/n!

还记得 "抽奖与顺序无关” 吗?于是。我们独立写出第 k 次中奖的概率的表达式:

C(m,1)*A(n-1,m-1) / A(n,m) = m/n。

故,上面的操作方法,随意一个元素被选中的概率都为 m/n。

解法四是对抽奖的全过程进行概率模拟,而解法二是对抽奖的前置处理进行模拟。

此解法的模型适用于保留下来的元素概率固定且相等。

从 n 个数字中选出 m 个不同的数字,保证这 m 个数字是等概率的的更多相关文章

  1. 查找n个数字中的最大值

    闲来无事,试试用arg_list查找n个数字中的最大者. 又因为本人喜欢模板, 所以就早早的写了以下代码, 没有经过严格测试. /*********************************** ...

  2. 小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。 例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11.如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6 现在小易给你n个数,让你找出无法从n个数中选取部分求和

    小易邀请你玩一个数字游戏,小易给你一系列的整数.你们俩使用这些整数玩游戏.每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字. 例如: 如果{2,1,2 ...

  3. 一串数字中,只有一个数字出现一次,其他数字都出现两次,查找出这个数字(python)(原创)

    背景: 电话面试&手撕代码 2019.03.22 Mufasa 问题: 一串数字中,只有一个数字出现一次,其他数字都出现两次,查找出这个数字 条件: 这串数字是有序数 解决方法: 核心代码只有 ...

  4. 谷歌笔试题--给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含), 指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数。

    谷歌笔试题--给定一个集合A=[0,1,3,8](该集合中的元素都是在0,9之间的数字,但未必全部包含), 指定任意一个正整数K,请用A中的元素组成一个大于K的最小正整数. Google2009华南地 ...

  5. 为什么Java7开始在数字中使用下划线

    JDK1.7的发布已经介绍了一些有用的特征,尽管大部分都是一些语法糖,但仍然极大地提高了代码的可读性和质量.其中的一个特征是介绍字面常量数字的下划线.从Java7开始,你就可以在你的Java代码里把长 ...

  6. 一道经典的面试题:如何从N个数中选出最大(小)的n个数

    转载:https://zhidao.baidu.com/question/1893908497885440140.html 这个问题我前前后后考虑了有快一年了,也和不少人讨论过.据我得到的消息,Goo ...

  7. “全栈2019”Java第十六章:下划线在数字中的意义

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. day04_03 题目判断三个数字中的最大值

    num1 = input("Num1:") num2 = input("Num2:") num3 = input("Num3:") 输出三个 ...

  9. SQL Server 从一组数字中随机获取一个数

    很多人在开发需求中想获取一个随机数,或者从一组数字中获取一个数, 这个需求很简单,而且有很多方式可以实现,下面就介绍几种常见的方式,以作为笔记或供有需要的人参考. 比如有一组数字: 57 59 63 ...

随机推荐

  1. 读书笔记--C陷阱与缺陷(五)

    第五章 第五章干货也偏少,但是几个练习题还不错,写出来大家分享下: 1.当一个程序异常终止时,程序输出的最后几行常常会丢失,原因是什么? 我们能够采取怎么样的措施来解决这个问题? 答:因为异常终止的程 ...

  2. 洛谷P2300 合并神犇

    传送门啦 分析: 刚开始读完题后感觉很懵,怎么算都不是3,结果发现题目理解错了.题目要求的是求一个不降的序列,不是递减的(发现自己好傻) 看明白题就好做了吧.经典的区间dp题,合并果子大家应该都做过, ...

  3. 云计算IaaS浅谈

    (本篇文章仅仅是整理文档资料时,发现的一篇课程报告,感觉还挺有参考意义的) 最近几年云计算一直是IT业的热点,一股炽热的云计算浪潮席卷了世界,全世界都在讲云计算,都在搞云计算.虽然最初是由谷歌公司提出 ...

  4. python 多线程删除MySQL表

    一.需求分析 在<python 统计MySQL表信息>这篇博客中,链接如下: https://www.cnblogs.com/xiao987334176/p/9901692.html 已经 ...

  5. Ubuntu Touch On Nexus4 Manual Install (手动安装) under Gentoo

    Table of Contents 1. 准备工作: 2. Saucy Salamander 3. 刷入 最新 版Touch 最近手里的 Nexus 4 手机一直闲置,它的配置要比我六年前买的笔记本还 ...

  6. CCF CSP 201709-3 JSON查询

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201709-3 JSON查询 问题描述 JSON (JavaScript Object Not ...

  7. day6 ConfigParser模块 yaml模块

        yaml模块: python可以处理yaml文件,yaml文件安装的方法为:$ pip3 install pyyaml    configparser模块,用来处理文件的模块,可以实现文件的增 ...

  8. awk调用shell命令的两种方法:system与print

    from:http://www.oklinux.cn/html/developer/shell/20070626/31550.htmlawk中使用的shell命令,有2种方法: 一.使用所以syste ...

  9. 模拟界面请求到web服务器

    客户端 package com.lsw.client; import java.io.*; import java.net.*; import java.util.*; public class HT ...

  10. vue 单向数据流 & 双向绑定

    在react中是单向数据绑定,而在vue中的特色是双向数据绑定.但是其实就我个人的理解是: 其实无论是vue还是react其实还是提倡单向数据流去管理状态,这一点在vuex和redux状态管理器上体现 ...