1. 程式人生 > >演算法設計與分析——動態規劃(一)矩陣連乘

演算法設計與分析——動態規劃(一)矩陣連乘

動態規劃——Dynamic programming,可以說是本人一直沒有啃下的骨頭,這次我就得好好來學學Dynamic programming.
OK,出發!

動態規劃通常是分治演算法的一種特殊情況,它一般用於最優化問題,如果這些問題能夠:
1.能夠分解為規模更小的子問題
2.遞迴的子問題具有最優子結構性質。也就是說,原問題的最優化解能夠通過子問題的解計算得到。

動態規劃的一個核心的步驟就是定義一個合適的問題形式,動態規劃與分治演算法不一樣,動態規劃演算法通常需要列舉所有的可能的切分策略,這是因為需要得到最優解;除此之外,動態規劃通過“記錄已計運算元問題的解”來避免重複計算。

為了使用動態規劃的方法,那麼我們首先得先看問題能夠使用分治的方法來解決。給定一個問題,我們需要先看看他的核心輸入資料結構是否可以分解。如果發現輸入的核心資料結構為:

  • 陣列
  • 矩陣
  • 集合

  • 那麼這些輸入是很容易分解的。

我們再看,子問題的輸出能夠構成原問題的輸出。
如果上面兩個條件都滿足,那麼這個問題當然是可以使用分治法來解決的。

下面,我們通過幾個經典的問題來看看如何由分治過渡到動態規劃。

矩陣連乘問題


INPUT:
給定: A

1 , A 2 , , A n
A_{1},A_{2},\dots,A_{n} n個矩陣,矩陣 A i A_{i} 的形狀為 p i 1 × p i p_{i-1}\times p_{i}
OUTPUT:
給出一個 A 1 , A 2 , , A n A_{1},A_{2},\dots,A_{n} 的運算順序,使得乘法的次數最少。


先看看為啥不同運算順序,會造成乘法的次數不一樣呢?
假設有三個矩陣:
A 1 1 × 2 , A 2 2 × 3 , A 3 3 × 4 A_{1}的形狀為1 \times 2,A_{2}的形狀為2\times 3,A_{3}形狀為3 \times 4
那麼第一種運算順序: ( A 1 A 2 ) A 3 (A_{1}A_{2})A_{3} ,需要的乘法次數為:1x2x3+1x3x4=18
第二種運算順序: A 1 ( A 2 A 3 ) A_{1}(A_{2}A_{3}) ,需要的乘法次數為2x3x4+1x2x4=32。
那麼第一種運算次數更少,因此第一種運算順序更好。

當直接給n個矩陣的時候,我們是不好下手的,也沒有啥頭緒。那我們就想,
我們就先看n=1,2,3,4的時候嘛。
當n=1的時候,不要乘了。
當只有2個矩陣相乘的時候,沒有什麼最優的,就只有一種運算順序,我們看到會做的;
當只有3個矩陣相乘的時候,我們也會做的,就是隻要像上面的例子一樣去比較就是了的。
當有四個矩陣的時候呢?能不能劃為先三個矩陣相乘再兩個矩陣相乘?或者左邊兩個矩陣,右邊兩個矩陣,兩個中間結果再乘起來?因為,我們會做2個和3個矩陣相乘了。
當有5個矩陣呢?

我們看到,當n很小的時候,我們是會做的。那麼給定一個很大的n,我們能不能把它轉換為規模更小的n呢?


假設我們把這個問題看做一個多階段決策問題,每次的決策就是選一個位置加一個括號。

OK,假設我們已經得到最優的解了。那麼這個最優解的最後一次乘法,一定可以看成是兩個部分的乘積。也就是說從:
A 1 A 2 A k A k + 1 A n A_{1}A_{2} \dots A_{k} A_{k+1} \dots A_{n}
選擇一個k,使得最終的結果是從:
( A 1 A 2 A k ) ( A k + 1 A n ) (A_{1}A_{2} \dots A_{k})( A_{k+1} \dots A_{n})
兩部分乘積得到。
這下子好了,左邊的連乘就是 A 1 A 2 A k A_{1}A_{2} \dots A_{k} ,這個問題和原問題很像啊,就是規模改了一下,內容不一樣而已。右邊也是類似的。
然後,我們先解決 A 1 A 2 A k A_{1}A_{2} \dots A_{k} ,顯然這個問題,也可以看成最後由兩個矩陣乘積得到,也就是要選擇一個j使得: ( A 1 A 2 A j ) ( A j + 1 A k ) (A_{1}A_{2} \dots A_{j})( A_{j+1}\dots A_{k})

等等···
經過若干次的分析以後,我們就開始看到我們所要解決問題的通用形式了:尋找某個執行順序,使得 A i A i + 1 A j A_{i}A_{i+1} \dots A_{j} 的乘法次數最少。定義 O P T ( i , j ) OPT(i,j) 表示 A i A i + 1 A j A_{i}A_{i+1} \dots A_{j} 所需的最少的乘法次數。原問題其實就是在求 O P T ( 1 , n ) OPT(1,n)

而將原問題分解為兩個子問題: O P T ( 1 , k ) OPT(1,k) O P T ( k , n ) OPT(k,n) ,那麼原問題的解可以通過 O P T ( 1 , n ) = O P T ( 1 , k ) + O P T ( k , n ) + p 0 × p k × p n OPT(1,n)=OPT(1,k)+OPT(k,n)+p_{0}\times p_{k} \times p_{n} 得到。

這是因為左邊 A 1 A 2 A k A_{1}A_{2} \dots A_{k} 得到一個 p