导语
春节将近,就发一期关于红包的问题。之前在某平台看到这样一个有趣的问题:
假设有3个红包,但是只有其中一个红包里面有钱,你可以从中任意选择一个红包。在你选择完之后,我会在剩下的两个红包里面去掉一个空的红包,这时候,还会剩下两个红包,一个有钱,一个没钱。现在给你一个机会,你是会更换你的选择呢?还是会坚持你的选择?
如果是你你会怎么做?在看下面的讨论之前,你可以先思考一下,如果是你的话,你是会去更换选择的红包,还是坚持自己的选择呢?或者说你觉得都无所谓,换不换都是没有意义的。
从直观角度分析
在我向身边的人提出这样有意思问题的时候,我得到的答案大多数是认为换不换都无所谓,理由是移除一个没有钱的红包之后,还剩两个,那么随便选哪个都是50%的概率啊,所以换不换又有什么关系呢?
似乎很有道理,但实际上却不是这样的。当你在第一次选择红包的时候,你的概率是 1/3,那为什么我在剩下来的两个里面删除掉一个没有钱的红包,实际上你什么都没有做,那么概率依然应该是你最开始选择时候的概率。
举一个非常极端的例子:假设我的红包有 1万个,你选其一,中奖的概率是 1/10000,显然是不太可能被选中的。然后我在剩下的9999移除掉 9998 个空的红包,你是否会换呢?如果你依然觉得剩下还是2个红包,换不换都是50%,那这个结果很明显是错误的了,因为最开始你就知道,你显然不太可能会中,怎么可能突然间你选择的选项变成50%了呢?
所以答案应该是:如果给我换的机会,我会选择换!
图解一下:
通过以上分析,如果选择换的话,那么中奖概率将会变成2/3。
从数学角度去分析
也许上述表达不够明确,或者别人认为不够说服力,那么我们从数学的角度去分析一下。
假设,我每次选的都是第一个红包,可以列出一个中奖概率表:
当我不做更换时:
红包所在位置 | 是否中奖 |
---|---|
1 | 是 |
2 | 否 |
3 | 否 |
两个否,一个是,那么中奖概率 1/3
当我选择更换时
红包所在位置 | 是否中奖 |
---|---|
1 | 否 |
2 | 是 |
3 | 是 |
中奖概率是 2/3
所以说,也许答案已经出来了。很明显我选择更换红包的话中奖概率要大一些,为 2/3
从实践角度去分析
概率并不是100%按照我们所计算的概率去发生次数的,但一定是接近于理论次数。实践是检验真理的唯一标准,我们可以通过时间去检验上述理论是否成立,这样才最有说服力。
要想达到最理想的实践环境就必须做大量的实验,且不说很浪费时间,还会有人为主观的因素的干扰。作为一名程序员,我打算让计算机去做这个事情,我们所要做的就是编码!
设计一个程序
我们的目的是做题目上的实践,所以我们可以开始设计程序:
-
定义一个数组,
packages
红包的数组 -
数组内的值随机一个为 true,另外两个是 false. (true表示中奖的红包,false表示没有中奖的红包)
-
随机选择一个红包:利用随机函数生成一个选择的下标
selectIndex
-
在没有被选择的剩下的两个红包中移除一个空的红包:
package
中移除一个元素,这个元素的下标不等于selectIndex
且这个元素的值是false
-
分别 交换/不交换
selectIndex
-
输出中奖结果, 输入大量的测试用例,输出中奖概率。
编写一个程序
按照以上思路,我们可以编写出如下的程序:
const swap = function (selectIndex) {
return selectIndex === 0 ? 1 : 0 // 交换下标
}
const handle = function (isSwap) {
const packages = [] // 红包数组
const moneyIndex = Math.ceil(Math.random() * 3 ) - 1 // 设计随机数,红包内有钱的下标
// 遍历红包,将钱放入第 moneyIndex 中
for (let index = 0 ; index < 3 ; index++) {
packages.push(moneyIndex === index) // true 表示有红包。 false 表示空红包
}
let selectIndex = Math.ceil(Math.random() * 3 ) - 1 // 设计随机数,随机选择一个红包
const result = packages[selectIndex]
for (let index = 0 ; index < 3; index++) {
if (index !== selectIndex && packages[index] === false) { // 移除一个没有选中,并且是空的红包
packages.splice(index, 1) // 移除这个红包
selectIndex = index < selectIndex ? selectIndex - 1 : selectIndex
isSwap ? selectIndex = swap(selectIndex) : '' //和剩下的那个红包交换
break
}
}
return packages[selectIndex] // 输出中奖结果
}
const res = [] // 结果数组
const count = 10000 // 测试次数
for (let index = 0 ; index < count; index++) {
res.push(handle(false)) // 开始测试,将结果放入结果数组中 -true 交换 -false 保持原来选择
}
const trueList = res.filter(item => {
return item === true // 取出所有中奖的测试用例
})
console.log(`共抽:${count}次,中奖次数为: ${trueList.length}次,中奖率为: ${(trueList.length / count).toFixed(4) * 100}%`)
由于程序没什么难度,所以就仅仅提供注释,就不做讲解了。我们的测试次数是 10000,可以运行一下看看。
可以发现未做交换时概率普遍在 33%左右, 做了交换后概率普遍在 66%左右,实践和理论是很接近的。
所以你现在觉得应不应该改变你最初的选择呢?