只给你二个鸡蛋,你能上100层楼,你想知道鸡蛋的硬度。鸡蛋可能很硬或很脆弱,如果鸡蛋从第m层掉下而没破裂,而从第m+1层掉下就破裂了,那么这个鸡蛋的硬度就是m。你需要找出这个m和在最坏情况下最少试验次数。(经典鸡蛋问题)
A: 计算机学生可能会首先用第一个鸡蛋做二分搜索(O(logN))再用第二个递增做线性搜索(O(N)),最后必将用线性搜索结束因为用第二个鸡蛋时你无法确定最高一层。因此,问题变为如何使用第一个鸡蛋来减少线性搜索。
于是如果第一个蛋破裂在最高点我们要扔x-1次并且我们必须从x层高扔第一个蛋。现在如果第一个蛋的第一次扔没有破裂,如果第一个蛋在第二次扔破了我们要扔x-2次第二个蛋。假如16是答案,我需要扔16次才能找到答案。来验证一下是否可以从16层开始扔,首先从16层扔如果它破裂了,我们尝试所有其下的楼层从1到15;如果没破我们还能扔15次,于是我们将从32层(16+15+1)再扔。原因是如果它在32层破裂我们能尝试其下所有楼层从17到31最坏扔第二个蛋14次(总共能扔16次了)。如果32层并没破,我们还剩下能扔13次,依此类推得:
1 + 15 16 如果它在16层破裂,从1到15层最坏扔15次第二个蛋
1 + 14 31 如果它在31层破裂,从17到30层最坏扔14次第二个蛋
1 + 13 45.....
1 + 12 58
1 + 11 70
1 + 10 81
1 + 9 91
1 + 8 100 在最后我们能轻易地做到因为我们有足够多扔的次数来完成任务
从上表我们能看到最佳的一个在最后一步将需要0次线性搜索。
能把上述规律写为: (1+p) + (1+(p-1))+ (1+(p-2)) + .........+ (1+0) >= 100.
令1+p=q上述式子变为q(q+1)/2>=100,对100解答得到q=14。
扔第一个蛋从层14,27,39,50,60,69,77,84,90,95,99,100直到它破裂,再开始扔第二个蛋。最坏情况只需14次。
------------------------------------------------------
在只有一个鸡蛋时,保险起见,我们只能从一楼开始,一层一层地试验,看看鸡蛋有没有被摔烂。这样最精确,但是消耗的时间也最久。如果我们事先就知道这个鸡蛋不被摔碎的最高落下点在30层到75层之间,我们最多也只要尝试45次就能知道结果。现在我们手上有两个鸡蛋,根据上面的分析,一个合理的策略就是用第一个鸡蛋确定出一个较小的楼层范围,然后在这个范围里用第二个鸡蛋从下往上逐层尝试。
比如说让第一个鸡蛋每隔5层试验一次。当它在某一层被摔烂时,也就意味着确定了一个4层的待测试宽度(为什么是4层呢?假如鸡蛋在5楼的时候没破,10楼的时候破了,那么我们就只需要知道鸡蛋在 6 , 7 , 8 , 9 层的结果)。这时候,用第二颗鸡蛋一层一层地尝试,就能用较少的次数找出鸡蛋刚好摔不烂的高度。
需要注意的是,如果想留给第二颗鸡蛋较小的测试宽度,就要缩短第一个鸡蛋的测试跨度。相应的,也就增加了尝试次数。为了确定合适的跨度,使得总试验次数之和尽可能小,我们可以采取如下的办法。
设跨度是L,第一颗鸡蛋的尝试次数就是[ 100/L ],第二颗鸡蛋的尝试次数就是 L - 1,因此尝试次数总和就是 [ 100/L ] + L - 1 。根据这个公式,我们可以列出下面这个表格:
可以看出,我们只需要选 8 - 13 之间的一个宽度,都能使得总尝试次数是19次。
但问题是,这已经是最优策略了吗,有没有更好的方法呢?
有的。上面的方法固定了第一颗鸡蛋的测试跨度,如果我们灵活变动,就能使得总尝试次数变得更少。首先,我们选择从14楼丢下第一颗鸡蛋。如果它破碎了,我们就从1楼开始,逐层丢第二颗鸡蛋,最多试14次便能得到答案。如果它没有破碎,那我们往上走 13 层,在 27 楼第二次丢下第一颗鸡蛋。此时如果鸡蛋碎了,那我们只需要在 15 层到 26 层之间用第二颗鸡蛋进行最多12次试验即可,加上第一颗鸡蛋的两次尝试,仍然是14次。类似的,依次减小测试跨度,如果鸡蛋足够顽强,那我们丢下第一颗鸡蛋的楼层就分别是 14 , 27 , 39 , 50 , 60 , 69 , 77 ,84 , 90 , 95 , 99 以及最后的100层。因为第一颗鸡蛋每多尝试一次,第二颗鸡蛋需要尝试的最大次数就减少一次,因此,总尝试次数的最大可能值一直是不变的,保持在14次。用这种方法,我们只需要不超过14次的尝试就能够找出答案。有没有更优的策略了?感兴趣的读者可以自行思考。