矩阵连乘问题讲课稿
矩阵连乘拟合思路

矩阵连乘拟合思路矩阵连乘拟合是一种常见的数学问题,它在计算机科学、统计学和机器学习等领域都有广泛应用。
本文将介绍矩阵连乘拟合的思路和算法,并通过实例来说明其应用。
矩阵连乘问题可以简单描述为:给定n个矩阵A1, A2, ..., An,其中Ai的维度为pi-1 × pi(i=1,2,...,n),求如何将这些矩阵连乘起来,使得乘法的计算次数最少。
为了解决这个问题,我们可以使用动态规划的方法。
首先定义一个n×n的二维数组m,其中m[i][j]表示从矩阵Ai乘到矩阵Aj所需的最少计算次数。
接下来,我们需要找到一个划分点k,将Ai × ... × Aj划分为(Ai × ... × Ak) × (Ak+1 × ... × Aj)。
根据划分点的选择,我们可以得到如下的递推关系式:m[i][j] = min{m[i][k] + m[k+1][j] + pi-1 × pk × pj} (i ≤ k < j)这个递推关系式表示,在计算Ai × ... × Aj时,我们先计算Ai × ... × Ak和Ak+1 × ... × Aj,然后再进行一次矩阵乘法,这样的计算次数最少。
根据这个递推关系式,我们可以使用动态规划的方法来计算m[i][j]的值。
首先,我们计算出所有m[i][i](1 ≤ i ≤ n)的值,这些值都为0,因为乘以一个矩阵的次数为0。
然后,我们按照从小到大的顺序计算出所有m[i][j](1 ≤ i < j ≤ n)的值,最终得到m[1][n]的最小计算次数。
在实际应用中,矩阵连乘拟合问题可以用于优化矩阵乘法的计算效率。
例如,在神经网络中,矩阵乘法是一个非常常见的操作,而且计算量通常很大。
通过矩阵连乘拟合,我们可以确定最优的矩阵乘法顺序,从而减少计算次数,提高运算效率。
矩阵连乘问题(动态规划算法)

矩阵连乘问题(动态规划算法)动态规划算法思想简介:将⼀个问题分解为多个⼦问题,这点和分治法类似,但是每个⼦问题不是独⽴的⽽是相互联系的,所以我们在求解每个⼦问题的时候可能需要重复计算到其他的⼦问题,所以我们将计算过的⼦问题的解放进⼀个表中,这样就能避免了重复计算带来的耗费,这就是动态规划的基本思想;⼀般地,动态规划思想⼀般⽤来解最优化问题,主要分为以下四个步骤:(1)找出最优解的性质,并刻画其结构特征;(2)递归地定义最优值;(3)以⾃底向上的⽅式计算出最优值;(4)根据计算得到的最优值时得到的信息,构造最优解;同时,问题的最优⼦结构性质也是该问题可⽤动态规划算法求解的显著特征,这⾥的最优⼦结构性质即指:问题的最优解也即代表着它的⼦问题有了最优解;问题描述:分析过程如下:(1)分析最优⼦结构的性质:(2)分析递归关系,以及利⽤⾃底向上的⽅式进⾏计算:(3)获取最优值和最优解:代码如下:#ifndef MATRIX_CHAIN_H#define MATRIX_CHAIN_Hvoid matrix_chain(int *p, int n, int **m, int **s);void traceback(int i, int j, int **s);#endif#include <iostream>#include "matrix_chain.h"using namespace std;//利⽤动态规划算法获取最优值void matrix_chain(int *p, int n, int **m, int **s) //p:各个矩阵的列数,n:矩阵个数,m:m[i:j]矩阵i到j的相乘次数,s:对应的分开位置{for (int i = 0; i < n; i++){m[i][i] = 0;}for (int r = 2; r <= n; r++){for (int i = 0; i < n - r + 1; i++){int j = i + r - 1;m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];s[i][j] = i;for (int 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;}}}}}//利⽤s[i][j]获取最优解void traceback(int i, int j, int **s){if (i == j)return;traceback(i, s[i][j], s);traceback(s[i][j] + 1, j, s);cout << "Multiply A" << i << " , " << s[i][j];cout << "and A" << (s[i][j] + 1) << " , " << j << endl;}#include <iostream>#include "matrix_chain.h"using namespace std;int main(void){int matrix_num = 0; //矩阵个数cout << "请输⼊矩阵个数:" << endl;cin >> matrix_num;int **m = new int *[matrix_num];for (int i = 0; i < matrix_num; i++)m[i] = new int[matrix_num];int **s = new int *[matrix_num];for (int i = 0; i < matrix_num; i++)s[i] = new int[matrix_num];int *p = new int[matrix_num];cout << "请输⼊各矩阵的列数:" << endl;for (int i = 0; i < matrix_num; i++){cin >> p[i];}matrix_chain(p, matrix_num, m, s);traceback(0, matrix_num - 1, s);system("pause");return1;}可结合我的另⼀篇关于贪⼼算法的博客进⾏⽐较,了解这两者的区别;。
矩阵连乘最优结合问题

