三个蛋问题

时间:2012-07-22 19:10:18

标签: algorithm math

我刚刚阅读The Two Egg Problem

  

两个鸡蛋问题

     

你有两个鸡蛋,可以进入100层高的建筑物。两个鸡蛋都是一样的。目的是找出最高的楼层,当从该楼层的窗户掉出一个鸡蛋时,鸡蛋不会破裂。如果一个鸡蛋掉落但没有破裂,它就没有损坏,可以再次掉落。然而,一旦鸡蛋被打破,那就是那个鸡蛋。

     

如果鸡蛋从地板n掉落时断裂,那么它也会从上面的任何地板上断裂。如果一个鸡蛋在摔倒后幸存下来,那么它会在任何比这更短的摔倒时存活下来。

     

问题是:您应该采取什么策略来最大限度地减少找到解决方案所需的鸡蛋数量?。 (最糟糕的情况是它需要多少滴?)

我一直在跟着“看看我能做三个”部分。作者指出,在第一个鸡蛋破裂后,它会降解成2个鸡蛋问题并可以递归解决。

这很好,但是当我使用3个鸡蛋代替2个鸡蛋(第一个鸡蛋)时,我们不想选择更大的步长吗?从哪个楼层扔掉第一个鸡蛋?

1个鸡蛋,我们必须从1楼开始 有2个鸡蛋,我们会解决n(n+1)/2=k并向上舍入,其中n是起始楼层,k是楼层数。
3 ...我在制定公式时遇到了麻烦。


考虑到这一点,用2个鸡蛋,最大滴数等于我们放下第一个鸡蛋的楼层数。例如,有2个鸡蛋和100个楼层,解决方案是14,这意味着我们从第14层丢弃第一个鸡蛋,如果它破裂,我们必须下降13次,对于1-13层。

用3个鸡蛋,溶液为9(如图所示)。但是我们不想把第一个蛋扔到第9层,我们可以把它扔得更高,因为我们不必在中间迭代1秒。

如果我们再次从14楼扔出来,然后它就会破裂,那么我们就会递归。 n(n+1)/2=k其中k现在是13 ...但是这给了我们4.815,如果我们ceil并且添加我们之前的drop我们得到6,这比实际解决方案低,所以这里有些错误...


4 个答案:

答案 0 :(得分:9)

  

如果我们再次从14楼扔出来,然后它就会破裂,那么我们就会递归。 n(n + 1)/ 2 = k其中k现在是13 ...但是这给了我们4.815,如果我们ceil和那个并且加上我们之前的下降我们得到6,这比实际解决方案低,所以这里的东西是错...

如果没有破坏怎么办?然后你有一个有三层鸡蛋的问题,共有86层,与100层楼的问题相比,可能需要少一滴才能解决。

假设您从50 th 楼层放下第一个鸡蛋。如果它破裂,你有一个两个鸡蛋问题,49层,最多需要10滴。因此,这将给你一个11滴的最坏情况(因为如果它没有破坏,50层三蛋问题最多需要7滴)。

如果您选择第一次掉落的37 th ,如果它破裂,您有一个36层的双蛋问题,需要多达8个额外的掉落。如果没有破坏,你会留下63层的三蛋问题。你想用最多8滴来解决这个问题,所以如果下一滴掉鸡蛋,剩下的两个鸡蛋问题应该可以解决最多7滴,因此你可以选择的第二滴最高层是{{ 1}},因为最多可以用最多7滴和两个鸡蛋解决28层楼。如果鸡蛋没有破裂,你会有一个34层的三蛋问题,剩下7滴。如果蛋中断的是21(6 * 7/2),剩下的6滴你当然可以解决的最高,所以你可以选择楼层37 + 28 + 1 = 66。如果鸡蛋没有破裂,你剩下12层6滴,只有两个鸡蛋就可以了。

系统地说,使用66 + 21 + 1 = 88滴和d蛋可以解决的最高楼层数是

e

如果你只有一滴,你别无选择,只能选择你还不知道鸡蛋没有破裂的最低层。如果它打破了它,你尝试了更高的楼层,你就不知道打破鸡蛋的第一层。

如果你只有一个鸡蛋,你必须按顺序检查每个鸡蛋,直到鸡蛋破裂或你的水分用完为止。

否则,如果第一次跌落来自高于 / 1, if d == 1 F(e,d) = | d, if e == 1 \ F(e-1,d-1) + 1 + F(e,d-1), if e > 1 and d > 1 的楼层,如果鸡蛋断裂,您可能找不到第一个破土动工。如果第一滴是来自较低的楼层,如果鸡蛋没有破裂,则不能达到F(e-1,d-1) + 1滴的高度,因此第一滴应该来自楼层d-1。如果它中断,您可以通过假设解决剩余的F(e-1,d-1) + 1鸡蛋和e-1滴。如果没有,您可以使用剩余的水滴和鸡蛋来解决下一个d-1楼层。

相反,要查找F(e,d-1)个鸡蛋f地板可能需要多少滴,你必须找到

e

您可以通过计算D(e,f) = min { d | F(e,d) >= f } 矩阵找到它,或者您可以使用动态编程:

如果你选择第一次掉落F(e,d),如果鸡蛋断了,你需要最多s滴来确定地板。如果鸡蛋没有破裂,您需要最多D(e-1,s-1)滴来确定地板。 因此,为第一次下降选择楼层D(e,f-s)的最坏情况是

