[国外原文翻译]鸡蛋掉落问题的一般性解法

原文地址

鸡蛋掉落问题详解

     鸡蛋掉落问题指的是在允许有限次出现错误状态的情况下,寻找当前问题解决方法。一个游戏类型的版本为:在一个高为n层的塔上,你有m个完全相同的理想鸡蛋可以扔下去,这些鸡蛋都具有一个属性就是,存在一个楼层高度f,当你从低于f的楼层扔下去时,鸡蛋不会破碎;当从高于或等于f的楼层扔下时,鸡蛋会碎掉,碎掉的鸡蛋当然不能再用。你要做的是:无论f为多少(0<=f<=n),为了明确的确定出其值至少需要做多少次尝试?(注意,这里明确的确定指的是,无论f的值是多少,在你给的尝试次数限定下,都可以找到至少一种方案确定出它的值。如果少做一次尝试,就无法对某些f的值无法验证)这个问题有很多现实应用版本,例如,规避访问那些慢速的硬盘、将缓存未命中次数降到最低或者在一个数据库上做多次昂贵的查询。

目录

  1. 2个鸡蛋,100层楼; (m=2,n=100)
  2. 2个鸡蛋,k层楼; (m=2,n=k)
  3. N个鸡蛋,k层楼; (m=N,n=k)
  4. 递归解法
  5. 动态规划解法(DP 算法)
  6. 二项式表达

一、2个鸡蛋,100层楼
你有2个鸡蛋,要在100层的楼上确定出f:

  • 如果鸡蛋在某个楼层(x)没有碎,则对任何低于x的楼层都不会碎;
  • 如果鸡蛋在某个楼层(x)碎了,则对于任何高于x的楼层也都会碎;
  • 鸡蛋可能在第一层楼就碎了;
  • 鸡蛋可能在最顶层楼也不会碎。

一开始你可能会想到使用二分法解决这个问题(我就是),但那绝不是最好的策略。不妨想一想,你有两个鸡蛋,碎了一个还有一个,即使没碎也意味着你已经进行了一次尝试,最开始的一次尝试应该选在哪个楼层呢?
如果使用二分法,你最开始会尝试第50层楼,如果没碎,再尝试75层楼,如果鸡蛋一直没碎,你做7次才能确定。如果鸡蛋在50层(或者随便哪一层)碎了,你必须使用唯一剩下的鸡蛋,一层一层的往下尝试,完全有可能是49次,因此这种策略复杂度为O(n);
我们接下来将证明,第一次尝试的最佳楼层为14,无论f的真实值为多少,第一次尝试都应该选在14层。
方案为:如果在14层碎了,你接下来的路线为:1-2-3-…13,共需要14步;
如果在14层没有碎,你需要尝试27层,我们可以将方案用图表展示:
在这里插入图片描述
这表明:无论f为多少,我们最多尝试14次就可以确定出其位置。

二、2个鸡蛋,k层楼
我们利用两个鸡蛋在k层楼进行尝试,一个很好的思考方向是:我们在x的尝试次数下,能否覆盖所有楼层?(这里的覆盖的意思是,如果f在这个楼的任意一层,那我们最多尝试x次,一定能确定出其位置,例如在上一种情况,如果我们有14次尝试机会,我们肯定可以覆盖100层的楼)。
我们假定现在有一个最好的方案,在这个方案中,最大尝试次数(也就是是f在最难找的层数,如上例的13、26、38等层数)是x (上例中的14),那么我们需要从x层做第一次尝试:
如果第一次碎了:你需要从1、2、3、x-2、x-1层依次尝试(上例的第一种情况),总次数为:((x-1)-1+1)+1= x次;总覆盖为:1— x = x层。
如果第一次没碎:你需要在第(x+(x-1))层做第二次尝试;
如果第二次碎了: 你需要从第x+1、x+2、…x+(x-1)-2、x+(x-1)-1层依次尝试(上例的第二种情况),总次数为((x+(x-1)-1)-(x+1)+1)+2=x次;总覆盖为:x+1 — x+(x-1)-1 = x-1 次;
如果第二次没碎: 你需要从第(x+(x-1))+(x-2)=3x-3层做第三次尝试;
如果第三次碎了: 同理;以此类推,直到第x次尝试。
意识到我们正在做什么了吗?我们现在不是在针对具体楼层寻找最小的尝试次数,而是现在将问题转化为:如果我们可以尝试x次,那么我们最高可以到哪一层?
我们可以这样总结:在2个鸡蛋的情况下,我们在x的尝试范围内,可以覆盖到:

在这里插入图片描述

