作爲一個非科班出身的碼農,對計算機的一些基礎知識有很多不太熟悉的地方。考慮到萬丈高樓平地起,只是一味地在工作中對業務問題構建空中樓閣確實不利於以後的發展,決定對計算機的基礎知識做一些學習和整理,一方面檢查自己是否真正掌握該內容,另一方面望各位同行不吝賜教。
廢話少說,直入正題,今天主要整理一下動態規劃,以下內容主要來源於對《算法導論》和《麻省理工大學算法導論公開課》的學習整理。
1. 動態規劃簡介
動態規劃(Dynamic Programming)中的programming指的是一種表格法,直譯過來就是用動態表格去解決問題的一種方法,通常用來解決最優化問題,問題的解並非唯一解,而是一個解。沒有實例講解算法就是耍流氓,接下來以最長公共子序列的問題來展開介紹動態規劃是個啥,以及如何設計動態規劃來解決實際問題。
2. 最長公共子序列問題
子序列:給定一個序列
公共子序列:給定兩個序列
最長公共子序列(Longest common subsequence, LCS)問題即給定兩個序列
問題實例
設給定兩個序列,
直觀地,我們可以考慮用窮舉法解決該問題。
2.1 窮舉法
因爲要查找
時間複雜度:
2.2 帶備忘的自頂向下方法
首先,引入一組定義:
從而可以得到
從而引出動態規劃的第一個重要性質——最優子結構:如果一個問題的最優解包含其子問題的最優解,那麼我們稱此問題具有最優子結構的性質。上面遞歸表達式中,
基於以上表達式,我們可以構建求解
求解的最差情況是
根據上面的遞歸表達式,我們以
可以發現,會有很多遞歸求解是重複的,這就引出了動態規劃的第二個重要性質——重疊子問題:如果遞歸算法反覆求解相同的子問題,則稱該問題具有重疊子問題。LCS的問題包含了
此時求解LCS的時間複雜度變爲
以上即爲帶備忘的自頂向下求解LCS的方法,通過增加空間複雜度,大大降低了時間複雜度。
2.3 自底向上動態規劃
自頂向下的方法中,每次會對自身進行迭代,獲取當前需要的子問題,相當於一個從大到小的轉變。在某些情況下,自頂向下對方法並未考慮所有的子問題(比如某些子問題併爲必須求解的),而且,頻繁地遞歸調用會有一定的開銷,因此考慮自底向上,求解全部的子問題的最優解,進而求解最終的最優解,簡單的僞代碼如下:
通過該代碼,即可求出LCS的長度,即最優解的值。爲了求出LCS的具體元素,可以額外使用一個數組儲存每次尋找的路徑,從而獲取最優解。
3. 動態規劃基本策略
綜上,可以總結一下動態規劃的基本策略:
1. 刻畫一個最優解的結構特徵
2. 遞歸地定義最優解的值
3. 計算最優解的值,通常採用自底向上的方法
4. 利用計算出的信息構造一個最優解