1. 程式人生 > >矩陣連乘--動態規劃演算法

矩陣連乘--動態規劃演算法

動態規劃(之前的揹包問題也是動態規劃的一種)
動態規劃演算法與分治法類似,其基本思想也是將待求解問題分解成若干個子問題,先求解子問題,然後從這些子問題的解得到原問題的解。
與分治法不同的是:適用於動態規劃法求解的問題,經分解得到的子問題往往不是互相獨立的。
目的:儲存已解決的子文題的答案,而在需要時再找出已求得的答案,這樣就可以避免大量的重複計算,從而得到多項式時間的演算法。
基本思路:用一個表來記錄所有已解決的子問題的答案。
常用範圍:通常用於求解具有某種最優性質的問題。
步驟如下:
(1)找出最優解的性質並刻畫其結構特徵
(2)遞迴的定義最優值
(3)以自底向上的方式計算出最優值
(4)根據計算最優值時得到的資訊,構造一個最優解。

矩陣連乘問題
問題概述
由於矩陣乘法滿足結合律,故計算矩陣的連乘積可以有許多不同的計算次序。而計算次序與計算量有著密切的關係,所以,我們的目的就是找出最優的計算次序,使得我們的計算量最少。

基礎知識
設A是一個p*q矩陣,B是一個q*r矩陣,那麼C=AB是一個p*r矩陣,從我們需要p*q*r次數乘

(1)分析最優解的結構
計算A[1:n],可以拆分成A[1:k]和A[k+1:n],然後將計算結果相乘,結果不變。
所以A[1:n]的計算了等於A[1:k]和A[k+1:n]的計算量之和再加上他們結果相乘的計算量的和。
關鍵特徵:A[1:n]的一個最優次序所包含的計算矩陣子鏈A[1:k]和A[k+1,n]的次序也是最優的。

最優子結構性質
一個問題的最優解包含著其子問題的最優解。
一個問題的最優子結構性質是該問題可用動態規劃演算法求解的顯著特徵。

建立遞迴關係
對於矩陣連乘積的最優計算次序問題,設計算A[i:j],所需的最少數乘次數時m[i][j]則元問題的最優值為m[1][n]。
當i=j時,A[i:j]=Ai為單一矩陣,無需計算,因此m[i][i]=0,i=1,2,3…n
m[i][j]=m[i][k]+m[k+1][j]+pr-1*pk*pj
k有j-1個位置,要在這j-1個位置中找出最適合的

計算最優值
在動態規劃中解決遞迴問題,可以採用自底向上的方式進行計算,保留已解決的子問題答案。

由MatrixChain可以計算出最優值
(1)i,j之間差1,先計算出[1,2] [2,3] [3,4]這些兩兩相鄰的矩陣
(2)i,j之間差2,然後計算[1,3] [2,4] [3,5]這些兩個兩個的矩陣,這樣就可以用上之前一個一個的結果了,並且k從第一個開始斷,檢測,拿[1,3]舉例子,[1,3]可以組成三個矩陣A,B,C那麼就有(AB)C或A(BC)這兩種不同的差別
(3)最終在(0,6)的位置上的就是所得的最小的計算量的值了

構造最優解
前面只是得出了最優解的值,但是沒有並沒有給出最優解
我們可以在計算最優解的同時,用一個矩陣s來記錄k(也就是從第幾個矩陣斷開)的取值,這樣就可以用Backtrack得出結論

程式碼

#include<iostream>
#include<iomanip>
#include<stack>
#include<queue>
using namespace std;

#include<malloc.h>
#include<string.h>
#include<stdlib.h>

int ** Get2Array(int n, int m)
{
    int **s = (int**)malloc(sizeof(int*)*n);
    for (int i = 0; i<n; ++i)
    {
        s[i] = (int*)malloc(sizeof(int)*m);
        memset(s[i], 0, sizeof(int)*m);
    }
    return s;
}

void Free2Array(int **p, int n)
{
    for (int i = 0; i<n; ++i)
    {
        free(p[i]);
    }
    free(p);
}

void Print_2Array(int **p, int n, int m)
{
    for (int i = 0; i<n; ++i)
    {
        for (int j = 0; j<m; ++j)
        {
            cout << setw(7) << p[i][j];
        }
        cout << endl;
    }
    cout << endl;
}

//////////////////////////////////////
void MatrixMul(int **a, int **b, int **c,//a*b=c
    int ra, int ca, int rb, int cb)
{
    if (ca != rb) return;
    for (int i = 0; i<ra; ++i)
    {
        for (int j = 0; j<cb; ++j)
        {
            int tmp = 0;
            for (int k = 0; k<ca; ++k)
            {
                tmp += a[i][k] * b[k][j];
            }
            c[i][j] = tmp;
        }
    }
}//矩陣相乘


void Backtrack(int **s, int i, int j)//s 1 6
{//關於1 到6 的最優解
    if (i < j)
    {
        Backtrack(s, i, s[i][j]);//遞迴1到s[1][6]//這裡儲存的是最後一個段點,先遞迴前面的
        Backtrack(s, s[i][j] + 1, j);
        cout << "Matrix A " << i << " , " << s[i][j] << " and A ";
        cout << s[i][j] + 1 << " , " << j << endl;
    }
}


int MatrixChain2(int *p, int n, int **m, int **s)//6個矩陣
{//                  p    6          m      s
    int i, j;
    for (i = 1; i <= n; ++i) m[i][i] = 0;//對角線上的元素都設定成0,1~6
    for (int len = 2; len <= n; ++len)//2~6 3
    {//從二開始斷12 23 34 45 56                                                                                                                                     
        for (i = 1; i <= n - len + 1; ++i)//1~5
        {//1 2
            j = i + len - 1;//1+2-1 = 2 3  2
            //1 2            2/3     2/3        0/1        1/2      2/3
            //1 3            2 3             0     1        3   
            m[i][j] = 0 + m[i + 1][j] + p[i - 1] * p[i] * p[j];//計算出m[1][2]就是1~2的值
            s[i][j] = i;//s[1][2] = 1
            for (int k = i + 1; k < j; ++k)//遍歷i,j之間j-1個位置,然後進行比較
            {//2                     3
                //        1 2         3    3        0         2    3   
                int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];
                if (m[i][j] > t)
                {
                    m[i][j] = t;
                    s[i][j] = k;
                }
            }
        }
        Print_2Array(m, n + 1, n + 1);
    }
    return m[1][n];
}


int main()
{
    const int n = 6;
    int p[n + 1] = { 30,35,15,5,10,20,25 };//30*35 35*15 15*5 5*10 10*20 20*25
    int **m = Get2Array(n + 1, n + 1);//7*7 0~6
    int **s = Get2Array(n + 1, n + 1);
    int min = MatrixChain2(p, n, m, s);
    cout << "Min: " << min << endl;
    Print_2Array(m, n + 1, n + 1);
    Print_2Array(s, n + 1, n + 1);

    Backtrack(s, 1, n);
    return 0;
}