矩阵连乘最优结合问题
【实用版】
目录
1.矩阵连乘最优结合问题的定义
2.矩阵连乘最优结合问题的求解方法
3.矩阵连乘最优结合问题的实际应用
正文
矩阵连乘最优结合问题是指在给定一组矩阵的情况下,通过选择最优的矩阵连乘顺序,使得连乘结果的误差最小或者效果最优。
这个问题在很多实际应用中都会出现,比如图像处理、信号处理等。
求解矩阵连乘最优结合问题的方法有很多,其中一种常见的方法是使用贪心算法。
贪心算法的思路是每一步都选择当前看起来最优的选择,直到解决问题。
在这问题中,我们可以从第一个矩阵开始,每一步都选择下一个矩阵,使得连乘结果的误差最小。
具体操作时,我们可以用一个函数来计算当前连乘结果的误差,然后选择误差最小的矩阵作为下一个连乘的矩阵,直到所有矩阵都被连乘完。
矩阵连乘最优结合问题的实际应用非常广泛。
在图像处理中,矩阵连乘可以用来实现图像的变换、滤波等操作,而矩阵连乘最优结合问题可以帮助我们选择最优的变换或滤波顺序,从而得到最好的图像效果。
第1页共1页。
矩阵连乘问题

矩阵连乘问题目录:矩阵连乘问题:1. 描述矩阵连乘问题2. 分析矩阵连乘问题以及对递归式的推导(1)直接递归思路(2)备忘录思路(3)动态规划思路3. 伪代码的方式描述算法:(1)直接递归算法(2)备忘录算法(3)动态规划算法4. 把算法转换成程序实现的过程及结果(1)直接递归算法程序(2)备忘录算法程序(3)动态规划算法程序1.描述矩阵连乘问题:,其中i A和1+i A是可乘的,给定n个矩阵{n AAA⋯,2,1}i=1,2,…,n-1。
考察这n个矩阵的连乘积n AAA⋯,1。
,2由于矩阵乘法具有结合律,故计算矩阵的连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式来确定。
若一个矩阵连乘积的计算次序完全确定,也就是说连乘积已完全加括号,则可依次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
完全加括号的矩阵连乘可递归地定义为:(1)单个矩阵是完全加括号的;(2)矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘B和C的乘积并加括号,即A=(BC)。
矩阵A和B可乘的条件是矩阵A的列数等于矩阵B的行数。
若A是一个p×q的矩阵,B 是一个q×r的矩阵,那么C=A×B就是一个p ×r矩阵。
它的计算是三重循环的,计算量是pqr。
如果加括号后矩阵的量是不同的,所以我们的问题就是要讨论如何给连乘的矩阵加括号才能使矩阵的计算量最少。
穷举搜索法:对于n 个矩阵的连乘积,设有不同的计算次序P(n)。
由于可以先在第k 个和第k+1个矩阵之间将原矩阵序列分为两个矩阵子序列,k=1,2,...,n-1;然后分别对这两个矩阵子序列完全加括号;最后对所得的结果加括号,得到原矩阵序列的一种完全加括号方式。
由此可得P(n)的递归式如下:1 n=1 P (n )=∑-=-11)()(n k k n P k P n>1解此递归方程可得,P(n)=C(n-1),而C(n)是一个指数增长的函数。
动态规划法解矩阵连乘问题

动态规划法解矩阵连乘问题实验内容给定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]的列数)实验实验代码#in elude <iostream>#in elude <vector>using n amespaee std ;class matrix_cha in{public:matrix_eha in(const vector <int> & c){ cols = c ;count = cols.size (); mc.resize (co unt);s.resize (co unt);for (i nt i = 0; i < count; ++i) { mc[i].resize (co unt); s[i].resize (co unt);}for (i = 0; i < count; ++i) { for (int j = 0; j < count; ++j) { mc[i][j] = 0 ;s[i][j] = 0 ;//记录每次子问题的结果void lookup_cha in () {__lookup_cha in (1, count - 1);min_count = mc[1][co unt - 1];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, count - 1);}//使用普通方法进行计算void calculate () {int n = count - 1; //矩阵的个数// r表示每次宽度// i,j表示从从矩阵i到矩阵j// k表示切割位置for (i nt r = 2; r <= n; ++ r) {for (int i = 1; i <= n - r + 1; ++ i) {int j = i + r - 1 ;//从矩阵i到矩阵j连乘,从i的位置切割,前半部分为0mc[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 rmin_count = mc[1][ n];cout << "min _multi_co unt = "<< min_count << endl ;//输出最优计算次序__trackback (1, n);private:int __lookup_cha in (int i, i nt j) {//该最优解已求出,直接返回if (mc[i][j] > 0) {return mc[i][j];}if (i == j) {return 0 ; //不需要计算,直接返回}//下面两行计算从i到j按照顺序计算的情况int u = __lookup_cha in (i, i) + __lookup_cha in (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_cha in (i, k) + __lookup_cha in(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, i nt 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 ; // 矩阵个数+ 1vector<vector<int> > mc; //从第i个矩阵乘到第j个矩阵最小数乘次数vector<vector<int> > s; //最小数乘的切分位置int min_count ; //最小数乘次数};int mai n(){//初始化con st i nt MATRIX_COUNT = 6 ;vectorvi nt> 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_cha in me (c); // mc.calculate (); mc」o okup_cha in (); return 0 ;}实验结果实验验证从s 可知计算顺序为((A1(A2A3))((A4A5))A6))实验总结在这次实验中懂得了动态规划法运用方法和解题思路的重要性,在这个程序中如何 建立动态规划的过程建立递归过程 保存已解决的子问题答案。
算法设计与分析——矩阵连乘问题(动态规划)

