動態規劃法(六)雞蛋掉落問題(一)(egg dropping problem)

weixin_34290000發表於2018-06-05

  繼續講故事~~
  這天,丁丁正走在路上,欣賞著路邊迷人的城市風景,突然發現前面的大樓前圍了一波吃瓜群眾。他好奇地湊上前去,想一探究竟,看看到底發生了什麼事情。
  原來本市的一位小有名氣的科學家正在這幢大樓進行一個實驗:某種材料的防護效能。他在大樓的底下鋪了一層這種防護材料,想拿雞蛋做實驗,將雞蛋從樓層掉下,看看雞蛋從哪一層掉下去會摔碎,以此測試該材料的防護效能。這就是著名的雞蛋掉落問題(egg dropping problem),即給定N個雞蛋和k層樓,試問至少需要幾次才能確定雞蛋從哪一層樓掉下去恰好摔碎。
  一聽到這個問題,他頓時感覺無從下手,沒有絲毫的頭緒。但他嘗試著先從最簡單的情形入手:

  • 若雞蛋數N=0或者樓層數為0,則嘗試0次即可。
  • 若雞蛋數N=1, 對於樓層數為k,最壞的情況為嘗試k次,因此至少需要k次才能確定雞蛋從哪一層樓掉下去恰好摔碎。

有了最基本的情形還不夠,對於其他的N,k並沒有給出答案。這時,他想到自己這幾天正在研究的演算法:動態規劃法,他想也許這個演算法可以幫上忙。假設用numdrops(N,k)表示該問題的解。則將雞蛋從x層扔下,有以下兩種情形:

  • 雞蛋摔碎了,此時剩下N-1個雞蛋,需要考慮比x層低的樓層,即1,2,...,x-1層,因為比x層高的樓層扔下去必定也摔碎,故可不比考慮。因此,這個問題會被縮減到numdrops(N-1, x-1).
  • 雞蛋沒有摔碎,此時剩下N個雞蛋,需要考慮比x層高的樓層,即x+1, x+2,...,k層,共有k-x層。因此,問題會被縮減到numdrops(N, k-x).

對於以上兩種情形,應該去兩者的最大值。同時,考慮到x=1,2,3,...,k, 因此有如下公式:

9419034-65a4f0ff435909db.jpg

有了以上演算法,再加上如下初始情況:

  • numdrops(0, x)=0, numdrops(x,0)=0.
  • numdrops(1, x)=x.

就可以用動態規劃法求解該問題了。他迅速地寫下了Python程式碼:

import numpy as np

def solvepuzzle(n, k):
    numdrops = np.array([[0]*(k+1)]*(n+1))

    for i in range(k+1):
        numdrops[1, i] = i

    for i in range(2, n+1):
        for j in range(1, k+1):
            minimum = float('inf')

            for x in range(1, j+1):
                minimum = min(minimum, (1+max(numdrops[i, j-x], numdrops[i-1, x-1])))

            numdrops[i, j] = minimum

    print(numdrops)
    return numdrops[n,k]

t = solvepuzzle(3, 10)
print(t)

輸出結果如下:

[[ 0  0  0  0  0  0  0  0  0  0  0]
 [ 0  1  2  3  4  5  6  7  8  9 10]
 [ 0  1  2  2  3  3  3  4  4  4  4]
 [ 0  1  2  2  3  3  3  3  4  4  4]]
4

這樣就可以求出該問題的解了,比如4個雞蛋10層樓,則只需要3次即可。
  丁丁連忙把這個解答問題通過助手告訴了科學家。科學家聽後興奮不已,立馬叫丁丁過來討論。科學家對他說道:“你的解決方法是如此的巧妙,讓人歎為觀止,很難相信它是出自一個少年的腦袋中。但是,親愛的朋友,你能告訴我具體應該怎麼扔雞蛋呢?”
  丁丁聽了,又是歡喜又有點擔心,因為科學家又提出了一個問題。他看看了輸出的numdrops表格,立馬就有了主意。
  對於N=2,k=50的情形,numdrops(N,k)=10,給出的方案如下:

從哪一層扔雞蛋 雞蛋摔碎後的情形 次數
10 1->2->3->4->5->6->7->8->9 9+1=10
19 11->12->13->14->15->16->17->18 8+2=10
27 20->21->22->23->24->25->26 7+3=10
34 28->29->30->31->32->33 6+4=10
40 35->36->37->38->39 5+5=10
45 41->42->43->44 4+6=10
49 46->47->48 3+7=10
50 8

嘗試著對上表做說明:先從第10層扔下,若雞蛋摔了,則還剩一個雞蛋,依次從1,2,3...9層扔下,若雞蛋沒碎,則失去了一次嘗試機會,再將雞蛋從19層扔下,若雞蛋碎了,則還剩一個雞蛋,依次從11,12,...,18層扔下......

  對於N=4,k=20的情形,numdrops(N,k)=10,輸出的numdrops表如下:

[[ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
 [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20]
 [ 0  1  2  2  3  3  3  4  4  4  4  5  5  5  5  5  6  6  6  6  6]
 [ 0  1  2  2  3  3  3  3  4  4  4  4  4  4  4  5  5  5  5  5  5]
 [ 0  1  2  2  3  3  3  3  4  4  4  4  4  4  4  4  5  5  5  5  5]]
5

首先找到numdrops(3,14)=4, 次數比5小一次, 層樓k儘可能大,則將雞蛋從15扔下,若雞蛋沒碎,則剩下3個雞蛋,探索4層樓,因為numdrops(3,14)=4,故能完成。若雞蛋碎了,則再找到numdrops(2,6)=3,則將雞蛋從7樓扔下,若雞蛋碎了,則情形轉化為numdrops(2,6)的情形,若雞蛋沒碎,則用剩下的雞蛋探索7層樓,是可以搞定的。
  當科學家看到這個結果後,滿意地點點頭,他想著是否要聘請這個少年來當自己的助手。不過,他決定再考驗丁丁一回~~
  未完待續~~

注意:本人現已開通兩個微信公眾號: 用Python做數學(微訊號為:python_math)以及輕鬆學會Python爬蟲(微訊號為:easy_web_scrape), 歡迎大家關注哦~~

相關文章