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

動態規劃 - 矩陣鏈乘法

limit nbsp div 繼續 clu 其中 matrix 分開 int

前言:今天接著學習動態規劃算法,學習如何用動態規劃來分析解決矩陣鏈乘問題。首先回顧一下矩陣乘法運算法,並給出C++語言實現過程。然後采用動態規劃算法分析矩陣鏈乘問題並給出C語言實現過程。

1、矩陣乘法  技術分享圖片         從定義可以看出:只有當矩陣A的列數與矩陣B的行數相等時A×B才有意義。一個m×r的矩陣A左乘一個r×n的矩陣B,會得到一個m×n的矩陣C。在計算機中,一個矩陣說穿了就是一個二維數組。一個m行r列的矩陣可以乘以一個r行n列的矩陣,得到的結果是一個m行n列的矩陣,其中的第i行第j列位置上的數等於前一個矩陣第i行上的r個數與後一個矩陣第j列上的r個數對應相乘後所有r個乘積的和。采用C++語言實現完整的兩個矩陣乘法,程序如下所示:
#include<iostream>
#include<cstdlib>
using namespace std;

#define A_ROWS        3
#define A_COLUMNS     2
#define B_ROWS        2
#define B_COLUMNS     3

void matrix_multiply(int A[A_ROWS][A_COLUMNS],int B[B_ROWS][B_COLUMNS],int C[A_ROWS][B_COLUMNS])
{
    if(A_COLUMNS!=B_ROWS)
    {
        cout<<"incompatible dimensions"<<endl;
        exit(1);
    }
    int i,j,k;
    for(i=0;i<A_ROWS;i++)
        for(j=0;j<B_COLUMNS;j++)
    {
        C[i][j]=0;
        for(k=0;k<A_COLUMNS;k++)
            C[i][j]+=A[i][k]*B[k][j];
    }
}

int main()
{
    int C[A_ROWS][B_COLUMNS];
    int A[A_ROWS][A_COLUMNS]={{1,2},{3,4},{5,6}};
    int B[B_ROWS][B_COLUMNS]={1,2,3,4,5,6};
    matrix_multiply(A,B,C);
    int i,j;
    for(i=0;i<A_ROWS;i++)
    {
        for(j=0;j<B_COLUMNS;j++)
            cout<<C[i][j]<<" ";
        cout<<endl;
    }
}

技術分享圖片

2、矩陣鏈乘問題描述

  給定n個矩陣構成的一個鏈<A1,A2,A3,.......An>,其中i=1,2,...n,矩陣A的維數為pi-1pi,對乘積 A1A2...An 以一種最小化標量乘法次數的方式進行加全部括號。

  註意:在矩陣鏈乘問題中,實際上並沒有把矩陣相乘,目的是確定一個具有最小代價的矩陣相乘順序。找出這樣一個結合順序使得相乘的代價最低。

3、動態規劃分析過程

1)最優加全部括號的結構

  動態規劃第一步是尋找一個最優的子結構。假設現在要計算AiAi+1....Aj的值,計算Ai...j過程當中肯定會存在某個k值(i<=k<j)將Ai...j分成兩部分,使得Ai...j

的計算量最小。分成兩個子問題Ai...k和Ak+1...j,需要繼續遞歸尋找這兩個子問題的最優解。

  有分析可以到最優子結構為:假設AiAi+1....Aj的一個最優加全括號把乘積在Ak和Ak+1之間分開,則Ai..k和Ak+1..j也都是最優加全括號的。

2)一個遞歸解

  設m[i,j]為計算機矩陣Ai...j所需的標量乘法運算次數的最小值,對此計算A1..n的最小代價就是m[1,n]。現在需要來遞歸定義m[i,j],分兩種情況進行討論如下:

當i==j時:m[i,j] = 0,(此時只包含一個矩陣)

當i<j 時:從步驟1中需要尋找一個k(i≤k<j)值,使得m[i,j] =min{m[i,k]+m[k+1,j]+pi-1

pkpj} (i≤k<j)。

3)計算最優代價

  雖然給出了遞歸解的過程,但是在實現的時候不采用遞歸實現,而是借助輔助空間,使用自底向上的表格進行實現。設矩陣Ai的維數為pi-1pi,i=1,2.....n。輸入序列為:p=<p0,p1,...pn>,length[p] = n+1。使用m[n][n]保存m[i,j]的代價,s[n][n]保存計算m[i,j]時取得最優代價處k的值,最後可以用s中的記錄構造一個最優解。書中給出了計算過程的偽代碼,摘錄如下:

MAXTRIX_CHAIN_ORDER(p)
  n = length[p]-1;
  for i=1 to n
      do m[i][i] = 0;
  for t = 2 to n  //t is the chain length
       do for i=1 to n-t+1
                     j=i+t-1;
                     m[i][j] = MAXLIMIT;
                     for k=i to j-1
                            q = m[i][k] + m[k+1][i] + qi-1qkqj;
                            if q < m[i][j]
                               then m[i][j] = q;
                                    s[i][j] = k;
  return m and s;

C++代碼:

#include<iostream>
using namespace std;
#define N 6
#define MAXVALUE 100000000
void matrix_chain_order(int *p,int m[N+1][N+1],int s[N+1][N+1])
{
    int i,j,l,k;
    for(i=1;i<=N;i++)
        m[i][i]=0;
    for(l=2;l<=N;l++)
    {
        for(i=1;i<=N-l+1;i++)
        {
            j=i+l-1;
            m[i][j]=MAXVALUE;
            for(k=i;k<=j-1;k++)
            {
                int temp=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
                if(temp<m[i][j])
                {
                    m[i][j]=temp;
                    s[i][j]=k;
                }
            }
        }
    }
}

void print_optimal_parens(int s[N+1][N+1],int i,int j)
{
    if(i==j)
        cout<<"A"<<i;
    else
    {
        cout<<"(";
        print_optimal_parens(s,i,s[i][j]);
        print_optimal_parens(s,s[i][j]+1,j);
        cout<<")";
    }
}

int main()
{
    int p[N+1] = {30,35,15,5,10,20,25};
    int m[N+1][N+1]={0};
    int s[N+1][N+1]={0};
    int i,j;
    matrix_chain_order(p,m,s);
    cout<<"m value is: "<<endl;
    for(i=1;i<=N;++i)
    {
        for(j=1;j<=N;++j)
            cout<<m[i][j]<<" ";
        cout<<endl;
    }
    cout<<"s value is: "<<endl;
    for(i=1;i<=N;++i)
    {
        for(j=1;j<=N;++j)
            cout<<s[i][j]<<" ";
        cout<<endl;
    }
    cout<<"The result is:"<<endl;
    print_optimal_parens(s,1,N);
    return 0;
}

技術分享圖片

動態規劃 - 矩陣鏈乘法