问题描述:给定n个矩阵{A1A2…An},其中Ai和Ai+1是可乘的,考察这n个矩阵的连乘积A1A2…An。由于矩阵的乘法满足结合律,故计算矩阵的连乘积有许多不同的计算次序,而不同的计算次序,所需要计算的连乘次数也是不同的,求解连乘次数最少的矩阵连乘最优次序。
举例说明矩阵结合方式对数乘次数的影响:
矩阵连乘积A1A2A3,3个矩阵的维数分别为10*100,100*5和5*50,连乘时加括号的方式有:
((A1*A2)*A3) 数乘次数:10*100*5+10*5*50=7500
(A1*(A2*A3)) 数乘次数:100*5*50+10*100*50=75000
由此可见,不同的加括号方式计算次数差距竟然如此之大!所以要想快速解决问题就需要找到最优次序。
方法一:穷举搜索法
列举出矩阵连乘所有的加括号方式,然后计算出每种加括号方式的计算次数,找到计算次数最少的加括号方式,然后进行矩阵连乘。
方法二:动态规划法
按照动态规划法处理问题的步骤:
1.分析最优解的结构:
记A[1:n] = A1A2…An。计算A[i:j]的最优次序时,可以将矩阵链断开分为两部分A[i:k]和A[k+1:j],其中i <= k < j(由于第二个子链以k+1开始,所以k不能等于j),即A[i:j] = A[i:k]*A[k+1:j],这时候:
A[i:j]的数乘次数 = A[i:k]的数乘次数 + A[k+1:j]的数乘次数 + p[i-1]p[k]p[j];
注意:Ai矩阵的维数为pi-1和pi。
p[i-1]p[k]p[j]为两个矩阵M1*M2的数乘次数。(M1=A[i:k],M2=A[k+1:j])
显然此时A[i:k]和A[k+1:j]也是最优次序。
如果A[i:k]不是最优次序,那么存在最优次序A1[i:k],那么此时A[i:j]的数乘次数将少于A[i:j]最有次序的计算量,这是矛盾的,所以A[i:k]子链是最优次序,同理A[k+1:j]也是最优次序。
因此,矩阵连乘问题的最优次序包含着其子问题的最优次序,这种性质就是最优子结构性质。
2.建立最优值的递归关系
对于矩阵连乘积的最优计算次序问题,设计算A[i:j]所需的最少数乘次数为m[i][j],则原问题的最优值为m[1][n].
当i=j时,A[i:j]为单一矩阵,无需计算,因此m[i][i] = 0, i = 1,2,3…n
当i
m[i][j] = m[i][k] + m[k+1][j] + p[i-1]p[k]p[j];
所以,最优值可以递归的定义为:
3.计算最优值
下面给出计算最优值的动态规划算法,其中连乘矩阵的维数存放在数组p中,p={p1,p2,p3,……},最优值存放在数组m中,最优次序的断开位置存放在数组s中:
View Code
迭代的执行过程,R控制子问题的规模,直至达原问题
当R=2时,迭代计算出:
m[1:2]=m[1:1]+m[2:2}+p[0]*p[1]*p[2];
m[2:3]=m[2:2]+m[3:3]+p[1]*p[2]*p[3];
m[3:4]=m[3:3]+m[4][4]+p[2]*p[3]*p[4];
m[4:5]=m[4:4]+m[5][5]+p[3]*p[4]*p[5];
m[5:6]=m[5][5]+m[6][6]+p[4]*p[5]*p[6];
当R=3时,迭代计算出:
m[1:3]=min(m[1:1]+m[2:3]+p[0]*p[1]*p[3],m[1:2]+m[3:3]+p[0]*p[2]*p[3]);
m[2:4]=min(m[2:2]+m[3:4]+p[1]*p[2]*p[4],m[2:3]+m[4:4]+p[1]*p[3]*p[4]);
......
m[4:6]=min(m[4:4]+m[5:6]+p[3]*p[4]*p[6],m[4:5]+m[6:6]+p[3]*p[5]*p[6]);
......
4.由最优值构造最优解
上述算法只是计算出了最优值,并未给出最优解,也就是说现在只是知道了最小数乘次数,并不知道矩阵连乘的乘法次序。
MatrixChain算法中的t=s[i][j]表明,计算矩阵链A[i:j]的最佳方式应该在矩阵At和At+1之间断开,即A[i:j]=A[i:t]*A[t+1:j]。所以,s[1][n]记录的信息可知A[1:n]的最优加括号方式为(A[1:s[1][n]])*(A[s[1][n]+1:j]),而A[1:s[1][n]]的最优加括号方式为(A[1:s[1][s[1][n]]])*(A[s[1][s[1][n]]+1:s[1][n]]),以此类推。
View Code
动态规划算法完整代码见文章末尾。
方法三:备忘录方法
备忘录方法是动态规划算法的变形,与动态规划算法一样,备忘录算法用表格保存已解决的子问题的答案,在下次需要解此问题时只是简单地查看该问题的答案,而不必重新计算。与动态规划算法不同的是,备忘录算法的递归方式是自顶向下的,而动态规划算法是自底向上的。
备忘录方法为每个子问题建立一个记录项,初始化时,该记录项存入一个特殊的值,表示该子问题尚未求解,对每个待求的子问题,首先查看其记录项。若记录项是原始值,则代表该问题是第一次遇到,计算该问题的值并保存;若记录项中存储的不是初始化时的特殊值,则表示子问题已经被计算过,此时只要从记录项中取出该子问题的解答即可,不必重新计算。
View Code
备忘录算法完整代码见文章末尾。
通过对求解最优值的两种算法分析可以看出,备忘录方法通过递归自顶向下由原问题慢慢分解为子问题,然后求解下层的子问题,最后返回到顶层原问题,在这个过程中只去计算与原问题有关的子问题,即递归调用的子问题;而动态规划算法自底向上,先去将所有的子问题解决,然后一步步扩大子问题的规模,直至到原问题,然后去掉用解决原问题是需要用到的子问题答案。
附一:
View Code
附二:
View Code