动态规划:矩阵连乘问题

来源:互联网 发布:java 读取hdfs文件 编辑:程序博客网 时间:2024/06/05 20:42

问题描述

给定n个矩阵{A1,A2,A3,,An}, 其中AiAi+1是可乘的, i = 1, 2, , n-1.考察这n个矩阵的连乘积A1A2,,An.由于矩阵乘法满足结合律,所以计算矩阵的连乘积可有多种不同的计算次序,而不同的计算次序有不同的计算量。设计算法求得多个矩阵连乘时的最小计算量。

解决方案

  1. 分析最优解的性质 (thinking top-dwon)
    为方便起见,将矩阵连乘积AiAi+1Aj简记为A[i:j]。考察计算A[1:n]的最优计算次序。设这个计算次序在矩阵AkAk+1之间将矩阵链断开, 1 <= k < n, 则其相应的完全加括号方式为((A1AK)(Ak+1An))。依此次序,先计算A[1:k]和A[k+1:n],然后将计算结果相乘得到A[1:n],依此计算顺序总计算量为A[1:k]的计算量加上A[k+1:n]的计算量,再加上A[1:k]和A[k+1:n]相乘的计算量。
    关键特征:计算A[1:n]的最优次序所包含的计算矩阵子链A[1:k]和A[k+1:n]的次序也是最优的。即满足最优子结构性质。
  2. 建立递归关系
    对于矩阵连乘积的最优计算次序问题,设计算A[i:j],1<=i<=j<=n,所需的最少数乘次数为m[i][j],则原问题的最优值为m[1][n]。可以将m[i][j]递归的定义为
    m[i][j]={0min{m[i][k]+m[k+1][j]+Pi1PkPj}i = ji < ji<=k<j

    m[i][j]给出了最优值,即计算A[i:j]所需最少数乘次数。同时还确定了计算A[i:j]的最优次序中的断开位置k,也就是说,对于这个k有
    m[i][j]=m[i][k]+m[k+1][j]+Pi1PkPj

    若将对应于m[i][j]的断开位置k记为s[i][j],在计算出最优值m[i][j]后,可递归地由s[i][j]构造出相应的最优解。
  3. 计算最优值(solving bottom-up)
    由于该问题满足子问题重叠的性质,如果采用递归方式求解,将大量重复计算,因此需要自底向上进行求解。在计算过程中,保存已解决的子问题答案。每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量重复计算,最终得到多项式时间的算法。用数组p表示矩阵的维数,用二维数组m来保存最优解,用二维数组s记录最优断开位置k。
  4. 构造最优解
    上面给出了计算最优解的递推式,可以通过查表m[1][n]即可得到最优值。如果需要构造最优解,需要利用二维数组s中保存的信息,即矩阵链断开的位置。具体算法在下面的Traceback函数中给出。

实现代码

/***动态规划——矩阵连乘*/#include <stdio.h>#include <stdlib.h>#define n 7void MartixChain(int p[], int m[][n], int s[][n]);void Traceback(int i, int j, int s[][n]);int main(){    int p[n] = {30, 35, 15, 5, 10, 20, 25};    int m[n][n];    int s[n][n];    MartixChain(p, m, s);    Traceback(1, 6, s);    return 0;}void MartixChain(int p[], int m[][n], int s[][n]){ //计算最优值    int i, r, j, k;    for (i = 1; i <= n - 1; i++)        m[i][i] = 0;    for (r = 2; r <= n - 1; r++)    {        for (i = 1; i <= n - r; i++)        {            j = i + r - 1;            m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];     //k=i            s[i][j] = i;            for (k = i + 1; k < j; k++)            { //找出最小值                int t = m[i][k] + m[k + 1][j] + p[i - 1] * p[k] * p[j];                if (t < m[i][j])                {                    m[i][j] = t;                    s[i][j] = k;                }            }        }    }}void Traceback(int i, int j, int s[][n]){ //构造最优解    if (i == j)        return;    Traceback(i, s[i][j], s);    Traceback(s[i][j] + 1, j, s);    printf("Multiply A %d, %d", i, s[i][j]);    printf("and A %d, %d\n", s[i][j] + 1, j);}
0 0
原创粉丝点击