假如有K个鸡蛋,一栋N层的楼,为了测试鸡蛋最高在哪一层扔下刚好不碎,并且扔鸡蛋的次数最少。假设鸡蛋碎了就不能用了,没碎的话可以继续使用。
这也是google曾经的面试题,解决方法有很多,
1、枚举法(最保守的扔法,最多碎一个鸡蛋)
只能从第一层逐层往上扔, 但是这样最坏的情况需要扔N次才能知道答案。方法优点是碎的鸡蛋少,缺点是测试的次数多。
2、二分法(高效的扔法,比较耗鸡蛋)
先从楼层中间开始扔,如果碎了就往更低的楼层扔,没碎就往更高的楼层扔。方法优点是测试次数少,缺点是二分后扔鸡蛋的楼层相比枚举法,鸡蛋碎的风险更高,所以在鸡蛋足够多的情况下这样的扔法可以很快的找到答案。
3、全局平衡公式算法(个人理解的)
从开始往结局做一次全局平衡,使得开始到结局的每一次结果对于最终的答案影响都尽量是一样的,感觉和动态规范方向相反,特殊情况下和动态规划效果是一样的。
4、动态规划
百科中说到动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
上面都是复制粘贴的,下面是正文,用纯数学的方式求解。
用
f
(
k
,
n
)
f(k,n)
f(k,n)表示用
k
k
k个鸡蛋,丢
n
n
n次,最多能测试出多少层。容易看出,
f
(
1
,
n
)
=
n
f(1,n)=n
f(1,n)=n
如果有两个鸡蛋,假设第一个鸡蛋在第
m
m
m层楼丢下,有两种结果,
- 鸡蛋碎了,那么剩下一个鸡蛋,还需要测试的次数 n 1 n_1 n1满足 f ( 1 , n 1 ) = m − 1 f(1,n_1)=m-1 f(1,n1)=m−1
- 鸡蛋没碎,剩下两个鸡蛋,还需要测试的次数
n
2
n_2
n2满足
f
(
2
,
n
2
)
=
h
−
m
f(2,n_2 )=h-m
f(2,n2)=h−m,
h
h
h代表最多能测试的楼层高度。为了在固定最坏情况测试次数,必有
n
1
=
n
2
=
n
−
1
n_1=n_2=n-1
n1=n2=n−1,因此,
h = f ( 2 , n ) = f ( 1 , n − 1 ) + f ( 2 , n − 1 ) + 1 h=f(2,n)=f(1,n-1)+f(2,n-1)+1 h=f(2,n)=f(1,n−1)+f(2,n−1)+1
同理,
f ( k , n ) = f ( k − 1 , n − 1 ) + f ( k , n − 1 ) + 1 f(k,n)=f(k-1,n-1)+f(k,n-1)+1 f(k,n)=f(k−1,n−1)+f(k,n−1)+1
由于 f ( k , n − 1 ) = f ( k − 1 , n − 2 ) + f ( k , n − 2 ) + 1 f(k,n-1)=f(k-1,n-2)+f(k,n-2)+1 f(k,n−1)=f(k−1,n−2)+f(k,n−2)+1,将上式展开,得到,
f ( k , n ) = 1 + ∑ i = 1 n − 1 f ( k − 1 , i ) f(k,n)=1+\sum_{i=1}^{n-1}{f(k-1,i)} f(k,n)=1+i=1∑n−1f(k−1,i)
边界条件为
{ f ( 1 , n ) = n f ( k , 1 ) = 1 \left\{\begin{matrix} f(1,n)=n\\ f(k,1)=1 \end{matrix}\right. {f(1,n)=nf(k,1)=1
可以得到
f ( 2 , n ) = n + ∑ i = 1 n − 1 f ( 1 , i ) = n ( n + 1 ) 2 f\left(2,n\right)=n+\sum_{i=1}^{n-1}f\left(1,i\right)=\frac{n\left(n+1\right)}{2} f(2,n)=n+i=1∑n−1f(1,i)=2n(n+1)
f ( k , n ) = n + ∑ i = 1 n − 1 f ( k − 1 , i ) f\left(k,n\right)=n+\sum_{i=1}^{n-1}f\left(k-1,i\right) f(k,n)=n+i=1∑n−1f(k−1,i)
容易证明, f ( k , n ) f(k,n) f(k,n)是关于 n n n的 k k k次多项式,设
f ( k , n ) = c 0 + c 1 n + c 2 n 2 + … + c k n k f\left(k,n\right)=c_0+c_1n+c_2n^2+\ldots+c_kn^k f(k,n)=c0+c1n+c2n2+…+cknk
由于 k k k次多项式可以由 k k k个采样点确定,为了方便计算,可设 f ( k , 0 ) = 0 f\left(k,0\right)=0 f(k,0)=0,从而 c 0 = 0 c_0=0 c0=0, f ( k , n ) f(k,n) f(k,n)可由剩下的 k k k个点确定。根据递推式,
f ( 1 , 1 ) = 1 f(1,1)=1\\ f(1,1)=1
{ f ( 2 , 1 ) = 1 f ( 2 , 2 ) = f ( 1 , 1 ) + f ( 2 , 1 ) + 1 = 3 \left\{ \begin{matrix} f(2,1)=1 \\ f(2,2)=f(1,1)+f(2,1)+1=3 \end{matrix} \right. {f(2,1)=1f(2,2)=f(1,1)+f(2,1)+1=3
{ f ( 3 , 1 ) = 1 f ( 3 , 2 ) = f ( 2 , 1 ) + f ( 3 , 1 ) + 1 = 3 f ( 3 , 3 ) = f ( 2 , 2 ) + f ( 3 , 2 ) + 1 = 7 \left\{ \begin{matrix} f(3,1)=1\\ f(3,2)=f(2,1)+f(3,1)+1=3\\ f(3,3)=f(2,2)+f(3,2)+1=7 \end{matrix} \right. ⎩ ⎨ ⎧f(3,1)=1f(3,2)=f(2,1)+f(3,1)+1=3f(3,3)=f(2,2)+f(3,2)+1=7
更一般的,对于 1 ≤ n ≤ k 1\le n\le k 1≤n≤k,有
f ( k , n ) = 2 n − 1 f\left(k,n\right)=2^n-1 f(k,n)=2n−1
已知 k + 1 k+1 k+1个点,求 k k k次多项式,将定义域扩展到实数域,利用拉格朗日插值多项式,令
l j ( x ) = ∏ i = 0 , i ≠ j k ( x − i ) ∏ i = 0 , i ≠ j k ( j − i ) l_j\left(x\right)=\frac{\prod_{i=0,i\neq j}^{k}{(x-i)}}{\prod_{i=0,i\neq j}^{k}{(j-i)}} lj(x)=∏i=0,i=jk(j−i)∏i=0,i=jk(x−i)
则
f ( k , n ) = ∑ j = 0 k ( 2 j − 1 ) l j ( x ) f\left(k,n\right)=\sum_{j=0}^{k}{(2^j-1)l_j(x)} f(k,n)=j=0∑k(2j−1)lj(x)
对于给定的 k k k和 n n n,若 n ≤ k n\le k n≤k,则 f ( k , n ) = 2 n − 1 f\left(k,n\right)=2^n-1 f(k,n)=2n−1,若 n > k n>k n>k,则 l j ( n ) l_j(n) lj(n)的分子部分仅有一项缺失,分母转化为两个阶乘因子,
l j ( n ) = ∏ i = 0 k ( n − i ) ( n − j ) ( − 1 ) i − 1 ( i − 1 ) ! ( k − i ) ! l_j\left(n\right)=\frac{\prod_{i=0}^{k}\left(n-i\right)}{\left(n-j\right)\ \left(-1\right)^{i-1}\left(i-1\right)!\left(k-i\right)!} lj(n)=(n−j) (−1)i−1(i−1)!(k−i)!∏i=0k(n−i)
对于不同的下标 j j j,分子相同,分母通过增量计算,可以在 O ( k ) O(k) O(k)时间内计算完成。
现在处理最后一步,原题是给定 k k k个鸡蛋和 h h h层楼,而上面计算的是丢 n n n次最多能测试多少层楼,因此要求的是满足 f ( k , n ) ≥ h f(k,n)\ge h f(k,n)≥h的最小的 n n n。若 h < 2 k h<2^k h<2k,则 n = ⌈ log 2 ( h + 1 ) ⌉ n=\left\lceil\log_2{(h+1)}\right\rceil n=⌈log2(h+1)⌉,否则,由于 f ( k , n ) f(k,n) f(k,n)在 [ n , + ∞ ) [n,+\infty) [n,+∞)是递增的,可使用牛顿迭代法快速得到 x h x_h xh,满足,
f ( k , x h ) = h f\left(k,x_h\right)=h f(k,xh)=h
从而求得 n ∗ = ⌈ x h ⌉ n^\ast=\left\lceil x_h\right\rceil n∗=⌈xh⌉。