老鼠毒药问题

一个经典的问题:有100瓶相同的瓶子,其中99瓶装的是普通的水,还有1瓶装的是毒药,外观没有区别,白鼠喝完毒药一个星期之后才会死亡,现在已有一个星期的时间,问至少需要多少只小白鼠才能找出有毒的那瓶水?

分析

问题的实质就是从100个瓶子中找出一个符合条件的瓶子,由此想到二分法查找或者红黑树,但问题的限制只有一个星期的时间,也就是每只老鼠只能判断一次就要得出答案,但每只老鼠可以同时喝多个瓶子的水,老鼠死亡说明喝的瓶子中必有一瓶是有毒的,否则没有一瓶有毒;
那么每只老鼠喝哪几瓶水呢?我们采用逆向思维,假设要能从100个瓶子中找出1个有毒的瓶子至少需要x只老鼠,每只老鼠只有活和死2中状态,那么这x只老鼠共有2^x中结果,这2^x种结果要能表示0~99中任一编号,x至少是7,因为2^6=64 < 100 < 2^7=128,那答案是7吗?还是比7大的数?
考虑二分法或者红黑树等查找算法的过程,其相同的特征都是:每进行一步,从候选中剔除一部分,重复此步骤,直至最后候选者中只剩下一个即为最后的结果,假设肯定有一个符合条件,其中剔除的过程就是在某一维度上将目标在此维度上的值和候选者在此维度上的值进行比较,将符合的候选者留下,进行下一个维度的比较,直至找到一个候选者在所有维度上与目标的所有维度相符合,即为最终结果,那现在问题转化为如何为编号划分维度

1
N = f(x1, x2, x3, x4, x5, x6, x7)

N代表编号,能够表示0~99,维度之间相互独立,每个维度至少有2个变化,如果只有一个值那这个维度的判断将没有意义,由这些条件,最容易联想到的就是二进制数的表示:

1
N = binary(x1 x2 x3 x4 x5 x6 x7)

每个变量只有0和1两种取值,7位二进制数可以表示0~99之间的任何数,符合上述假设;
回到这个问题上,那具体如何操作呢? x1代表的老鼠要喝哪几瓶水呢?可以想到如果x1死了,那x1便可以用1表示,也就是第7位为1,意味着剔除了第7位为0的编号,所以x1应该选第7位为1的编号所代表的瓶子,同理x2应该选第6位为1的编号所代表的瓶子,等等,最后,将实验结果中老鼠的死亡记为1活着记为0,按序组合成二进制所代表的编号即为有毒的瓶子。

延伸

如果问题变为:有2个星期的时间来实验,问至少需要多少只白鼠?有n个星期的时间呢?
按照前面的分析,每只老鼠有3种状态变化,0-两次都没有死,1-第一次实验死了,2-第2次实验死了,老鼠不可能死两次的,于是

1
N = ternary(x1, x2, x3, x4, x5) // 3^4=81 < 100 < 3^5=243, 三进制

实际操作是将每个编号用三进制表示,x1喝三进制第5位为1的瓶子,x2喝三进制第4位为2的瓶子,等等,若某个白鼠死了说明目标编号三进制数上的对应位为1,其余位不是0就是2,再进行第二次试验,让幸存的白鼠喝对应位是2的瓶子,若白鼠死了,则目标编号的对应位为2,最终构成的三进制数即为目标编号。
有n个星期至少需要floor(log(N)/log(n+1))个白鼠.

0%