算法设计与分析——矩阵连乘问题(动态规划)⼀、问题描述引出问题之前我们先来复习⼀下矩阵乘积的标准算法。
int ra,ca;//矩阵A的⾏数和列数int rb,cb;//矩阵B的⾏数和列数void matrixMultiply(){for(int i=0;i<ra;i++){for(int j=0;j<cb;j++){int sun=0;for(int k=0;k<=ca;k++){sum+=a[i][k]*b[k][j];}c[i][j]=sum;}}}给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
加括号的⽅式对计算量有很⼤的影响,于是⾃然地提出矩阵连乘的最优计算次序问题,即对于给定的相继n个矩阵,如何确定矩阵连乘的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
⼆、问题分析矩阵连乘也是Catalan数的⼀个常⽤的例⼦,关于时间复杂度的推算需要参考离散数学关于Catalan的内容。
下⾯考虑使⽤动态规划法解矩阵连乘积的最优计算次序问题。
1、分析最优解的结构问题的最优⼦结构性质是该问题可以⽤动态规划求解的显著特征!!!2、建⽴递归关系3、计算最优值public static void matrixChain(int n) {for (int i = 1; i <= n; i++) {m[i][i] = 0;}for (int r = 2; r <= n; r++) {//i与j的差值for (int i = 1; i <= n - r + 1; i++) {int j = i + r - 1;m[i][j] = m[i + 1][j] + p[i - 1] * p[i] * p[j];s[i][j] = i;for (int 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;}}}}}4、构造最优解public static void traceback(int i, int j) {if (i == j) {System.out.printf("A%d", i); // 输出是第⼏个数据return;}System.out.printf("(");traceback(i, s[i][j]);// 递归下⼀个数据System.out.printf(" x ");traceback(s[i][j] + 1, j);System.out.printf(")");}三、总结。
高中数学教案矩阵的乘法与应用
高中数学教案矩阵的乘法与应用高中数学教案:矩阵的乘法与应用高中数学作为学科中的一门重要课程,为学生提供了扎实的数学基础与解决实际问题的能力。
本教案将重点介绍矩阵的乘法与应用,帮助学生理解和掌握相关概念与技巧。
一、矩阵的乘法矩阵的乘法是矩阵运算中的重要内容,通过矩阵的乘法可以实现多个矩阵之间的运算和变换。
具体来说,设有两个矩阵A和B,它们的乘积记作AB,计算方法如下:1.1 定义设A是一个 m×n 的矩阵,B是一个 n×p 的矩阵,那么乘积AB是一个 m×p 的矩阵,其中乘积矩阵中的元素c(i,j)可表示为:c(i,j) = a(i,1)b(1,j) + a(i,2)b(2,j) + ... + a(i,n)b(n,j)1.2 注意事项在进行矩阵乘法时,需要注意以下几点:1) 两个矩阵相乘的前提是,第一个矩阵的列数与第二个矩阵的行数相等;2) 矩阵乘法不满足交换律,即AB不一定等于BA;3) 相乘的两个矩阵的对应元素必须满足相同的运算法则,通常为加法和乘法;二、矩阵的应用矩阵在数学中具有广泛的应用,尤其在线性代数、图论、概率统计等领域。
以下将简要介绍矩阵的几个常见应用。
2.1 线性变换矩阵可以用来表示线性变换,例如旋转、缩放、平移等。
通过对矩阵的乘法运算,可以实现对多个变换的叠加,从而达到复杂变换的目的。
2.2 线性方程组的求解矩阵可以应用于线性方程组的求解。
将线性方程组的系数矩阵和常数矩阵进行相乘,可以将方程组转化为矩阵的乘法运算,从而通过求解矩阵的逆矩阵或使用高斯消元法来解得方程组的解。
2.3 图论中的邻接矩阵在图论中,矩阵可以用于表示图的相关信息。
邻接矩阵是描述无向图或有向图的常用方法之一。
通过邻接矩阵的乘法,可以实现对图的遍历、路径搜索等操作。
2.4 概率统计中的转移矩阵转移矩阵是概率统计中常见的矩阵表示形式。
通过转移矩阵的乘法运算,可以描述系统在不同状态之间的转移概率,例如马尔可夫链、隐马尔可夫模型等。
矩阵连乘问题的算法
矩阵连乘问题的算法介绍矩阵连乘问题是一个经典的数学问题,它涉及到如何寻找一组矩阵相乘的最优顺序,使得计算所需的乘法操作总数最小化。
这个问题在计算机科学和算法设计中有着重要的应用。
本文将介绍矩阵连乘问题的算法及其相关概念和应用。
问题描述给定一组矩阵{A1, A2, A3, …, An},其中Ai的维度为pi-1 × pi(1 ≤ i ≤ n),我们希望找到一种矩阵相乘的顺序,使得计算这些矩阵相乘所需的乘法操作总数最小化。
动态规划算法动态规划算法是解决矩阵连乘问题的经典方法。
它通过存储中间结果来避免重复计算,从而提高计算效率。
下面将介绍动态规划算法的具体实现步骤。
定义子问题假设我们要计算矩阵Ai × Ai+1 × … × Aj的最优顺序和乘法操作总数,其中i ≤ j。
确定状态转移方程设m[i][j]表示计算矩阵Ai × Ai+1 × … × Aj的最优顺序和乘法操作总数。
根据定义,我们有以下状态转移方程: - 当i = j时,m[i][j] = 0,因为只有一个矩阵无需进行乘法操作; - 当i < j时,m[i][j] = min{m[i][k] + m[k+1][j] + pi-1 × pk × pj},其中i ≤ k < j。
填表计算最优值根据状态转移方程,我们可以使用动态规划的方法逐步填充表格m。
具体步骤如下:1. 初始化所有m[i][i]为0(0 ≤ i ≤ n); 2. 对于每个子问题(i, j),从i= 1递增到j = n-1,按照递增的长度进行计算: - 对于每个i和j,根据状态转移方程计算m[i][j]; 3. 最终,m[1][n-1]即为所求的计算矩阵Ai × Ai+1× … × An的最优顺序和乘法操作总数。
重构最优解为了得到最优顺序下的具体计算过程,我们可以使用一个辅助表格s来记录最优划分点。
n个矩阵连乘问题
矩阵连乘问题是一个经典的优化问题,其目标是在给定一组矩阵和它们之间的乘法顺序下,找出最少的括号方案数,使得乘法操作可以按照给定的顺序进行。
假设有n个矩阵A1, A2, ..., An,我们需要计算它们的连乘积。
每个矩阵Ai都有m×m的元素。
矩阵连乘问题可以转化为以下动态规划问题:
1. 定义dp[i][j]为计算矩阵Ai到Aj的连乘积所需的最少括号方案数。
2. 初始化dp[i][i]=0,表示单个矩阵不需要任何括号。
3. 对于i<j,计算dp[i][j]的递推关系:
dp[i][j] = dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j],其中k=i,...,j-1。
其中p是任意一个正整数,表示矩阵的维度m。
4. 最终答案为dp[1][n]。
以下是Python代码实现:
计算结果为:最少需要15个括号方案数。
动态规划之矩阵连乘
动态规划之矩阵连乘【问题描述】给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。
如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。
例如,给定三个连乘矩阵{A1,A2,A3}的维数分别是10*100,100*5和5*50,采⽤(A1A2)A3,乘法次数为10*100*5+10*5*50=7500次,⽽采⽤A1(A2A3),乘法次数为100*5*50+10*100*50=75000次乘法,显然,最好的次序是(A1A2)A3,乘法次数为7500次。
分析:矩阵链乘法问题描述:给定由n个矩阵构成的序列{A1,A2,...,An},对乘积A1A2...An,找到最⼩化乘法次数的加括号⽅法。
1)寻找最优⼦结构此问题最难的地⽅在于找到最优⼦结构。
对乘积A1A2...An的任意加括号⽅法都会将序列在某个地⽅分成两部分,也就是最后⼀次乘法计算的地⽅,我们将这个位置记为k,也就是说⾸先计算A1...Ak和Ak+1...An,然后再将这两部分的结果相乘。
最优⼦结构如下:假设A1A2...An的⼀个最优加括号把乘积在Ak和Ak+1间分开,则前缀⼦链A1...Ak的加括号⽅式必定为A1...Ak的⼀个最优加括号,后缀⼦链同理。
⼀开始并不知道k的确切位置,需要遍历所有位置以保证找到合适的k来分割乘积。
2)构造递归解设m[i,j]为矩阵链Ai...Aj的最优解的代价,则3)构建辅助表,解决重叠⼦问题从第⼆步的递归式可以发现解的过程中会有很多重叠⼦问题,可以⽤⼀个nXn维的辅助表m[n][n] s[n][n]分别表⽰最优乘积代价及其分割位置k 。
辅助表s[n][n]可以由2种⽅法构造,⼀种是⾃底向上填表构建,该⽅法要求按照递增的⽅式逐步填写⼦问题的解,也就是先计算长度为2的所有矩阵链的解,然后计算长度3的矩阵链,直到长度n;另⼀种是⾃顶向下填表的备忘录法,该⽅法将表的每个元素初始化为某特殊值(本问题中可以将最优乘积代价设置为⼀极⼤值),以表⽰待计算,在递归的过程中逐个填⼊遇到的⼦问题的解。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
矩阵连乘问题目录:矩阵连乘问题:1. 描述矩阵连乘问题2. 分析矩阵连乘问题以及对递归式的推导(1)直接递归思路(2)备忘录思路(3)动态规划思路3. 伪代码的方式描述算法:(1)直接递归算法(2)备忘录算法(3)动态规划算法4. 把算法转换成程序实现的过程及结果(1)直接递归算法程序(2)备忘录算法程序(3)动态规划算法程序1.描述矩阵连乘问题:给定n 个矩阵{n A A A ⋯,2,1},其中i A 和1+i A 是可乘的,i=1,2,…,n-1。
考察这n 个矩阵的连乘积n A A A ⋯,2,1。
由于矩阵乘法具有结合律,故计算矩阵的连乘积可以有许多不同的计算次序。
这种计算次序可以用加括号的方式来确定。
若一个矩阵连乘积的计算次序完全确定,也就是说连乘积已完全加括号,则可依次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积。
完全加括号的矩阵连乘可递归地定义为:(1)单个矩阵是完全加括号的;(2)矩阵连乘积A 是完全加括号的,则A 可表示为2个完全加括号的矩阵连乘B 和C 的乘积并加括号,即A=(BC )。
矩阵A 和B 可乘的条件是矩阵A 的列数等于矩阵B 的行数。
若A 是一个p ×q 的矩阵,B 是一个q ×r 的矩阵,那么C=A ×B 就是一个p ×r 矩阵。
它的计算是三重循环的,计算量是pqr 。
如果加括号后矩阵的量是不同的,所以我们的问题就是要讨论如何给连乘的矩阵加括号才能使矩阵的计算量最少。
穷举搜索法:对于n 个矩阵的连乘积,设有不同的计算次序P(n)。
由于可以先在第k 个和第k+1个矩阵之间将原矩阵序列分为两个矩阵子序列,k=1,2,...,n-1;然后分别对这两个矩阵子序列完全加括号;最后对所得的结果加括号,得到原矩阵序列的一种完全加括号方式。
由此可得P(n)的递归式如下: 1 n=1 P (n )=∑-=-11)()(n k k n P k P n>1解此递归方程可得,P(n)=C(n-1),而C(n)是一个指数增长的函数。
因此穷举搜索法不是一个有效的算法。
以下将用三种方法来解决矩阵连乘问题的最优加括号方式以及最优解。
2. 分析矩阵连乘问题以及对递归式的推导将矩阵连乘积j i i A A A ⋯+,1,简记为A[i:j]。
考察计算A[1:n]的最优计算次序。
这个问题的一个关键特征是:计算A[1:n]的最优次序包含的计算矩阵子链A[1:k]和A[k+1:n]的次序也是最优的。
这是因为:定义矩阵A i 的维数为p i-1×p i ,则A[i:k]的计算次数为p i-1×p k ,A[k+1,j]的计算次数为p k ×p j ,而这两个总的矩阵最后相乘时的计算量是固定的,为p i-1×p k ×p j 。
所以,矩阵连乘计算次序问题的最优解包含着其子问题的最优解。
这种性质称为最优子结构性质。
(1)、直接递归的思路:记计算A[i:j],1≤i ≤j ≤n ,所需最少数乘次数为m[i][j],则原问题的最优质为m[1][n]。
由分析得知:m[i][j]可以递归的定义为:0 i=jm[i][j]= }]][1[]][[{min 1j k i jk i p p p j k m k i m -≤≤+++ i<j m[i][j]给出了最优值,即计算A[i][j]所需的最少数乘次数。
同时还确定了计算A[i :j]的最优次序中的断开位置k ,也就是说,对于这个k 有m[i][j]=jk i p p p j k m k i m 1]][1[]][[-+++若将对应于m[i][j]的断开位置k 记s[i][j],在计算出最优值m[i][j]后,可递归地由s[i][j]构造出相应的最优解。
可以证明该算法的计算时间T(n)有指数下界,由算法的递归部分可得:O(1) n=1T(n)≥1+∑-=+-+11)1)()((n k k n T k T n>1因此,当n>1时,T(n)≥n+2∑-=11)(n k k T据此,可用数学归纳法证明T(n)≥2n-1=)2(n Ω。
直接递归法的计算时间随n 的增长指数增长。
(2)、备忘录方法的思路:备忘录方法为每个子问题建立一个记录项,初始化时,该记录项存入一个特殊的值,表示该问题尚未求解。
在求解过程中,对每个待求的子问题,首先查看其相应的记录项。
若记录项中存储的是初始化时存入的特殊值,则表示该问题第一次遇到,此时计算出该子问题的解,并保存在相应的记录项中,以备以后查看。
若记录项中存储的不是初始化存入的特殊值,(比如初始化为-1,解答后赋值为0),则表示该问题已被计算过,其相应的记录项中存储的应该是该子问题的解答。
此时,只要从记录相中取出该子问题的解答即可,而不必重新计算。
备忘录方法的计算量:因为是要计算m[i][j], 因此只要从n 个变量中任意选出2个分别作为i ,j ,则共有2n C 种选法,即有2n C 个子问题;当i=j 时有n 种选法,所以总的子问题就为:2n C +n=2)1(+n n 个。
每填入一个记录项,就要花费O (n )的时间,所以备忘录方法的时间复杂度为O(n 3)。
代表单个矩阵,所以m[i][i]=1.根据直接递归的方法的思路,如果要求m[i][j],就必须要求m[i][k]和m[k+1][j],根据m[i][j]的矩阵,则如果要求解m[1][2],则需要知道m[1][1]和m[1][2];如果要求解m[1][3],则要知道m[1][1]、m[1][2]和m[1][1]和m[2][3];以此类推。
通过此规律可以总结出要求某一个元动态规划算法的计算量主要取决于程序中对行、列和加括号的位置k的三重循环。
循环体内的计算量为O(1),而三重循环的总次数为O(n3)。
因此该算法的计算时间上界为O(n3)。
和备忘录的算法的时间复杂度一样,都比直接递归的穷举搜索法有效得多。
3. 伪代码的方式描述算法:(1)直接递归算法:int RecurMatrixChain(int i,int j){if(i==j) return 0;int u=RecurMatrixChain(i,i)+RecurMatrixChain(i+1,j)+p[i-1]*p[i]*p[j];//递归,p[ ]为维数s[i][j]=i;//记录加括号的位置for(int k=i+1;k<j;k++){int t=RecurMatrixChain(i,k)+RecurMatrixChain(k+1,j)+p[i-1]*p[k]*p[j];//递归if(t<u) {u=t; s[i][j]=k;}//判断哪个值更小,选取哪个}return 0;}(2)备忘录算法:int MemoizedMatrixChain(int n,int * * m,int * * s){for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)m[i][j]=0; //把m[i][j]的矩阵的上三角全部赋值为0。
return LookupChain(1,n);}int LookupChain(int i,int j){if(m[i][j]>0) return m[i][j]; //如果是已经解决的问题,则标记记录项m[i][j]已经有值,且大于0,避免重复计算。
if(i==j) return 0;int u=LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];//递归s[i][j]=i;for(int k=i+1;k<j;k++){int t=LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];//递归 if(t<u) {u=t; s[i][j]=k;} //找最小值}m[i][j]=u;return u;}(3)动态规划算法:void MatrixChain(int *p,int n,int * *m,int * *s){for(int i=1;i<=n;i++) m[i][i]=0;for(int r=2;r<=n;r++)for(int i=1;i<=n-r+1;i++){int j=i+r-1;//具体推导由来见下图m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];s[i][j]=i;for(int k=i+1;k<j;k++){int t=m[i][k]+[k+1][j]+p[i-1]*p[k]*p[j];if(t<m[i][j]){m[i][j]=t; s[i][j]=k;}//s[i][j]记录加括号的位置}}}下面的算法Traceback按算法MatrixChain计算出的断点矩阵s指示的加括方式输出计算A[i:j]的最优计算次序:void Traceback(int i,int j,int * *s){if(i==j) return;Traceback(i,s[i][j],s);//递归地对左边进行加括号Traceback(s[i][j]+1,j,s);//递归地对右边进行加括号cout<<"Multiply A"<<i<<"i"<<s[i][j];cout<<"and A"<<(s[i][j]+1)<<","<<j<<endl;//输出最优值和最优解。
}4.把算法转换成程序实现的过程及结果程序源代码:matrix.h#include <iostream>using namespace std;#define NUM 100int m[NUM][NUM],s[NUM][NUM],p[NUM];//m[][]代洙?表括?最?小?数簓乘?次?数簓的?矩?阵ó;?s[][]记?录?的?是?最?佳?加ó括ぁ?号?的?位?置?;?p[]表括?示?矩?阵ó的?维?数簓,A[i]的?维?数簓为ap[i-1]×áp[i].int n;//备?忘?录?方?法ぁ?int LookupChain(int i,int j){if (m[i][j]>0){return m[i][j];}if (i == j)return 0;int u=LookupChain(i, i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];s[i][j]=i;for (int k=i+1; k<j;k++){int t=LookupChain(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];if (t<u){u=t;s[i][j]=k;}}m[i][j]=u;return u;}int MemoizedMatrixChain(int n,int * * m,int * * s){for(int i=1;i<=n;i++)for(int j=i;j<=n;j++)m[i][j]=0; //把?m[i][j]的?矩?阵ó的?上?三▂角?全?部?赋3值μ为a0。