Dynamic programming

本文針對兩篇優秀動態規劃文章中存在的不易理解的部分:狀態、狀態轉移的定義和狀態轉移方程的編程實現部分進行個人解讀。歡迎指出本文存在的錯誤和表述不清之處。

DP概述

DP(Dynamic Programming)是算法設計中解決最優化問題(eg: 最長公共子序列)的重要思想。
(待補充)
因此我們可以得出結論:

動態規劃的本質,是對問題狀態的定義和狀態轉移方程的定義。
——什么是動態規劃?動態規劃的意義是什么?(徐凱強 Andy的回答)

狀態

這里給出我自己的一個定義后面會解釋:

從“原始的最優化問題”剝離出的每個“最優化子問題”都是當前DP問題的一個狀態,每個狀態分布在“每次的決策前后”。

我們現在來看兩個簡單的動態規劃問題:

問題1:
給定一個數列A,長度為N,
求這個數列的最長上升(遞增)子數列(LIS)的長度.

1 7 2 8 3 4
為例。
這個數列的最長遞增子數列是 1 2 3 4,長度為4;
次長的長度為3, 包括 1 7 8; 1 2 3 等.

剝離出的子問題,以及等價最終問題:
給定一個數列A,長度為N
設F(x):以數列A中第x項結尾的最長遞增子序列的長度, 其中0 <= x <= N 。(子問題or狀態)
求F(1)..F(N)中的最大值 (等價最終問題)

問題2:
如果我們有面值為1元、3元和5元的硬幣若干枚,如何用最少的硬幣湊夠11元?

剝離出的子問題,以及等價最終問題:
如果我們有面值為1元、3元和5元的硬幣若干枚
設D(i):湊夠i元使用最少硬幣個數,其中 0 <= i <= 11。(子問題or狀態)
求D(11)的值。 (等價最終問題)

這兩個問題便是“原始的最優化問題”。現在,根據“狀態和狀態轉移方程的定義是動態規劃的本質”的結論,我們需要做的就是從“原始的最優化問題”中找出狀態,然后再得到狀態轉移方程,之后根據狀態轉移方程實現代碼(其間還會使用到記憶和重疊子問題特性),最終得到解。

  • 剝離子問題,獲得狀態(最需要技巧和訓練的部分)
    之前說過,DP中的狀態就是子問題。顯然,我們要獲得狀態就必須剝離(拆分)“原始的最優化問題”。這里通過分析問題1和問題2剝離好的子問題探索一下DP中剝離(拆分)問題的“奇淫巧技”。

    首先拿到一個DP問題,我們最先要思考的是: 該問題的“變數”在哪里?這里的“變數”很重要,問題的“變數”往往是整個問題定義中的一個參數,當我們改變這個參數時,將會使問題的規模縮小。 例如,問題1的變數是數列A的大小,當我們改變數列A的大小時,問題1的規模變縮小了。因此,我們才這樣剝離問題1:"F(x)是以A[x]為結尾的LIS的長度,且 0<= x <= N"。顯然,此時若x 等于 N-1,我們把問題縮小了單位1長度;此時若x等于0,我們則把問題縮小成了0,顯然此時還觸碰到了當前問題的邊界:“數列長度必須大于等于0”。問題2中也可套用同樣的思路: 我們可以把最終需要湊夠的價格i元作為變數,當我們改變i元的大小時,問題2的規模同樣被縮小了。因此我們才這樣重新定義問題2:"D(i)是湊夠i元需要的最少硬幣個數"。此時若i等于0,我們同樣到達了當前問題的邊界:"湊錢不可能湊出負數"。

    因此,DP剝離子問題需要多聯系"當前問題中可能的變數",同時多加練習。

  • DP中每個子問題都是狀態,分布在每次決策前后
    把DP中的子問題問題叫做狀態有兩方面原因。

  • 為了區分問題和子問題而產生的叫法。

之所以把Fx叫做“狀態”而不是“問題” ,一是因為避免跟原問題中“問題”混淆,二是因為這個新問題是數學化定義的。
——什么是動態規劃?動態規劃的意義是什么?(徐凱強 Andy的回答)

  • 狀態二詞更能準確表示出DP子問題是什么

從前面對剝離子問題的分析大抵可以感受到這么一個過程: 整個DP問題從問題的定義邊界開始(比如: 數列之長度為0,湊錢之總額為0),到問題收斂處為止(比如: 數列之長度為N,湊錢之總額為0),算法的每次決策都在推動前一個子問題發展到后一個子問題,往往這種推動都有多種可能性

比如:A={1 7 2 8 3 4},從當前F(3)={1,2}到F(6)時,我們可以很機智地選擇從F(5) = {1,2,3}走最優解到F(6)={1,2,3,4}。但我們也可以任性地選擇F(5)={1,2}到F(6)={1,2,4},或者F(5)={1,2,3},F(6)={1,2,3}..以及其他多種可能性)。此時我們可以在子問題之間以不同的狀態(如F(5)={1,2,3} 或者F(5)={1,2})進行轉移(比如從F(5)={1,2,3}到F(6)={1,2,3,4}),因此把子問題叫做“狀態”,把子問題間的轉移叫做“狀態轉移”是很形象的說辭。

狀態轉移方程

對狀態的解釋已經觸及到了狀態轉移的概念:“子問題間通過決策進行推演的過程叫做狀態轉移”。所以,狀態轉移方程就應該是“子問題間通過決策定義的等式關系叫做狀態轉移方程”,通常狀態轉移方程和當前問題的邊界條件聯立起來叫做狀態轉移方程式。


——什么是動態規劃?動態規劃的意義是什么?(徐凱強 Andy的回答)


——動態規劃:從新手到專家

顯然,max(), min()就代表著一種最優決策。我們如果把max或者min去掉,那么上面的方程式依然可以叫做狀態轉移方程,只是得到的結果不一定是最優解了。

編程實現

初學者在解DP問題時,如何編程實現狀態轉移方程?如何在DP求解過程中利用重疊子問題進行記憶求解?
讓我們看一下問題2的偽代碼:


從偽代碼我們可以分析出一個較為通用的DP代碼實現過程:(S代表總額,N-1代表可能的硬幣面值)

  • 首先我們需要存儲每個子問題的最優解,比如偽代碼中使用Min[i]數組存儲每個子問題的最優解。
  • 然后我們需要對DP問題的邊界的解進行初始化,比如偽代碼中Min[0] = 0代表著邊界條件"湊夠0元需要的最少硬幣數"的解。
  • 定義好邊界解后,我們就可以從邊界解開始,使用遞歸的方法自底向上的求解DP的每個子問題,并把子問題的最優解記錄下來。比如,偽代碼中從i = 1開始求解直到i = s,每次得到的子問題最優解都被存儲在Min[i]中。
  • 最后,根據特定的題目,或直接輸出某個子問題的最優解,或對所有子問題的最優解進行處理以后再進行輸出。比如:硬幣問題我們可以直接輸出Min[11]。而LIS問題我們需要比較F(x),x = 0,1,2...N, 然后輸出F(x)的最大值。

引用

什么是動態規劃?動態規劃的意義是什么?
http://www.hawstein.com/posts/dp-novice-to-advanced.html
動態規劃:從新手到專家
https://www.zhihu.com/question/23995189

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容