双蛋问题

来自testwiki
1.200.96.105留言2021年11月16日 (二) 18:04的版本 n个鸡蛋,k层楼:​ 修正筆誤)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索

双蛋问题Template:Lang)是一个经典的算法问题,它经常被描述为“给你两个相同的异常坚硬的鸡蛋,通过在一栋100层的楼的不同层扔下鸡蛋进行实验,试验出可以摔碎该鸡蛋的最高楼层(临界楼层)。已知未碎的鸡蛋可以重复使用。求最少的实验次数n,使得在n次实验后,一定能判断出该临界楼层。”

该问题可以扩展为这样一类问题: 在N个鸡蛋,k层楼的条件下,找到一个最小的m,使得存在一种方案,在m次实验以后,一定能找到鸡蛋的临界楼层。

问题假设

为了更加精确地思考问题,该问题中必须满足以下的条件:

  1. 如果鸡蛋在某一层没有碎,它不会在任何更低层的破碎。
  2. 如果鸡蛋在某一层碎了,它在更高层上一定会碎。
  3. 鸡蛋可能在一楼摔破,也可能在最高层还不摔破。

解决方案

此时,你有2个鸡蛋,楼高100层。我们可以先思考有1个鸡蛋和有无数个鸡蛋的情况[1]

1个鸡蛋

此时,由于只有一个鸡蛋,所以一旦破碎,那么就无法继续进行试验,我们只能从第1层开始,一层一层地实验。在这种情况下最多需要99次实验。

无数个鸡蛋

容易的解决方案是二分法,log21006.644<7 ,所以如果我们有无数个鸡蛋,我们最多只需要7次就可以试验出。比如,先在64楼扔一次鸡蛋,如果碎了,那就到32层扔第二次,如果第二次扔鸡蛋又碎了,再到16层去扔第三次,如果这次没有碎,那你可以再到第24层去扔第四次,又没碎,那就去28层扔第五次,还是没有碎,再到30层扔第六次,这次又碎了,再到29层扔第七次,第七次碎了,那么临界楼层就是第28层,第七次没碎,临界楼层就是第29层。所以无数个鸡蛋最多只需要7次就可以实验。

两个鸡蛋

借助于二分法提供的分组思想,我们可以尝试将100平均分成10组,用第一个鸡蛋在每组最后一层进行实验,这样可以实验出临界楼层在哪一组。然后再用第二个鸡蛋从该组第一层依次实验。这种方案的最坏情况是19次。

我们发现,如果19层是临界楼层,只需要实验11次,而如果临界层是99层,就需要实验19次。因此我们是否可以将19次平均到11次-{zh:里;zh-hans:里;zh-hant:裡;}-一部分?为此,有以下方案,第一组x人,第二组(x1)人,第三组(x2)人,……第x组1人,考虑到x+(x1)+(x2)++3+2+1=x(x+1)2>100,解得x>13.65,所以x=14时,最多需要14次便可以找出临界楼层。

推广问题

下面是双蛋问题的几个推广问题[2][3]

2个鸡蛋,k层楼

类似于之前的方法,只需要x+(x1)+(x2)++3+2+1=x(x+1)2>k即可,可以求出k=1+1+8k2(参见取整函数高斯符号

n个鸡蛋,k层楼

让我们定义一个函数f(d,n),表示有n个鸡蛋,通过d次实验就一定能够判断出临界楼层的最大楼层。 如果鸡蛋打破,我们将能够将临界楼层范围缩小到f(d−1,n−1)层;否则我们将能够把范围缩小到 f(d-1,n)层。

因此,f(d,n)=1+f(d1,n1)+f(d1,n)。这只是一个递归关系,而我们必须找到一个函数 f(d,n)。

因此,我们将定义一个辅助函数g(d,n):g(d,n)=f(d,n+1)f(d,n)

根据我们的第一个方程

g(d,n)=f(d,n+1)f(d,n)=f(d1,n+1)+f(d1,n)+1f(d1,n)f(d1,n1)1=[f(d1,n+1)f(d1,n)]+[f(d1,n)f(d1,n1)]=g(d1,n)+g(d1,n1) 函数g(d,n)可以写成g(d,n)=(dn)(参见二项式系数

但是我们有一个问题:根据之前的关系fg,对于任何nf(0,n)以及g(0,n)都是0。然而,在n=0时会发生矛盾,因为g(0,0)=(00)=1,但对于每一个ng(0,n)应该是0!

我们可以通过重定义g(d,n)修复这个问题如下:

g(d,n)=(dn+1)

递归是仍然有效。

现在,展开f(d,n),我们可以把它写成

f(d,n)=[f(d,n)f(d,n1)]+[f(d,n1)f(d,n2)]++[f(d,1)f(d,0)]+f(d,0).

我们知道f(d,0)=0,因此

f(d,n)=g(d,n1)+g(d,n2)++g(d,0)

我们也知道

g(d,n)=(dn+1)


因此,

g(d,n1)+g(d,n2)++g(d,0)=(dn)+(dn1)++(d1)

最后,

f(d,n)=i=1n(di)

我们知道f(d,n)是所有最少实验次数为d的总楼层数中最大的一个,我们只要找到一个k满足以下条件即可:

f(d,n)k

使用我们最后的公式,

i=1n(di)k

让我们来试验一下:

f(t,3)=i=13(ti)=t(t2+5)6

因此有f(8,3)=92,f(9,3)=129

所以我们如果有三个鸡蛋,可以保证在9次实验之内找到临界楼层。

其他方法

除了解析法之外,最常见的方法是递归法。

想象以下情况:n个鸡蛋,k层楼,然后你把鸡蛋在连续的h层中的第i层进行试验。

如果鸡蛋打破:这个问题会减少为n-1鸡蛋和 i-1个剩余楼层的问题;如果不打破:这个问题会减少为n鸡蛋和h-i个剩余楼层的问题。 现在我们可以定义一个函数W(n,h)计算所需的最小实验次数:

我们可以编写上述结果为确定找到下面的递归W(n,h):

以下代码由C++编写

W(n,h)=1+min(max(W(n1,i1),W(n,hi)))

#include <iostream>
#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;
}
}

参见


參考資料

Template:Reflist

外部連結

  1. 双蛋问题的另一个递归程序Template:Wayback
  2. 李永乐老师Template:Wayback
  3. 相关动态规划问题Template:Wayback