双蛋的蛋碎问题

这是一篇近日刚由DataGenetics 发表在他们团队博客的有趣文章,我把做了一个中文翻译分享。

这是一个在互联网上流传的很有趣的智力小谜题,有人说这是微软公司的面试题,有人说这是Google的面试题,不过它的来源无关紧要,我们只是来看看它到底是什么一个情况

问题定义:现在你手上有两个蛋,还有一幢100层的楼,给你的蛋是完全一样无分别的,现在,你需要找出扔下鸡蛋,在鸡蛋落地后不碎裂的最高楼层。只要蛋没有摔坏,你就可以继续扔它,但是一旦这个蛋碎了,那么对于碎的蛋而言,扔下的楼层就属于最高楼层。

所以我们这么说:如果蛋从楼层N扔下,碎了,那么从高于N的任一楼层它都会碎;如果蛋没碎,那么从任一低于N的楼层也都不会碎。

问题:你该采取什么策略,可以用最少的扔蛋次数来找到最高楼层?最坏的情况下,需要扔多少次?

我来解释一下这个问题:问题的意思是,第一你需要找出一个理论上来说只需要很少扔蛋的测试方法,第二在该方法里,你最多会扔多少次?

声明一下,没有任何的作弊、耍诈、诡辩的策略,也不要钻进周期转速、势能和风阻的牛角尖里,这仅仅是一个简单的数学层面的问题。

现在你可以考虑几分钟,然后我们继续。

一个蛋

同时扔蛋没有被严格地的限制在规则里,我们先来设想如果我们只有一个蛋应该怎么做。

由于蛋碎了,我们就没得玩了,所以我们除了一层层的扔之外,没有别的策略,从一楼开始,只要没碎就一层一层递增,最终直到某一层,蛋碎了,那么我们就找到答案了,例如,蛋在57层碎了,那么最高楼层应该是56.

除此之外,对于一个蛋来说,没有别的解法,当然,我们可以一次上两层楼来碰碰运气,不过如果蛋碎在16楼了,我们无法知道它在15楼还会不会碎。

不过确实存在几率这个蛋很硬,直到楼顶也没碎………

无限蛋攻模式

在另一个极限里,我们有无限多个蛋怎么办?(或者说至少够我们用的蛋)这时候该采取什么策略?在这里,我们将使用程序猿最喜欢的工具之一—–二叉树

首先来到50楼扔个蛋玩玩,要么碎要么不碎,这可以使我们的工作范围减半。碎了,楼层就在1~49之间,没碎,在51~100之间。我们就这么扔下去,每次拦腰一砍,很快也能找到答案。

对于一个数学家来说,他很快就可以发现,这个需要的次数是log2 n,n 代表楼层数,这有点像是变相地求2的几次幂的情况。

由于这个楼层没有对应于2的指数,所以我们差不多最多需要扔7次( log2 100 = 6.644

用这个方法,我们只需要7次就可以搞定128层以内的楼,8次就是256层…

最终的蛋碎数目取决于最终的扔蛋结果,这是一个区间,最坏的情况是7个蛋,因为如果每次扔的蛋都碎了,而且最后在1楼也杯具了,最好的情况是0个蛋碎了,因为一直扔啊扔啊,到了100楼还不碎。

所以到了这里,我特别想开一门课:期望值与最坏值,一定很有趣

回到两个蛋

好了,我们终于又回到了两个蛋的情况,从上述的情况来看,根据二叉树的最坏结果,我们最多会扔掉个蛋,但是我们手里只有两个,这显然不靠谱。

有多么不靠谱呢,假设我们在50层扔的第一个蛋碎了,那么手里之剩下一个蛋了,如果很不巧的话,我们需要扔总共50次,如果从1楼开始尝试这个唯一的蛋。我们应该可以改进做的更好。

来,我们第一次10楼,没碎就到20楼,再来30层,如果碎了,我们手里剩下的那个蛋只需要在余下的9层楼里扔就行了,大大简化了过程。

在这个方案里,我们如果一路顺畅地来到了100楼,啪嗒,蛋碎了,那么我们从91楼开始试的话,我们最坏的情况下需要扔总共19次。

看看这个10层一次的方案,我们发现还可以做的更好。举个例子,我们在10楼碎了第一个蛋,那么我们只需要再扔9次就可以了;如果一次扔11层,碎了,我们需要多扔10次,如果没有,恭喜,我们的总次数就减少了,双赢?跟着这个想法,我们来试试

好吧,我们从12楼开始试试,和之前的步骤类似:如果碎了,我们需要用第二个蛋尝试剩下的11层楼,如此类推,但是等一下,如果我们尝试12层的推进,24.26.48.60.72.84.96,最终碎了,我们就已经浪费了8次机会,再来尝试剩下的11层,我们的总次数就已经超过了19次的方案。

最小化 最大失误

现在我们需要的是一个可以最小化我们最大失误次数的方案,上述的例子暗示我们需要的是一个在可以尝试相同深度(扔同样多的次数)的情况下能够遍历所有结果的方案,这个可以减少最不利情况的方式就是尝试让所有的方法都扔了相同的次数。

正如我希望你现在可以明白的是,如果这个方法在较低的楼层时,我们还需要额外的从区段开始的楼层一层层的尝试,  但是越是高的楼层,我们就越是已经排除了额外需要扔蛋的次数,我们需要一层层尝试的次数就减少了。

让我们来算一算这个问题。

假设我们从n 层扔下了第一个蛋,如果碎了,我们需要 n-1  的楼层数来一层层试。

如果没碎,我们不需要跳过 n 层,我们应该跳过的是 n -1 层(因为我们一层层扔的话,我们这样就可以少扔一层),再下一个就是 n + (n-1)

类比下去,我们在一百层楼中所需要的扔的次数应当是这样的一个数列问题:

n + (n-1) + (n-2) + (n-3) + (n-4) + … + 1  >=  100

这个数列我们应当很熟悉如何去求解,

n (n+1) / 2  >=  100

这个二次方程的解是正13.651,约等于14。

两个蛋的解决方案

现在,我们已经取得了求解这个双蛋问题的所有信息,我们的策略应当是从第14层开始,下一步跳跃13层,然后是12层、11、10….

按照下方的数塔,我们将一直尝试知道最后…

 Drop  Floor
#1 14
#2 27
#3 39
#4 50
#5 60
#6 69
#7 77
#8 84
#9 90
#10 95
#11 99
#12 100

最大的14次是第一个鸡蛋所扔的次数(按步)和第二个鸡蛋(按层)的混合,每一次向上的跳跃,都使得我们减少了最不利情况下的扔蛋次数。

让我们试试3个蛋

3个蛋的情况更复杂,但是我们应该深吸一口气,因为原则依然适用于3个蛋的情况,当第一个蛋碎与不碎的时候,我们处理的是一个蛋的问题,如果碎了,我们处理的是双蛋的问题,而这些我们都已经会解了。

好,我们继续深入。

让我们定义楼层为k 层,我们有e 个蛋,我们所准备扔的楼层为n ,我们首先要做的是递归列举从没一层楼扔下蛋的情况(1~k ),然后计算最坏的情况,我们需要这个结果,

我们从n 层楼扔下蛋,发生的时间只有这么几种:

1)蛋碎了,我们继续尝试减少到 n -1 的数塔,我们有e -1 个蛋

