徹底搞懂-扔雞蛋問題-方程-動態規劃

1、題目:

2個雞蛋,從100層樓上往下扔,以此來測試雞蛋的硬度,比如雞蛋在第9層沒有摔碎而在第10層摔碎了,那麼雞蛋不會摔碎的零界點就是9層,如何用最少的嘗試次數,測試出雞蛋不會摔碎的臨界點?

2、解決思路

2.1、最笨法:

把其中一個雞蛋從第1層開始往下扔,如果第1層沒碎換到第2層扔,如果第2層沒碎換到第3層扔,,,如果第59層沒碎換到第60層扔,如果第60層碎了,說明不會摔碎的臨界點是59層,最壞情況下需要扔100次

2.2、二分法:

把雞蛋從50層往下扔,如果第一枚在50層碎了,就從第1層開始(一共只有兩個雞蛋,第一個雞蛋碎了,第二個雞蛋就只能用最保守的方式一層一層扔,假如第二個雞蛋從25層扔,碎了,就無法判斷臨界點了);如果沒碎,繼續使用二分法,在剩餘樓層的一半(75層)往下扔,,,,最壞需要嘗試50次

2.3、平方根法:

做一個平方運算,100的平方根是10,因此我們嘗試每10層扔一次,第一次從10層扔,第二次從20層扔,第三次從30層,,,一直到100層,最好情況是在第10層碎掉,嘗試次數爲1+9=10次;最壞情況是在第100層碎掉,嘗試次數爲10+9=19次

2.4、方程法:將思路逆轉

假設問題存在最優解,這個最優解的最壞情況嘗試次數是x次,那麼我們第一次扔雞蛋該選擇哪一層?

答案是從第x層開始,選擇更高一層或更第一層都不合適

分析:假設第一次扔在第x+1層,如果第一個雞蛋碎了,那麼第二個雞蛋只能從第1層一層一層扔,一直到第x層,這樣總共扔了x+1次,和假設嘗試x次相悖,由此可見,第一次扔的樓層必須小於x+1次

假設第一次扔在第x-1層,如果第一個雞蛋碎了,那麼第二個雞蛋開始一層一層扔,直到第x-2層,這樣我們總共扔了x-1次,雖然沒有超過假設次數,但似乎有些過於保守(什麼意思,還沒理解)

假設第一次扔在第x層,如果第一個雞蛋碎了,那麼第二個雞蛋開始一層一層扔,一直到第x-1層,共x次

因此要向儘量樓層跨度大一些,又保證不超過假設的嘗試次數x,那麼第一次扔雞蛋的最優選擇就是第x層

更進一步。。。

如果第一次扔雞蛋在第x層,但並沒有打碎,第二次扔雞蛋在哪一層?

如果第一次扔雞蛋沒碎,我們的嘗試次數消耗了一次,問題就轉爲,兩個雞蛋在100-x層樓往下扔,要求嘗試次數不得超過x-1次,則第二次嘗試的樓層跨度爲x-1層,絕對樓層是x+x-1層;如果還沒碎,第三層樓層跨度是x-2,第四層是x-3!

so,x+(x-1)+(x-2+...+1=100,左邊多項式是各次扔雞蛋的樓層跨度之和,假設嘗試x次,所以該多項式共有x項,右邊是總的樓層數100,解方程可得x=14((x+1)*x/2=100)

因此,最優解在最壞情況的嘗試次數是14次,第一次扔雞蛋的樓層也是14層,在第一個雞蛋沒碎的情況下,嘗試的樓層爲:14,27,39,50,60,69,77,84,90,95,99,100,假如雞蛋不會碎臨界點在65層,那麼依次:14,27,50,60,69碎了,讓後第二個雞蛋從61層開始61,62,63,64,65,66碎了,即不會碎的臨界點是65層

3、動態規劃解題:有M層樓N個雞蛋,找到雞蛋摔不碎的臨界點,需要嘗試幾次?

可以把m層樓和n個雞蛋的問題轉化爲一個函數F(m,n),其中樓層數m和雞蛋數n是函數的兩個參數,函數的值則是最優解的最大嘗試次數,假設第一個雞蛋扔出的位置在第x層(1<=x<=m),會出現兩種情況:

1).第一個雞蛋沒碎,則剩餘m-x層樓和n個雞蛋,F(m-x,n)+1

