两个鸡蛋测试:从100层楼往下扔鸡蛋,求最坏情况下确认保证鸡蛋可以不破的最大楼层所需次数
2018-01-18 14:18
465 查看
最坏情况下求得最优解所需的次数
内容说明
本文是在看过<<妙解谷歌压箱底面试题:如何正确的从楼上抛鸡蛋>>一文以后做的总结,该文章对此问题描写的很详细,但是在拜读的过程中也花了一些时间去理解和消化,所以在读完以后按照自己的理解写了这篇文章,用以梳理思路并验证。所有思路均非原创问题描述
问题简述:有两个完全相同的鸡蛋,从100层高的楼往下扔,要求找出最坏情况下在保证鸡蛋不破的前提下找到最高的楼层且计算扔鸡蛋的次数最少问题分析
首先,在我的理解下这是两个个最优解问题,第一,在保证楼层最高的情况下计算次数最少;第二,在保证鸡蛋不破的前提下楼层最高第二个问题很容易解,很直观的思路,从一层开始一层一层往上,最终能够得到一个最高楼层;但是这对于第一个问题就变得很不友好,在最坏情况下我需要扔100次才能找到最终楼层
不过第一个问题的思路也很直观,分区,要想扔的次数少肯定不能一楼一楼的往上,那就要跳层,好在两个鸡蛋,如果跳层成功则继续跳,失败了就在这个区间内一层一层往上
于是这个问题就变成了怎么跳层即分区的问题,一般常用的分区是等分法,但显然这个问题并不适用,在最坏的情况下,分区越靠后扔鸡蛋的次数就越多,不符合这一个问题的要求
于是想到另一种常见的分区方式:每次跳转剩余楼层的1/n楼,如果最高楼层在前面的区间,多层跳转(从m层跳转到m+n层,n大于1)的次数少,但因为区间大,所需的单层跳转(从m层跳转到m+1层)次数也多;如果最高楼层在后面的区间,跳转的次数多了,但是单层跳转次数却变少了。这样无论最高楼层在哪里,都能有一个相对比较平衡的跳转次数,这种分区方式很好的较少了最坏情况下的平均跳转次数,但是并不能保证任何情况下跳转次数都是最少的
那么如果保证无论最后的楼层在哪一个区间跳转次数最少呢,就必须保证每一个区间的跳转次数都是相等的。(n个数的和是固定的,想要n个数都最小,只能让n个数相等)
既然最简单的等分不适合了,那么我们就需要去计算一个可靠的分区方式。先做一些假设方便计算:
相同的楼层对任意鸡蛋都是同样的效果
鸡蛋破碎就不能继续使用
鸡蛋完好则可以继续使用
鸡蛋在第n层没有破,那么在小于n层的楼层也不会破
鸡蛋在第n层破了,那么在大于n层的楼层也会破
n为大于0的正整数
这个问题里面包含几个变量:f(n)表示在最坏时需要扔鸡蛋的次数最少的情况下第n次扔鸡蛋的楼层,W(n)表示第n层是鸡蛋不破的最高层,那么最少需要的扔鸡蛋的次数
显然f(0)=0(第0次还没有开始扔),f(n)=100-1=99(最后一次扔鸡蛋一定是在100楼,所以上一次一定是在100层的前一层即99层)。f(n)将100层分成了n+1个区间
{[1,f(1)),[f(1),f(2)),…,[f(n),100,…)}
假设鸡蛋不破的最高楼层在第1个区间,那么W(1)=f(1),此时的场景是:第一个鸡蛋从f(1)层往下扔,破了,第二个鸡蛋从第1层开始一层一层往上,最坏的情况需要扔f(1)-1次,所以共需扔f(1)次
依次类推,假设鸡蛋不破的楼层在第j个区间,此时W(j)=j+f(j)-f(j-1)-1,假设f(0)=0,那么
W(n)=f(n)-f(n-1)-1+n,n>0
理论上我们希望W(n)是一个常量数列,那么
因为W(j+1) = W(j)
所以f(j+1) + f(j-1) + 1 = 2f(j)
g(n)=f(n)-f(n-1)-1是一个等差数列,此时我们知道已经没有办法更加简化了,需要的是进一步的计算
代码
根据上述分析,可以得到非常简单的实现://这里的iStartFloor和iEndFloor分别为1和100 void calFloors(int iStartFloor,int iEndFloor){ cout<<"calFloors starts, the result is:"<<endl; int* a= new int[iEndFloor - iStartFloor +1]; //不妨假设最高楼层在最后一个区间 //只剩下一个鸡蛋,最后一步一定是最后一层 //上一步一定是最后一层的前一层 a[0] = iEndFloor; a[1] = iEndFloor - 1; int i=2; while (2*a[i-1]-a[i-2]-1 >= iStartFloor){ a[i] = 2*a[i-1] - a[i-2] - 1; i++; } for (int j = 0; j < i; j++){ cout<<a[j]<<","; } cout<<endl; delete[] a; }
输出的结果如下:
根据传统的遍历法计算得到的结果与上述一直,遍历的代码如下(该遍历方式未经过任何优化,计算速度相当堪忧,100楼跑程序跑的我都吐了,网上有很多优化遍历的算法,有兴趣可以自行搜索,在100楼的情况下相对原生遍历计算速度大大提高,这里就不给出具体实现了):
//投掷次数的最优解:假设每一个下一层决策都是最优的,那么整体结果就是最优的 int bestMaxThrows(int iFloorsLeft){ return maxThrows(iFloorsLeft, bestNextStep(iFloorsLeft)); } //下一层的最优解 int bestNextStep(int iFloorsLeft){ if (iFloorsLeft <= 2) return 1; else{ int minThrows = iFloorsLeft; for(int i = 1; i < iFloorsLeft; i++){ int tmpThrows = maxThrows(iFloorsLeft,i); if (minThrows > tmpThrows) minThrows = tmpThrows; } return minThrows; } } //计算投掷次数 int maxThrows(int iFloorsLeft, int iNextFloor){ if (iFloorsLeft <= 2) return iFloorsLeft; else return max(iNextFloor,bestMaxThrows(iFloorsLeft - iNextFloor) + 1); } //输出每次投掷的楼层 void printBestFloors(int iFloors){ cout<<"遍历计算的结果是:"; int floor = 0; while(floor < iFloors){ int iFloorLeft = iFloors - floor; int nextStep = bestNextStep(iFloorLeft); floor += nextStep; cout<<floor<<","; } cout <<endl; }
问题扩展
到目前为止,该问题对于两个鸡蛋在任何有限的高楼里都能获得理想的解决方案,需要考虑的是如果鸡蛋数目是n个,该方案是否有效。事实上,如果把每一个分区都当做一个待划分的区域,那么鸡蛋的个数越多也就是在某一个分区内自己做的分区越多。知道只剩下最后一个鸡蛋,就按照一层一层向上,即分区到达最小粒度。
举个栗子,如果我有3个鸡蛋,还是划分100层,那么按照上面两个鸡蛋时的划分方法,先跳层(后面叫做外层分区),在确定分区以后用掉了一个鸡蛋,然后在这个分区里再做上述划分的过程,用完剩下两个鸡蛋确认最后的楼层(内层分区),初步设想最终最坏情况下的最少次数应该是少于2个鸡蛋的。
那么这种方式是否可以用于2个以上的鸡蛋呢,这种方式的计算基础就是基于只有两个鸡蛋,现在不止两个鸡蛋了,这种方式是否依然能够找到正确的结果呢。
从直觉来看,应该是适用的。首先内层分区上面已经验证过是有效地,然后在最坏的情况下,也需要外层分区按这个逻辑走。验证待补充
相关文章推荐
- 有一栋楼共100层,一个鸡蛋从第N层及以上的楼层落下来会摔破, 在第N层以下的楼层落下不会摔破。给你2个鸡蛋,设计方案找出N,并且保证在最坏情况下, 最小化鸡蛋下落的次数。
- [百度面试题]100层楼,球可能会在某一层楼摔坏,问用2个球,最坏情况下几次测试可以找出该楼层
- [百度面试题]100层楼,球可能会在某一层楼摔坏,问用2个球,最坏情况下几次测试可以找出该楼层
- 动态规划--100层楼2只鸡蛋最少次可以测试最高楼层不摔破
- 100层楼有一个鸡蛋,如果确定刚好摔碎的那个楼层,最坏情况下最少需要摔多少次?
- 100层楼有一个鸡蛋,如果确定刚好摔碎的那个楼层,最坏情况下最少需要摔多少次?
- 200层高的大楼,两个鸡蛋,如果在N扔下不碎的话,那么N-1之下都不碎。在最坏的情况下怎么用两个鸡蛋试出会碎掉的楼层
- 两个鸡蛋,100层楼,找出摔碎鸡蛋的最低楼层,所用的摔鸡蛋次数最少
- 最坏情况下,合并两个大小为n的已排序数组所需要的比较次数为
- 同时寻找最大数和最小数的最优算法以及寻找最大的两个数所需的最少比较次数
- 100层楼2个鸡蛋,测试其最低破碎楼层问题
- 标题:测试次数(100层楼扔鸡蛋)
- 同时寻找最大数和最小数的最优算法以及寻找最大的两个数所需的最少比较次数
- 最坏情况下,合并两个大小为n的已排序数组所需要的比较次数
- 找出具有n个元素的集合中最大的两个元素,要求比较次数尽可能少(三种算法的思考)
- 让win2003突破两个人同时远程登录的限制,由于2003默认情况下远程连接只能允许两个用户同时登录,超过两人同时登录就会提示:终端服务器超出最大连接数。。
- 100层楼扔两个鸡蛋的问题
- 关于100层楼,扔两个鸡蛋,求摔碎鸡蛋的临界层的问题
- 如何在需求不明确的情况下保证测试质量
- 最坏情况下保证时间复杂度为N*logN的快速排序