s

最糟糕的情况是

WC(s,e,f) = 1 + max { D(e-1,s-1), D(e,f-s) }

(当然D(e,f) = minimum { WC(s,e,f) | 1 <= s <= f } )。

答案 1 :(得分:3)

这个问题可以通过以下3种方法解决(我知道):

  1. 动态编程
  2. 使用二进制搜索树的解决方案
  3. 通过获得最大楼层数的直接数学公式得出解决方案,该公式可以测试或覆盖给定数量的鸡蛋和给定的滴数
  4. 首先让我定义一些符号如下:

    e = number of eggs
    f = number of floors in building
    n = number of egg drops 
    Fmax(e, n) = maximum number of floors that can be tested with e eggs and n drops
    

    动态编程方法的关键在于遵循Fmax的递归公式:

    Fmax(e, n) = 1 + Fmax(e-1, n-1) + fmax(e, n-1)
    

    获得Fmax的直接数学公式的关键在于遵循Fmax的递归公式:

    Fmax(e, n) = { ∑Fmax(e-1,i) for i = 1 to n } - Fmax(e-1, n) + n 
    

    使用二进制搜索树(BST)的替代解决方案也可能解决此问题。为了便于我们的分析,让我们绘制BST,稍作修改如下:

    1.    If egg breaks then child node is drawn on left down side
    2.    If egg does not break then child node is drawn straight down side
    

    如果我们用上面的表示法绘制BST,那么BST的宽度(即BST中的垂直列数)代表鸡蛋的数量。

    任何具有f个节点的BST,用上述类型的表示绘制并受到BST <= e(蛋的数量)的约束宽度是一种解决方案,但它可能不是最佳解决方案。

    因此,获得最优解决方案等同于获得BST中具有最小高度的节点的布置受到约束:BST的宽度<= e

    有关上述所有3种方法的详细信息,请查看我的博客:3 approaches for solving generalized egg drop problem

答案 2 :(得分:1)

您可以使用简单的动态编程解决方案。 n - 楼层数。 k - 鸡蛋数量。 D [n,k] =你回答(最低投掷次数)。 对于每个1 <= j <= n

,D [j,1] = n-1

计算D [n,k]的主要思想是k> 1:

D [n,k] =最大1 <= j <= n-1 {最大{D [j,k-1] +1,D [n-j,k] +1}。

答案 3 :(得分:1)

这与我在Egg Drop Printing Solutions中提供的答案相同。我在这里向希望查看整个决策树和推理布局的任何人提供它。

# This uses dynamic programming to find the basic information.
def optimal_solution(floors, eggs):
    # dp[drops][eggs] = max_floors
    dp = []

    # With no drops, we can do no floors
    dp.append([0 for x in range(eggs+1)])

    # With one drop and any eggs, we can do 1 floor
    one_drop = [1 for _ in range(eggs+1)]
    one_drop[0] = 0 # Except no eggs is still no floors
    dp.append(one_drop)

    # Keep adding drops until we have our answer
    # Note, in python array element -1 is shorthand for the end of the array.
    while dp[-1][eggs] < floors:
        # 0 floors for 0 eggs.  1 more for one egg
        next_drop = [0, dp[-1][1] + 1]
        for i in range(2, eggs+1): # Python for 2..eggs
            # The best we can do is drop at floor dp[-1][i-1].
            # If the egg breaks, we can find the answer using that solution.
            # If the egg holds, we can find another dp[-1][i] floors.
            next_drop.append(dp[-1][i] + dp[-1][i-1])
        dp.append(next_drop)

    return dp

# This turns that optimal solution into a decision tree.    
def dp_to_decision_tree(dp, floors, start_floor=None, eggs=None, drops=None):
    # Figure out defaults if needed.
    if start_floor is None:
        start_floor = 0
    if drops is None:
        drops = len(dp) - 1
    if eggs is None:
        eggs = len(dp[0]) - 1

    # Are we done?
    if floors == start_floor:
        return start_floor
    elif dp[drops][eggs] < floors - start_floor:
        return None

    # Do we need all of our drops?
    while floors - start_floor < dp[drops-1][eggs]:
        drops -= 1

    drop_at = start_floor + dp[drops-1][eggs-1]
    if eggs == 1:
        drop_at = start_floor + 1
    if floors < drop_at:
        drop_at = floors
    return [
        drop_at,
        dp_to_decision_tree(dp,  floors,     drop_at,   eggs, drops-1),
        dp_to_decision_tree(dp, drop_at-1, start_floor, eggs-1, drops-1),
        {'eggs': eggs, 'floor_range': (start_floor, floors)}
        ]

# This prints the decision tree in a human readable format.
def print_decision_tree(tree, indent="", label="start"):
    if tree is None:
        print(f"{indent}{label}: ?")
    elif isinstance(tree, int):
        print(f"{indent}{label}: {tree} found")
    else:
        print(f"{indent}{label}: {tree[0]} {tree[3]}")
        print_decision_tree(tree[1], indent + "    ", label="held")
        print_decision_tree(tree[2], indent + "    ", label="broke")

# And this calls the previous functions.
def print_optimal_decisions(floors, eggs):
    print_decision_tree(
        dp_to_decision_tree(
            optimal_solution(floors, eggs), floors))

# And now we can try it.
print_optimal_decisions(36, 3)