python解决开盲盒问题
这个问题是某天我们年级的一位同学提出的,感觉很有意思,就在这里记录一下。
本题算是数学方法应用在生活中的一个小例子了。
一、问题描述
开盲盒问题:现在有 12 个盒子,里面分别装了物品A-L。每个盒子都装有一个物品;每个物品都装在一个盒子中。
在开盲盒之前,有一次透视机会,可以知道其中任意一个盒子里是什么(例如开了 3 号盒子可以得知里面是物品 C)。
现在每个盒子有3次提示的机会,提示内容分别是“该盒子中不是 X”(X是物品编号),同一个盒子中的3次提示内容不会重复,但盒子之间的提示是会重复的。此外,还有额外的6次机会,可以随机选 6个盒子,得到第四条提示。
例如:目前开了1个盒子,并对另外两个盒子的物品进行了提示,情况如下:
盒子编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
第一次提示 | 非 A | 非 A | 是 C | |||||||||
第二次提示 | 非 B | 非 D | ||||||||||
第三次提示 | 非 C | 非 E | ||||||||||
额外的提示 | 非 K | 非 L |
编写程序,当输入所有已知条件时,输出物品A-L在每个盒子里的概率。
二、思路与代码
(一)物品位置状态表的获取
题干中的表格给了我们很大的提示,我们也可以建立一张表来展示各个物品在不同盒子中的概率。如下表所示,我们建立了一个12x12的状态表,其中每一行代表一个物品(A-L对应第0-11行),每一列代表一个盒子(1-12号盒子对应第0-11列)。
我们定义数字 1代表物品可能在盒子中,0代表物品不可能在盒子中。因此根据题目已知条件,这样的表格如下:
盒子编号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
A | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
B | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
C | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
D | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
E | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
F | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
G | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
H | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
I | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
J | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
K | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
L | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
通过代码得到上面的表格并不难,即使输入条件有变化,我们也可以根据输入条件的不同进行改变。
下面是我们的代码:
1 | import numpy as np |
输入条件在代码的 inspect_box
, inspect_item
, tips_table
三个变量中定义(见上述代码“此处根据看到的具体情况进行赋值”的提示语句)。
inspect_box
: 在一次透视机会中,查看的盒子的下标。盒子的下标等于盒子编号减1。inspect_item
:在一次透视机会中,看到的物品的下标。物品编号A-L分别对应物品下标0-11。tips_table
:通过每个盒子的3次提示机会,和6次随机盒子的提示机会,得到的所有信息。这些信息按二维数组进行组织(见代码),其中外层数组的下标对应盒子下标,内层数组的元素代表不会出现的物品的下标。内层数组可以是空数组。
(二)概率值的计算
上面得到的仅仅是一个状态表。我们需要知道每个物品出现在各个盒子中的概率。而这个概率,可以用线性规划的方法解出来。
具体来说,当我们把12x12的状态表变成12x12的概率表以后,表格中的每个格子就变成了对应物品在对应盒子中出现的概率。一共144个格子(144个待求解概率),因此线性规划的变量个数为144个。
除此之外,我们需要明确两点事实:
- 任意一个盒子中,各个物品出现概率的总和为1。(也就是说对于一个特定的盒子,从物品A到物品L,不论每个物品各自出现的概率有多大,这些物品出现概率的总和一定是1)
- 任意一个物品,出现在每个盒子中的概率的总和为1。(也就是说对于一个特定物品,它在盒子1到盒子12中出现的概率值加在一起也是1)
放到12x12的概率表上,就是这个概率表的每一行的元素总和全为1,每一列的元素总和也全为1。由此可以得到12×12=24个约束条件函数。
此外,每个变量也有自己的边界条件。概率的取值范围为 $\in[0,1]$ ,然而对于不可能出现的物品来说,概率值一定为0。这是另外144个条件。
我们的目标函数是概率总和最大;然而对于这个问题来说,目标函数是什么其实无所谓,因为上面的这些约束条件足以求出我们所要的结果。
求解线性规划问题的方法有很多,此处我们使用scipy进行求解
代码如下:
1 | # 这段代码需要接在上面那段代码的后面 |
三、代码输出:
全部的代码输出如下,其中程序首先输出了12×12状态表,并打印出各个物品可能出现在哪些盒子中,最后输出了12×12的概率表,从概率表中我们可以选择每个物品出现概率最大的盒子,以确定要开哪一个盲盒。
1 | 状态表= |
由于已知条件的限制,我们得到的概率表中不同盒子之间的概率差异还是有点小,不足以判断一个物品最有可能出现在哪个盒子中。当已知条件足够多(例如使用完全部36次提示机会和6次额外机会),则得到的概率值会更加精确。