三、N个鸡蛋,k层楼
通过上面的情况我们知道:必须找到一种关于鸡蛋数和楼层数的通用的算法;
* 递归版本:这种最好理解,也最好实施,但是最慢的一种,不建议使用。
* 动态规划版本:与递归类似,但是更快,适用于中小型问题。
* 二分——递归结合:最快,如果明白策略也很好实施。

四、 递归解法
假设你有n个鸡蛋和h个待测试的楼层,现在你从第i层扔下,有两种结果:
* 碎了:问题变成了:你有n-1个鸡蛋,i-1个待测楼层(1、2、…i-1)。
* 没碎: 问题变成了:你有n个鸡蛋,h-i个待测楼层(i+1,i+2,…h).
你应该意识到:重要的不是我们总共要探测的楼层数,而是那些仍有嫌疑的楼层数。例如,我们去检查(1-20)层和(21-40)层是没有区别的,我们一共检查了二十层。
现在我们可以用函数W(n,h) 表示最优次数:
在这里插入图片描述
边界条件为:
1个鸡蛋,h层楼: h次;
n个鸡蛋,1层楼: 1次;
n个鸡蛋,0层楼: 0次。

C++实现

#include <iostream>
#include <limits.h>

using namespace std;

//Compares 2 values and returns the bigger one
int max(int a,int b) {
    int ans=(a>b)?a:b;
    return ans;
}

//Compares 2 values and returns the smaller one
int min(int a,int b){
    int ans=(a<b)?a:b;
    return ans;
}

int egg(int n,int h){

    //Basis case
    if(n==1) return h;
    if(h==0) return 0;
    if(h==1) return 1;

    int minimum=INT_MAX;

    //Recursion to find egg(n,k). The loop iterates i: 1,2,3,...h
    for(int x=1;x<=h;x++) minimum=min(minimum,(1+max(egg(n,h-x),egg(n-1,x-1))));

    return minimum;
}

int main()
{
    int e;//Number of eggs
    int f;//Number of floors

    cout<<"Egg dropping puzzle\n\nNumber of eggs:";

    cin>>e;

    cout<<"\nNumber of floors:";

    cin>>f;

    cout<<"\nNumber of drops in the worst case:"<<egg(e,f);

    return 0;
}

五,动态规划

C++实现

int solvepuzzle(int n,int k){

    int numdrops[n+1][k+1];
    int i,j,x;

    for(i=0;i<=k;i++) numdrops[0][i]=0;
    for(i=0;i<=k;i++) numdrops[1][i]=i;
    for(j=0;j<=n;j++) numdrops[j][0]=0;

    //This loop fills up the matrix
    for(i=2;i<=n;i++){
        for(j=1;j<=k;j++){

            //Defines the minimum as the highest possible value
            int minimum=INT_MAX;

            //Evaluates 1+min{max(numeggs[i][j-x],numeggs[i-1][x-1])), for x:1,2,3...j-1,j}
            for(x=1;x<=j;x++) minimum=min(minimum,(1+max(numdrops[i][j-x],numdrops[i-1][x-1])));

            //Defines the minimum value for numeggs[i][j]
            numdrops[i][j]=minimum;
        }

    }

    cout<<"\nArray:\n\n";

    //Prints numeggs
    for(i=0;i<=n;i++){
        for(j=0;j<=k;j++){
            cout<<numdrops[i][j]<<" ";
        }
        cout<<"\n";
    }

    cout<<"\nNumber of trials in the worst case using the best strategy:\n";

    return numdrops[n][k];
}

六、二项—递归解法
在开始之前,先看一点数学知识:
二项分布:
在这里插入图片描述
杨辉三角
在这里插入图片描述
将杨辉三角写成如下形式:
在这里插入图片描述
可以找到如下递归式:

在这里插入图片描述

现在,我们开始下一种解法:
第一步:我们可以将问题重新表述:如果我们有d次尝试机会,n个鸡蛋,我们最大可以覆盖的层数为f(d,n):
有:

方程1(1)
如果我们能找到这样一个函数f(d,n),问题就迎刃而解了。下面将介绍如何寻找到这个函数:
假设一个辅函数:

在这里插入图片描述 (2)
代入方程1可得:
在这里插入图片描述
可以看到,这个辅函数的迭代式和前面杨辉三角递推式完全相同。因此可以将g(d,n)写成如下形式:在这里插入图片描述

但是这里需要做一点修正,因为对任意n,f(0,n)=0;所以g(0,n)也应该为0,但是
在这里插入图片描述
我们可以做如下修正:
在这里插入图片描述
这样对迭代式依然满足。
接下来有:

在这里插入图片描述
因为f(d,0)=0;结合(2)式:
在这里插入图片描述
这样,求解尝试次数就变成了:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值