2)蛋没碎,我们需要继续尝试k -n 层,我们手里还有e 个蛋

记住,我们需要最小化最不利情况下的扔蛋次数,所以我们扔的楼层越高,我们所需结果的范围就会越小。

解决这样一个问题的递归函数很好写,但是也同样和其他递归问题一样受困于等价的琐碎带来的对性能的拖累。所以我们在这个过程中要一直注意求解的数值变化,以免不停地重复计算等价值。

蛋蛋蛋蛋蛋…..好多蛋

好了,不要再关注跟长了瘤一样的递归计算,我们来分享一些计算结果。

这个表给出了不同楼层、不同蛋数组合下的各种结果。

Eggs
 Building Height  1  2  3  4  5  6  7  8  9  10
1 floor 1 1 1 1 1 1 1 1 1 1
2 floors 2 2 2 2 2 2 2 2 2 2
3 floors 3 2 2 2 2 2 2 2 2 2
4 floors 4 3 3 3 3 3 3 3 3 3
5 floors 5 3 3 3 3 3 3 3 3 3
6 floors 6 3 3 3 3 3 3 3 3 3
7 floors 7 4 3 3 3 3 3 3 3 3
8 floors 8 4 4 4 4 4 4 4 4 4
9 floors 9 4 4 4 4 4 4 4 4 4
10 floors 10 4 4 4 4 4 4 4 4 4
11 floors 11 5 4 4 4 4 4 4 4 4
12 floors 12 5 4 4 4 4 4 4 4 4
13 floors 13 5 4 4 4 4 4 4 4 4
14 floors 14 5 4 4 4 4 4 4 4 4
15 floors 15 5 5 4 4 4 4 4 4 4
16 floors 16 6 5 5 5 5 5 5 5 5
17 floors 17 6 5 5 5 5 5 5 5 5
18 floors 18 6 5 5 5 5 5 5 5 5
19 floors 19 6 5 5 5 5 5 5 5 5
20 floors 20 6 5 5 5 5 5 5 5 5
21 floors 21 6 5 5 5 5 5 5 5 5
22 floors 22 7 5 5 5 5 5 5 5 5
23 floors 23 7 5 5 5 5 5 5 5 5
24 floors 24 7 5 5 5 5 5 5 5 5
25 floors 25 7 5 5 5 5 5 5 5 5
30 floors 30 8 6 5 5 5 5 5 5 5
35 floors 35 8 6 6 6 6 6 6 6 6
40 floors 40 9 6 6 6 6 6 6 6 6
45 floors 45 9 7 6 6 6 6 6 6 6
50 floors 50 10 7 6 6 6 6 6 6 6
100 floors 100 14 9 8 7 7 7 7 7 7
200 floors 200 20 11 9 8 8 8 8 8 8
300 floors 300 24 13 10 9 9 9 9 9 9
400 floors 400 28 14 11 10 9 9 9 9 9
500 floors 500 32 15 11 10 10 9 9 9 9
1,000 floors 1000 45 19 13 11 11 11 10 10 10

这张数据节点图,y 轴表示最不利情况下的扔蛋次数,x 轴代表扔蛋的楼层数,不同的蛋数结果已经用不同颜色的线标了出来。

下面的就自己看吧(点击图放大)

以后手记:翻译这篇文章的初衷一个是为了分享,另一个为了尝试了解国外的一些专业人员他们的归纳演绎的理性分析的过程,我相信这样是大部分人需要继续补充和完善的地方