最優化問題的解法 - 動態規劃

雖是讀書筆記,可是如轉載請註明出處 http://segmentfault.com/blog/exploring/
.. 拒絕伸手複製黨算法

如下是算法導論第15章的學習筆記segmentfault


動態規劃經常使用於最優化問題。可能存在多個取最優解的值,但願找到其中一個最優解。函數

[摘自安勃卿的 BLOG][1]
動態規劃與分治法相似,也是將問題分解爲若干個子問題,先求解子問題,而後從這些子問題的解獲得原問題的解。

分治法在問題較大且相互不獨立的狀況下,因爲分解獲得的子問題數目太多,各個遞歸子問題被重複計算屢次,求解過程呈冪級數增加,其時間複雜度爲 n 的指數時間。
與分治法不一樣,動態規劃方法採用自底向上的遞推方式求解,而且經分解獲得的子問題每每不是相互獨立的,根據子問題的相關性,在每步列出可能的局部解中選出能產生最佳解的部分,並將計算過程填表,只要某個子問題被解決, 將不會被屢次計算,從而減小了算法的時間複雜度。

求解問題時每次決策依賴於當前狀態,又隨即引發狀態的轉移,決策序列在變化的狀態中逐步產生,這種用多階段最優化決策方式解決問題的過程稱爲動態規劃。

遞推關係 (狀態轉移方程) 是實現由分解後的子問題向最終問題求解轉化的紐帶。 填表技術是把計算過的全部子問題的解都記錄下來,因爲將原問題分解後的各個子問題可能存在重複性,因此當之後重複遇到該子問題時,只須要查表繼續問題的求解,而不須要重複計算,這樣就節省了不少計算時間,這就是動態規劃的精髓。

動態規劃的設計分爲如下四個步驟:學習

  • 描述最優解結構
  • 遞歸定義最優解的值
  • 自底向上的方式計算最優解的值
  • 由計算出的結果構造一個最優解

動態規劃的基本要素(如何判斷一個問題能夠應用動態規劃方法的標誌)
1. 一個問題的最優解包含了子問題的一個最優解。優化

動態規劃方法的關鍵在於正確地寫出基本的遞推關係式和恰當的邊界條件。要作到這一點,就必須將原問題分解爲幾個相互聯繫的階段,恰當的選取狀態變量和決策變量及定義最優值函數,從而把一個大問題轉化成一組同類的子問題,而後逐個求解。即從邊界條件開始,逐階段遞推尋優,在每個子問題的求解中,均利用它前面的子問題的最優化結果,依次進行,最後一個子問題所得的最優解就是整個問題的最優解,這樣就說明具備最優子結構
  1. 子問題重疊。一個遞歸算法在其遞歸樹的不一樣分支中可能會屢次遇到同一個子問題。
當一個遞歸算法不斷地調用同一問題時,則該最優問題包含重疊子問題。
   `動態規劃`算法老是充分利用重疊子問題,即經過每一個子問題只解一次,把解保存在一個在須要時就能夠查看的`表`中。
   而`分治法`解決的問題每每在遞歸的每一步都產生全新的子問題,且對遞歸樹中重複出現的每一個子問題都要重複解一次。

動態規劃的時間複雜度:
依賴於兩個因素的乘積:1. 子問題的總個數 2. 每一個子問題有多少種選擇。
裝配線問題一共有O(n)個子問題,每一個問題2個選擇.故複雜度O^3
矩陣鏈乘法一共有O(n^2)個子問題,每一個問題n個選擇.故複雜度O^3spa


實際問題:
1. 裝配線調度
尋找最優解結構:
i=1時候問題的狀況;
i>1時候問題的狀況;
發現最優子結構能夠利用子問題的最優子結構構造原問題的一個最優解。
S1,jS1,j-1的關係;或者S2,j-1的關係。設計

自頂向下再自頂向上的遞歸方法時間複雜度達到O(2^n);而直接從自底向上,能夠線性時間解決問題。
2. 矩陣鏈乘法
問題描述:給定n個要相乘的矩陣構成的序列鏈<A1,A2,...,An>,要計算乘積A1,A2,...,An
可將兩個矩陣相乘的標準算法做爲一個子程序,根據括號給出的計算順序作出所有的矩陣乘法。不一樣的加所有括號順序所帶來的矩陣乘法的代價不一樣。
好比:
(A1,(A2(A3,A4)))
...
(A1(A2A3)A4)code

因此矩陣鏈乘法問題其實是,要求獲得一種最小化標量乘法次數的方式進行加所有括號。即肯定具備最小代價的矩陣相乘順序。該問題的最優子結構以下:假設 A1 A2...An 的一個最優加括號把乘積在 AkAk+1 間分開,則前綴子鏈 A1...Ak 的加括號方式一定爲 A1...Ak 的一個最優加括號,後綴子鏈同理。blog

設矩陣Ai的維數pi-1 * pi
圖片描述遞歸

使用自底向上的表格法來計算最優代價。

先貼一段這塊用到的兩個矩陣相乘的代碼:

//矩陣相乘
public class MatrixMultiply {
    // 矩陣的乘法
    public int[][] martirxMultiply(int A[][], int B[][]){
        int result [][] = new int [A.length][B[0].length];
        if(A[0].length != B.length){
            System.out.println("test");
            return result;
        }
        else{
            // result 的每一行
            for(int i=0;i<A.length;i++)
                // result 的每一列
                for(int j=0;j<B[0].length;j++)
                    // 對每一個元素的計算
                    for(int k = 0;k<A[0].length;k++)
                        result[i][j] = result[i][j] + A[i][k]*B[k][j];
        }
        return result;
    }

    public static void main(String[] args) {
        MatrixMultiply m = new MatrixMultiply();
        int[][] a={{1,2},{3,4},{5,6}};// 本身定義矩陣   
        int[][] b={{1,2,3},{4,5,6}};
        int [][] result = m.martirxMultiply(a, b);
        for(int i=0;i<a.length;i++)
            for(int j=0;j<b[0].length;j++)
                System.out.println(result[i][j]);
    }
}
相關文章
相關標籤/搜索