用 Python 解决“老鼠喝药水”问题

前言
我在网上高强度冲浪时偶然发现了这个问题,经过 OI 大神的指点,再加上 CSDN 的帮助,终于想出了解决办法。
题目
你面前有 100 瓶打乱的药水,其中只有一瓶是毒药。你有 7 只老鼠,喝了毒药的老鼠会死,反之不会。请利用这 7 只老鼠设计一种方案,根据老鼠的死活情况找出毒药。
假设每只老鼠可以喝无限量的药水,且每瓶药水不会被喝完。在方案实施过程中,你无法得知当前的执行情况,也就是说,你不能根据前一只老鼠的死活来决定后续操作。
难点
这个问题最困难的地方在于,我们必须等所有步骤都完成后才能知道结果,无法根据前一只老鼠的死活来决定后续操作。
在和同学讨论时,大家都倾向于用二分法不断逼近,但即使不考虑上面的限制,单说二分法,我们很可能在七只老鼠都死完后还是找不到那瓶毒药。
这时候,以我们高中常规的数学方法,已经解不了这道题了,那该怎么办?
二进制法
二进制是计算机的语言,让我们先粗略了解一下二进制。
我们平时用的是十进制,也就是逢 10 进 1。当我们写到第十一个数时是 11,而不是别的;就像十六进制里第十一个数用 a 表示一样。二进制同理,逢 2 进 1,写到第三个数时,二进制用 11 表示。
001 # 1
002 # 2
011 # 3
100 # 4
101 # 5
后面是十进制数,# 前面是二进制数,采用三位数对齐,1 左边的 0 没有实际意义。
了解了二进制,我们再来看题目:7 只老鼠,100 瓶药水。我用 Python 写了二进制转换算法,列出了 1 到 100 所有数的二进制。
x=0
for i in range(0,100):
x=x+1
y = x
b=""
while(y>=1):
b=str(num%2)+b
num=num//2
print(b)
我们发现,100 的二进制是 1100100,正好是七位数,这意味着 100 以内的数,其二进制表示永远不会超过七位,而且每个十进制数只对应一个二进制数。正好我们有 7 只老鼠,可以利用这个特性来解决问题。
从左到右七个数,每个数对应一只老鼠。当某一位是 1 时,就让对应的老鼠喝这瓶药水。比如喝第 35 瓶药水,先算出 35 的二进制,并用七位数对齐,得到 0100011。从左到右,第 2 位、第 6 位、第 7 位是 1,那么就让第 2、6、7 只老鼠去喝。如果这三只老鼠都死了,那这瓶就是毒药;如果没死或没全死,那就不是毒药,因为这几只老鼠可能还要喝别的药水,我们无法判断。
讲解结束,开始实践。
实践推演
我们用 Excel 来统计,也就是列表。用 Python 的二进制算法得到 1-100 的所有二进制数,并导入 Excel。给七只老鼠编号,让它们喝对应的药水。
我们假定第 84 号药水有毒,接下来进行推演。

可以看到,当一组中所有老鼠全部死亡时,才能判断这组是毒药,也就是第 85 瓶。
换位思考
当我写表格的时候,我发现如果不从数学思维出发,而是把它看成生物遗传:当同时满足这几个基因均为显性基因时,表现型才为显性。但我们要明白,二进制解法是关键,如果没有二进制,