動態規劃的英文叫做 Dynamic Programming,也就是大家俗稱的低批(DP),不是打棒球的那個雙殺、不是狄利克雷過程、不是設計模式、也不是指神奇寶貝的珍珠鑽石版,是演算法中的動態規劃。在解決一個問題的時候,我們往往會喜歡把這個問題拆成很多小問題,然後先解決小的問題,如果沒辦法解決,那就把提出問題的人解決掉,就沒有問題了。不過這樣很麻煩,因為會少一個人很不方便。
據傳聞,只要寫出了遞迴式子,就可以從小範圍逐步計算出我們要的答案!那我們先來練習看看,對於給定的遞迴式子,要怎麼把它寫成長得像動態規劃的程式碼吧!
https://leetcode.com/problems/fibonacci-number/
費氏數列的定義如下:F(0) = 0, F(1) = 1, F(N) = F(N-1) + F(N-2)。現在給你 N 之值 0 ≤ N ≤ 30,請問 F(N) 為何。
先撇開非常著名有爭議的 <moon.h>
不談,如果只是要用動態規劃的想法來依序推論出前 N 項之值,那麼顯然我們可以開一個陣列然後讓 N 從 0, 1, 2, … 開始跑,逐項計算它。在計算遞迴關係式的時候,只要保證被依賴的那幾項都已算出答案並且存得好好的,就不難推算出下一個未知項了!這就是動態規劃的概念~
#include <moon.h>
class Solution:
def fib(self, N: int) -> int:
F = {}
F[0], F[1] = 0, 1
for i in range(2, N+1):
F[i] = F[i-1] + F[i-2]
return F[N]
蛤,你說為什麼 python 裡面可以 include?因為 # 是註解呀~
這個例子可能太簡單了,那我們來看看下面這個經典例子。
https://leetcode.com/problems/coin-change/
給你一些硬幣的面額,在每一種面額都無限量供應的情形下,想要湊出某個特定額度 amount
,至少需要幾個硬幣呢?
假設能夠使用的面額為 c1, c2, …, cK。對於指定額度 A > 0,如果能夠湊得 $A,顯然至少需要用某個硬幣吧~於是,我們分別試試看「先拿出 $c1」、「先拿出 $c2」、...、「先拿出 $cK」這些方法。拿走某個額度後,剩下總額度就變小啦~也就是說,如果我們先算出額度比 A 小的任何額度所需要的硬幣數量,加上最先拿出來那個硬幣,總數最少者就是答案!
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = collections.defaultdict(lambda: float("inf"))
dp[0] = 0
for A in range(1, amount+1):
dp[A] = min([1 + dp[A - coin] for coin in coins])
return -1 if math.isinf(dp[amount]) else dp[amount]
在上面這個例子中,我們讓 A 從 $1, $2, $3, …, 直接跑到 $amount,對於每個這樣的 A,我們考慮拿出每種錢幣的情形,並且找出能夠湊出剩下金額的最小值。
好的,今天的範例就到這邊。顯然兩份程式碼在「效能、精簡、好讀」三個方向都有可以改善的空間,這個就交給各位讀者朋友們一起來集思廣益啦~我們明天見!
如果沒辦法解決,那就把提出問題的人解決掉 XD
期待這一系列 加油加油
啊...被發現偷放垃圾話了 :D
謝謝您的鼓勵!我今年會努力撐好撐滿一個月的~~~
那想請教一下遞迴跟動態規劃有什麼差別呢?
遞迴(Recursion) 跟遞迴關係(Recurrence Relation) 對我來說是兩個不一樣的東西。
我的理解是這樣的:動態規劃是一種設計演算法的範型(Paradigm),而遞迴是用來實現題解的一種手段(Implementation)。
只要滿足:一個問題可以拆成很多子問題,而這個問題的答案可以透過組合子問題的答案來得到它、再加上子問題的重複量很大,那麼這個問題就可以使用「把算過的東西記下來,以後會用得上」這個策略來設計演算法,而依循這個策略設計出來的演算法基本上就是動態規劃了。
至於要怎麼實作它,可以用遞迴(發現即將要使用的子問題還沒算過,就 call 一次自己然後把計算的結果存下來)、也可以不需要透過遞迴(比方說採用迴圈建表)。
所以,在某些題目條件下,我們可以使用遞迴來實作動態規劃演算法~
遞迴關係是指把問題與子問題連結起來的那個表達式,可以把它想像成公式那樣,只告訴你解是什麼,但沒指定要用什麼演算法來計算出這個解~但好處是,遞迴關係(包含遞迴終止條件)只要定義清晰,就可以設計出動態規劃(或分而治之)的演算法了。
不曉得有沒有解釋到您提出的疑惑? 謝謝您的留言!如果我有講錯的地方或不足的地方還請指教與討論了~~~~
原來如此!!謝謝你的解釋,因為我之後文章也要寫遞迴跟動態規劃,但還是覺得自己這方面知識超不足的
哦哦!透過討論和大量的例題+練習,感覺可以把東西講得更清楚!期待您的文章~~