C++,python,热爱算法和机器学习
全部博文(1214)
分类:
2011-05-28 23:01:42
大家应该都听说过这个老题目:有 1000 个一模一样的瓶子,其中有 999 瓶是普通的水,有一瓶是毒药。任何喝下毒药的生物都会在一星期之后死亡。现在,你只有 10 只小白鼠和一星期的时间,如何检验出哪个瓶子里有毒药?
这个问题的答案也堪称经典:把瓶子从 0 到 999 依次编号,然后全部转换为 10 位二进制数。让第一只老鼠喝掉所有二进制数右起第一位是 1 的瓶子,让第二只老鼠喝掉所有二进制数右起第二位是 1 的瓶子,等等。一星期后,如果第一只老鼠死了,就知道毒药瓶子的二进制编号中,右起第一位是 1 ;如果第二只老鼠没死,就知道毒药瓶子的二进制编号中,右起第二位是 0 ⋯⋯每只老鼠的死活都能确定出 10 位二进制数的其中一位,由此便可知道毒药瓶子的编号了。
现在,有意思的问题来了:如果你有两个星期的时间(换句话说你可以做两轮实验),为了从 1000 个瓶子中找出毒药,你最少需要几只老鼠?注意,在第一轮实验中死掉的老鼠,就无法继续参与第二次实验了。
答案:7 只老鼠就足够了。事实上,7 只老鼠足以从 37 = 2187 个瓶子中找出毒药来。首先,把所有瓶子从 0
到 2186 编号,然后全部转换为 7 位三进制数。现在,让第一只老鼠喝掉所有三进制数右起第一位是 2
的瓶子,让第二只老鼠喝掉所有三进制数右起第二位是 2 的瓶子,等等。一星期之后,如果第一只老鼠死了,就知道毒药瓶子的三进制编号中,右起第一位是 2
;如果第二只老鼠没死,就知道毒药瓶子的三进制编号中,右起第二位不是 2,只可能是 0 或者 1
⋯⋯也就是说,每只死掉的老鼠都用自己的生命确定出了,三进制编号中自己负责的那一位是 2 ;但每只活着的老鼠都只能确定,它所负责的那一位不是 2
。于是,问题就归约到了只剩一个星期时的情况。在第二轮实验里,让每只活着的老鼠继续自己未完成的任务,喝掉它负责的那一位是 1
的所有瓶子。再过一星期,毒药瓶子的三进制编号便能全部揭晓了。
类似地,我们可以证明, n 只小白鼠 t 周的时间可以从 (t+1)n 个瓶子中检验出毒药来。
感到自己到弱智了,还有一道衍生题:
1000个瓶,但是2瓶有毒,10个老鼠,一周时间,最多可以确定多少瓶没有毒?自己没有想到。
给力的大家给出了给力的想法。
1个老鼠喝100瓶,可以确定800瓶没有毒。1只老鼠喝1000/11 = 91瓶,余下90瓶,可以确定1000 - 91*2 = 818瓶没有毒。
如果把1000瓶放入5x5的矩阵中,5只老鼠喝横行,5只老鼠喝纵列,最多有4个格子不能确定,这种情况发生在无法确定是(1,1)(2,2)还是(1,2)(2,1)的状态下。因此可以确定1000 - 1000/25 * 4 = 840瓶没有毒。
如果把1000瓶放入6x6的矩阵中,5只老鼠喝横行,5只老鼠喝纵列,空出没有老鼠喝的一行和一列,也是和上面一样可行的,最多也是4个格子无法确定,但这次可以确定1000 - 1000/36 * 4 = 888瓶没有毒。
如果二维的可行,那么我们可以扩展到3维。
把1000瓶药水放入3x3x4的长方体阵,考虑老鼠可以空出一行一列不喝,分成4x4x5的长方体阵。3只老鼠喝横行,3只老鼠喝纵列,4只老鼠喝第三方向空间延伸,横纵伸各空出一行来。最多有8个格子无法确定。和二维的类似,假设前两横行,前两纵列,前两延伸分别有2只老鼠共6只死去,在三维空间中形成一个正方体。有毒的药水一定是4对对角线中的一种情况,而我们无法确定的就有8瓶药水了。所以可以确定出 1000 - 1000/(4*4*5) * 8 = 900瓶没有毒。