有1000杯水,其中1杯有毒。 老鼠只要喝下一滴毒药,就会在两小时内死亡。
假设每杯有足够的水并且有足够的采样设备。
如果只需要2小时,需要多少只老鼠才能找出哪杯水有毒?
问题分析1
这个问题比较直观,也容易理解。 解决这个问题的算法的好坏可以通过两个因素来评价:时间成本和鼠标成本。
显然,该问题对时间和鼠标成本都有要求。 可以选择先找到一个直观的算法,然后对直观的算法进行分析和改进,得到符合要求的算法。
问题的初步抽象
对于水:我从0开始为每杯水编号,0为第一杯水的编号,999为第1000杯(最后一杯)水的编号。 另外,为了描述方便,将0号杯水简称为0号水,其他编号的各杯水亦同。
对于小鼠:我对每只消耗的小鼠从0开始编号,以0作为第一个消耗的小鼠的编号,其他编号的小鼠以此类推。
问题的初步解决方案
秉承循序渐进的原则,不考虑时间成本,也不考虑老鼠的成本。
那么问题就可以变成如何花费一定的时间和一定数量的老鼠从1000杯水中找出有毒的那杯水。
这个问题很直观,很容易找到下面的算法:
直观的算法1
1. 用1只小鼠从第一杯水开始喝水,喝完水后等待2小时。
2. 如果老鼠在2小时后死亡,那么老鼠2小时前喝的这杯水是有毒的。 如果没有死,老鼠会继续喝1杯水,直到喝完最后一杯水或死亡。
直观算法2
1. 使用 1,000 只小鼠。 每只小鼠喝一杯与自己相同数量的水,喝完水后等待2小时。
2. 2小时后毒水,老鼠喝下的这杯水是有毒的。
算法1评估
最差时间成本:2000小时(可改善至1998小时)
老鼠数量成本:1
算法2评估
最差时间:2小时
老鼠成本:1000只(当然可以提高到999只)
问题分析2
上述两种算法直观,但效率不高。 由于算法2比算法1更接近问题需求(只需要2个小时),所以这里我们重点对算法2进行分析和改进。
算法2之所以需要1000只老鼠,是因为每只老鼠只喝了1杯水。 也就是说,1只老鼠只存储了一杯水是否有毒的信息。 如果能让每只老鼠喝更多杯水,从而存储更多关于水是否有毒的信息,那么老鼠的成本就可以降低。
当有4杯水时,你可以用2只老鼠找到有毒的那杯水; 0号小鼠可以同时喝0号和1号水,1号小鼠可以同时喝0号和2号水。 根据0号和1号老鼠的“反应”找到那杯有毒的水。 具体来说:如果两只老鼠都死了,那么这杯毒水就是0号; 如果两只老鼠都存活下来,那么这杯有毒的水就是第三杯; 如果0号老鼠存活,1号老鼠死亡,则2号水和1号水有毒; 如果1号老鼠存活,0号老鼠死亡,则1号水有毒。 由此可以得出结论,需要2只老鼠才能找到4杯水中有毒的1杯。
按照这个方法,需要花费500只老鼠才能解决这个问题! 至此,就得到了一个新的算法,这里称为算法3!
算法3
使用500只小鼠,每组2只小鼠,共250组
1、0号小鼠喝0号水和1号水; 1号小鼠喝0号和2号水。
2号大鼠喝4号和5号水; 3号老鼠喝4号和6号水。
……
直到,498号老鼠喝了号水; 499号老鼠喝号水
2.当一只老鼠死亡时,可以根据同组中另一只老鼠的存活情况来找到这杯有毒的水。
算法3评估
最差时间:2小时
老鼠数量成本:500
问题分析3
算法3:根据任意一组小鼠的生存状况,只能判断该组小鼠喝下的水是否有毒,而无法帮助判断其他数量的水。
4杯水的问题可以由2只老鼠一组来解决。 所以这里假设1000杯水的问题也被一组N只老鼠解决了。 任意1杯水是否有毒,都是由这N只老鼠共同决定的!
问题进一步抽象
老鼠喝水的话用0表示,不喝水的话用1表示;
存活用1表示,小鼠死亡用0表示,
所有饮水条件和生存条件按照小鼠数量从左到右排列;
当有4杯水时,使用2只小鼠。
对于0号水,两只老鼠都喝了,定义为0, 0
对于1号水,0号老鼠喝了它,但1号老鼠不喝它,定义为0,1
对于2号水,0号小鼠没有喝它,但是1号小鼠喝了它,定义为1,0
对于3号水,0号小鼠不喝,1号小鼠也不喝,定义为1,1
可以将水数0~3改成如下的二进制形式:
00,
01,
10、
11
小鼠饮水情况为:
0号大鼠喝0号水和1号水;
1号大鼠喝0号和2号水;
老鼠的生活条件有四种:
0号小鼠死亡,1号小鼠死亡; 用0,0表示,此时0号水是有毒的。
0号小鼠死亡,1号小鼠存活; 用0和1表示,此时1号水是有毒的。
0号小鼠存活,1号小鼠死亡; 用1,0表示,此时2号水有毒。
0号小鼠存活,1号小鼠存活; 以1,1表示,此时3号水是有毒的。
可以发现,两只小鼠的生存状态与水数是对应的。
这里可以把生存情况当成二进制,转成十进制。 此时,其值为有毒的一杯水的数量。
当有8杯水时,根据以上结论。 使用 4 只老鼠可以让你找到那杯有毒的水,但这不太可能是一个有效的方法,因为 8 杯水被分为 2 个不同的组。 难道只有用3才能找到吗? 假设能查到,这里根据4杯水的处理方法推导一下:
对于0号水,0、1、2号小鼠都喝了,定义为0、0、0。
对于1号水,0号小鼠喝水,1号小鼠喝水,2号小鼠不喝水,定义为0,0,1
对于2号水,0号小鼠喝水,1号小鼠不喝水,2号小鼠喝水,定义为0,1,0
对于3号水,0号小鼠喝了,1号小鼠不喝,2号小鼠不喝,定义为0,1,1
对于4号水,0号小鼠不喝,1号小鼠喝,2号小鼠喝,定义为1,0,0
对于5号水,0号小鼠不喝水,1号小鼠喝水,2号小鼠不喝水,定义为1,0,1
对于6号水,0号小鼠不喝水,1号小鼠不喝水,2号小鼠喝水,定义为1,1,0
对于7号水,0、1、2号小鼠不喝,定义为1、1、1
小鼠饮水情况为:
0号大鼠喝0、1、2、3号水;
1号大鼠喝0、1、4、5号水;
2号大鼠喝0、2、4、6号水;
老鼠的生存条件有8种:
0号小鼠死亡,1号小鼠死亡,2号小鼠死亡; 用0,0,0表示,此时0号水是有毒的。
0号小鼠死亡,1号小鼠死亡,2号小鼠存活; 用0、0、1表示,此时1号水是有毒的。
0号小鼠死亡,1号小鼠存活,2号小鼠死亡; 用0、1、0表示,此时2号水有毒。
0号小鼠死亡,1号小鼠存活,2号小鼠存活; 用0、1、1表示,此时3号水有毒。
0号小鼠存活,1号小鼠死亡,2号小鼠死亡; 用1、0、0表示,此时4号水有毒。
0号小鼠存活,1号小鼠死亡,2号小鼠存活; 用1、0、1表示,此时5号水是有毒的。
0号小鼠存活,1号小鼠存活,2号小鼠死亡; 用1、1、0表示,此时6号水是有毒的。
0号小鼠存活,1号小鼠存活,2号小鼠存活; 用1、1、1表示,此时7号水是有毒的。
至此,8杯水的问题可以用3只老鼠解决。
2只小鼠的4种生存情况分别对应4杯水的数量;
3只小鼠的8种生存情况分别对应8杯水的数量。
当有N只老鼠时,由于每只老鼠有两种生存情况:0和1,所以总共有2种N次方生存情况,可以解决大小2的N次方问题。
当N为10时,有1024种生存情况。
此时,对于 1000 杯水,只需要 10 只老鼠就可以找到这杯有毒的水。
算法4
1000杯水,用10只老鼠,
将每杯水的编号从0到999以二进制形式写出
从 到 对于每 1 杯水,每 1 杯水以 10 位二进制形式编号,从左到右分别描述为第 1 到第 10 位数字。
0号老鼠喝的是第一位为0的二进制数对应的水。
1号老鼠喝的是第二位为0的二进制数对应的水。
……
8号老鼠喝的是第九位为0的二进制数对应的水。
9号老鼠喝的是第10位为0的二进制数对应的水。
2小时后,将表示生存状态的10位二进制数转换成十进制数,查出毒水的编号。
算法4评估
最差时间:2小时
老鼠成本:10只