今天来讲一下矩阵的连乘问题。在计算机科学中,矩阵链乘法问题是一个经典的动态规划问题,它涉及到如何高效地计算一系列矩阵的乘积。这个问题在算法设计和编译原理中有着广泛的应用。本文将介绍矩阵链乘法问题的基本概念,并通过Java语言实现一个求解该问题的程序。
一、首先是问题描述
由不同加括号顺序所带来的矩阵乘积的代价不同,考虑三个矩阵的链(A1,A2,A3)的问题。假设三个矩阵的维数分别为10*100,100*5,5*50。如果按((A1A2)A3)规定的次序来做乘法,求105的矩阵乘积A1A2要做10*100*5=5000次的标量乘法运算,再乘上A3还要做10*5*50=2500次标量乘法,总共7500次标量乘法运算。如果按(A1(A2A3))的次序来计算,则为求10050的矩阵乘积A2A3要做100*5*50=25000次标量乘法运算,再乘上A1还要10*100*50=50000次标量乘法,总共75000次标量乘法运算。因此,按第一种运算次序进行计算就要快到10倍。
矩阵链乘法问题可表述如下:给定n个矩阵{A1,A2,…,An},其中,Ai与Ai+1是可乘的,(i=1,2 ,…,n-1)。用加括号的方法表示矩阵连乘的次序,不同的计算次序计算量(乘法次数)是不同的,找出一种加括号的方法,使得矩阵连乘的次数最小。
输入:
输入一个n表示有n个矩阵
第二行输入n+1个数字,其中第i个数字和第i+1
输出:
输入一个n表示有n个矩阵
第二行输入n+1个数字,其中第i个数字和第i+1
话不多说先上代码:
import java.util.Scanner;
public class Main {
static final int MAXN = 105;//题目所求
static final int INF = 0x3f3f3f3f;//初始化无穷数,用来初始化乘法次数
static int[] p = new int[MAXN];//存矩阵
static int[][] m = new int[MAXN][MAXN];//存计算次数
public static void main(String[] args) {
int n;
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int i = 0; i <= n; i++) {
p[i] = sc.nextInt();
}
System.out.println(Maxchain(n));
sc.close();
}
public static int Maxchain(int n) {
// 对角线清零
for (int i = 0; i < MAXN; i++) {
m[i][i] = 0;
}
for (int d = 2; d <= n; d++) {//区间大小
for (int i = 1; i <= n - d + 1; i++) {
int j = i + d - 1;
m[i][j] = INF;
for (int k = i; k < j; 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;
}
}
}
}
return m[1][n];
}
}
如果实在看不懂的老哥可以去看看这个老哥写的矩阵连乘原理的详解,写的挺好的:
https://blog.csdn.net/qq_19782019/article/details/94356886
二、思路
1、首先是最基本的求解公式。
2、分别定义一个一位数组p[]和一个二维数组m[][]分别用来存储矩阵和乘法次数。
static final int MAXN = 105;//题目所求
static final int INF = 0x3f3f3f3f;//初始化无穷数,用来初始化乘法次数
static int[] p = new int[MAXN];//存矩阵
static int[][] m = new int[MAXN][MAXN];//存计算次数
3、主体部分,直接读入矩阵个数n和并将矩阵写入p[]中,然后通过Maxchain()方法求得乘法次数。
public static void main(String[] args) {
int n;
Scanner sc = new Scanner(System.in);
n = sc.nextInt();//读入矩阵个数
for (int i = 0; i <= n; i++) {
p[i] = sc.nextInt();//循环读入矩阵
}
System.out.println(Maxchain(n));//求解并输出
sc.close();
}
3、将公式变为方法:
1)、将对角线上的乘法次数清零,因为m[i][j](i=j)在意义上是自己到自己的乘法,举个例子就是矩阵A2到A2,这是没有意义的。对应的是公式的第一部分
2)、公式的第二部分。举个例子,假如n=4,即对应4个矩阵。我们公式是自下而上进行求解的。
(偷一下老哥的图)
我们一层一层来,首先是最底下的一层,全为0,就是刚才说的对角线,对应的是1-1,2-2,3-3,4-4。然后是倒数第二层,对应的是1-2,2-3,3-4.然后再往上一层对应1-3,2-4.然后最后一层就是我们所求,对应1-4。发现没有,这其实就是一个区间逐渐递增的循环。我们可以用一个d代表这个区间,从倒数第二层开始计算1-2,2-3,3-4这些对应区间为2,上限1-4对应区间为4。
然后我们可以根据这些区间进行设计循环
首先是i的上限,d最大时为4,而区间=2时,最大值为3-4,由此得出i<=n-d+1,j随i根据区间进行变化,根据上面的结论可以推出j=i+d-1,然后将二维数组m[i][j]单元初始化为无穷。因为我们所求的值为最小值,这样才能正确求出结果。
然后根据公式穷举k在i-j范围的值,当当前乘法次数小于数组中的值时进行替换,最后得到一个记录有完整乘法次数数据的数组m,根据我们的需要返回相应的m[1][4],即m[1][n].
public static int Maxchain(int n) {
// 对角线清零
for (int i = 0; i < MAXN; i++) {
m[i][i] = 0;
}
for (int d = 2; d <= n; d++) {//区间大小
for (int i = 1; i <= n - d + 1; i++) {
int j = i + d - 1;
m[i][j] = INF;
for (int k = i; k < j; 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;
}
}
}
}
return m[1][n];
}
}
由此我们便构造出求解方法Maxchain()。
希望这篇文章能帮助大家更好地理解矩阵链乘法问题及其解决方案。如果你有任何问题或建议,欢迎在评论区讨论。