2).第一個雞蛋碎了,則從1到x-1層嘗試,剩餘雞蛋n-1,F(x-1,n-1)+1

so,我們要求m層樓n個雞蛋條件下,最大嘗試次數的最小解,即

F(m,n)= Min(Max(F(m-x,n)+1,F(x-1,n-1)+1)) . 1<=x<=m

3.1、eg:3個雞蛋,4層樓

首先填充第一個雞蛋在各個樓層的嘗試次數,以及任意多雞蛋在1層樓的嘗試次數,原因:只有一個雞蛋,so只能從1層扔到最後一層,嘗試次數爲樓層數量;只有一個樓層,無論有幾個雞蛋,也只有一種扔法,嘗試次數只能爲1

2個雞蛋2層樓,帶入狀態轉移方程式F(2,2)=Min( Max(F(2-x,2)+1,F(x-1,2-1)+1)) )

x可以爲1和2,so:

x=1時,F(2,2)=Max(F(2-1,2)+1,F(1-1,2-1)+1))=Max(F(1,2)+1, F(0,1)+1)=Max(1+1, 0+1)=2

x=2時, F(2,2)=Max(F(2-2,2)+1,F(2-1,2-1)+1))=Max(F(0,2)+1, F(1,1)+1)=Max(0+1, 1+1)=2

取最小值即爲2

依次類推

先上代碼(當然了,還是別人寫的,看的可真費勁,話說動態規劃看一次不懂一次。。。)

3.2、python版代碼解決floor層樓仍egg個雞蛋的問題

# 我們要求m層樓n個雞蛋條件下,最大嘗試次數的最小解,即
# F(m,n)= Min(Max(F(m-x,n)+1,F(x-1,n-1)+1)) . 1<=x<=m
import numpy as np


def get_min_floor(floors=100, eggs=2):
    # 第一步,創建動態規劃的備忘錄,即狀態轉移矩陣
    f = np.zeros((eggs+1, floors+1), dtype=np.int)
    # 第二步,考慮邊界
    # part1: 先考慮eggs邊界,eggs爲0,則爲0;eggs爲1,肯定從第0層往上依次實驗
    for i in range(floors+1):
        f[0][i] = 0
        f[1][i] = i
    # part2: 再考慮floor的邊界,floors爲0即爲0,floors爲1即爲1
    for i in range(eggs+1):
        f[i][0] = 0
        f[i][1] = 1
    # 第三步就是狀態方程了,雞蛋從第2個開始算,樓層也從第2開始算
    for egg in range(2, eggs+1):
        for floor in range(2, floors+1):
            # 你還有egg個雞蛋,一共有floor層樓的子問題
            # 定義一個變量來存儲最終結果,找到在哪裏扔能達到所扔次數最少的目標
            result = 100000
            # 從第drop層扔雞蛋
            for drop in range(1, floor+1):
                # 碎了,剩下的問題即如何在drop-1層,用egg-1個雞蛋尋找最優解
                broken = f[egg-1][drop-1]
                # 沒碎,在floors-drop層,用egg個雞蛋找最優解
                unbroken = f[egg][floor-drop]
                # 兩種情況取最大值,因爲我根本不知道雞蛋會不會碎
                condition = max(broken, unbroken)+1
                # 不斷和上一次結果做比較,得到最少的扔次數
                result = min(condition, result)
            f[egg][floor] = result
    # 以上步驟在不斷的往狀態舉證填充,到這裏已經填充完畢
    final_result = f[eggs][floors]
    return final_result


if __name__ == '__main__':
    print get_min_floor(eggs=2, floors=100)  # 輸出結果14
    print get_min_floor(eggs=2, floors=36)   # 輸出結果8
    print get_min_floor(eggs=2, floors=39)   # 輸出結果9
    print get_min_floor(eggs=3, floors=39)   # 輸出結果6

參考:https://blog.csdn.net/Autumn03/article/details/80886814

          https://blog.csdn.net/aBen_Dan/article/details/90897527

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章