一个非常有意思的概率趣题:猜红包问题

2,007 阅读4分钟

导语

春节将近,就发一期关于红包的问题。之前在某平台看到这样一个有趣的问题:

假设有3个红包,但是只有其中一个红包里面有钱,你可以从中任意选择一个红包。在你选择完之后,我会在剩下的两个红包里面去掉一个空的红包,这时候,还会剩下两个红包,一个有钱,一个没钱。现在给你一个机会,你是会更换你的选择呢?还是会坚持你的选择?

1.gif

如果是你你会怎么做?在看下面的讨论之前,你可以先思考一下,如果是你的话,你是会去更换选择的红包,还是坚持自己的选择呢?或者说你觉得都无所谓,换不换都是没有意义的。

从直观角度分析

在我向身边的人提出这样有意思问题的时候,我得到的答案大多数是认为换不换都无所谓,理由是移除一个没有钱的红包之后,还剩两个,那么随便选哪个都是50%的概率啊,所以换不换又有什么关系呢?

似乎很有道理,但实际上却不是这样的。当你在第一次选择红包的时候,你的概率是 1/3,那为什么我在剩下来的两个里面删除掉一个没有钱的红包,实际上你什么都没有做,那么概率依然应该是你最开始选择时候的概率。

举一个非常极端的例子:假设我的红包有 1万个,你选其一,中奖的概率是 1/10000,显然是不太可能被选中的。然后我在剩下的9999移除掉 9998 个空的红包,你是否会换呢?如果你依然觉得剩下还是2个红包,换不换都是50%,那这个结果很明显是错误的了,因为最开始你就知道,你显然不太可能会中,怎么可能突然间你选择的选项变成50%了呢?

所以答案应该是:如果给我换的机会,我会选择换!

图解一下:

1.gif

通过以上分析,如果选择换的话,那么中奖概率将会变成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,可以运行一下看看。

ScreenRecorderProject23.gif

可以发现未做交换时概率普遍在 33%左右, 做了交换后概率普遍在 66%左右,实践和理论是很接近的。

所以你现在觉得应不应该改变你最初的选择呢?