动态规划法解矩阵连乘问题
实验内容
给定n个矩阵{A1,A2,….An},其中Ai与Ai+1是可乘的,i=1,2,3。
,n-1。
我们要计算这n个矩阵的连乘积。
由于矩阵乘法满足结合性,故计算矩阵连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式确定。
若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则我们可依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
解题思路
将矩阵连乘积A(i)A(i+1)…A(j)简记为A[i:j],这里 i <= j。
考察计算A[i:j]的最优计算次序。
设这个计算次序在矩阵A(k)和A(k+1)之间将矩阵链断开,i <= k < j, 则其相应完全加括号方式为(A(i)A(i+1)…A(k)) * (A(k+1)A(k+2)…A(j))。
特征:计算A[i:j]的最优次序所包含的计算矩阵子链 A[i:k]和A[k+1:j]的次序也是最优的。
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。
设计算A[i:j],1 <= i <= j <= n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]
当i = j时,A[i:j]=Ai,因此,m[i,i] = 0,i = 1,2,…,n
当i < j时,m[i,j] = m[i,k] + m[k+1,j] + p(i-1)p(k)p(j)这里A(i)的维数为
p(i-1)*(i)(注:p(i-1)为矩阵A(i)的行数,p(i)为矩阵A[i]的列数)
实验
实验代码
#include <iostream>
#include <vector>
using namespace std ;
class matrix_chain
{
public:
matrix_chain(const vector<int> & c) {
cols = c ;
count = cols.size () ;
mc.resize (count) ;
s.resize (count) ;
for (int i = 0; i < count; ++i) {
mc[i].resize (count) ;
s[i].resize (count) ;
}
for (i = 0; i < count; ++i) {
for (int j = 0; j < count; ++j) {
mc[i][j] = 0 ;
s[i][j] = 0 ;
}
}
}
// 记录每次子问题的结果
void lookup_chain () {
__lookup_chain (1, count - 1) ;
min_count = mc[1][count - 1] ;
cout << "min_multi_count = "<< min_count << endl ;
// 输出最优计算次序
__trackback (1, count - 1) ;
}
// 使用普通方法进行计算
void calculate () {
int n = count - 1; // 矩阵的个数
// r 表示每次宽度
// i,j表示从从矩阵i到矩阵j
// k 表示切割位置
for (int r = 2; r <= n; ++ r) {
for (int i = 1; i <= n - r + 1; ++ i) {
int j = i + r - 1 ;
// 从矩阵i到矩阵j连乘,从i的位置切割,前半部分为0
mc[i][j] = mc[i+1][j] + cols[i-1] * cols[i] * cols[j] ;
s[i][j] = i ;
for (int k = i + 1; k < j; ++ k) {
int temp = mc[i][k] + mc[k + 1][j] +
cols[i-1] * cols[k] * cols[j] ;
if (temp < mc[i][j]) {
mc[i][j] = temp ;
s[i][j] = k ;
}
} // for k
} // for i
} // for r
min_count = mc[1][n] ;
cout << "min_multi_count = "<< min_count << endl ;
// 输出最优计算次序
__trackback (1, n) ;
}
private:
int __lookup_chain (int i, int j) {
// 该最优解已求出,直接返回
if (mc[i][j] > 0) {
return mc[i][j] ;
}
if (i == j) {
return 0 ; // 不需要计算,直接返回
}
// 下面两行计算从i到j按照顺序计算的情况
int u = __lookup_chain (i, i) + __lookup_chain (i + 1, j)
+ cols[i-1] * cols[i] * cols[j] ;
s[i][j] = i ;
for (int k = i + 1; k < j; ++ k) {
int temp = __lookup_chain(i, k) + __lookup_chain(k + 1, j)
+ cols[i - 1] * cols[k] * cols[j] ;
if (temp < u) {
u = temp ;
s[i][j] = k ;
}
}
mc[i][j] = u ;
return u ;
}
void __trackback (int i, int j) {
if (i == j) {
return ;
}
__trackback (i, s[i][j]) ;
__trackback (s[i][j] + 1, j) ;
cout <<i << "," << s[i][j] << " " << s[i][j] + 1 << "," << j << endl;
}
private:
vector<int> cols ; // 列数
int count ; // 矩阵个数+ 1
vector<vector<int> > mc; // 从第i个矩阵乘到第j个矩阵最小数乘次数vector<vector<int> > s; // 最小数乘的切分位置
int min_count ; // 最小数乘次数
} ;
int main()
{
// 初始化
const int MATRIX_COUNT = 6 ;
vector<int> c(MA TRIX_COUNT + 1) ;
c[0] = 30 ;
c[1] = 35 ;
c[2] = 15 ;
c[3] = 5 ;
c[4] = 10 ;
c[5] = 20 ;
c[6] = 25 ;
matrix_chain mc (c) ;
// mc.calculate () ;
mc.lookup_chain () ;
return 0 ;
}
实验结果
实验验证
连乘矩阵假如为:
计算过程为:
从m可知最小连乘次数为m[1][6] = 15125
从s可知计算顺序为((A1(A2A3))((A4A5))A6))
实验总结
在这次实验中懂得了动态规划法运用方法和解题思路的重要性,在这个程序中如何
建立动态规划的过程
建立递归过程
保存已解决的子问题答案。
每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法。
(注:文档可能无法思考全面,请浏览后下载,供参考。
可复制、编制,期待你的好评与关注)。