目录
Day 8:矩阵乘法的代码实现
Task:
- 三重循环是多数程序的极限.
- 非法输入检查是程序正常运行的基本保障. 如果检查所有的非法输入, 会导致大量代码行, 这在商业代码中是必须的.
一、矩阵乘法基础知识
矩阵计算在线性代数中已经有详细解释,这里为了方便理解代码逻辑,故单独挑出来在解释一下。
所谓矩阵乘法是两个具有一定关系的两个矩阵之间彼此行列之间关系的一种运算,一般来说,左乘矩阵的列数等于右乘矩阵的行数。然后运算时候左矩阵逐行提取分别与右乘矩阵的逐列相乘再相加。口述空洞,我们用图和表达式来说明:
这里我们定义A矩阵为m×n的矩阵,B矩阵为n×p的一个矩阵,自然C是个m×p的矩阵。对于C矩阵中的每个元素的计算可有以下定义:
那具体的例子来说,可以参考如下:
其中每个单独C中的元素都是由矩阵A和矩阵B中确定的一个行与列作用得到的。就图中的C[1][1]按照这种理论可以写作:
C[1][1] = A[1][0]*B[0][1] + A[1][1]*B[1][1] + A[1][2]*B[2][1] + A[1][3]*B[3][1] + A[1][4]*B[4][1]
由此可见,A矩阵的第i行可以与B矩阵的第j列共同决定C矩阵的(i,j)元素,如下图所示:
二、代码逻辑
1. 复杂度分析
因为矩阵乘法涉及矩阵的每一个元素,同时A矩阵的全体行构造的集合要与B矩阵全体列构成的集合进行逐一运算,因此自然匹配的复杂度是。又运算时是若干乘积求和,这个乘积的过程在代码也需要一个循环来实现,所以直观来看我们需要三层循环来完成程序,基本复杂度是
。
2. 核心代码
由上述分析可知,大体上有三层循环。需要注意的难点是确定循环的上限以及先后顺序。
第一二层循环用于遍历矩阵,第三层循环用于控制乘法匹配。
综上,核心代码如下:
int[][] resultMatrix = new int[m][p];
for (int i = 0; i < m; i++) {
for (int j = 0; j < p; j++) {
for (int k = 0; k < n; k++) {
resultMatrix[i][j] += paraFirstMatrix[i][k] * paraSecondMatrix[k][j];
} // Of for k
} // Of for j
} // Of for i
三、代码及测试
package basic;
import java.util.Arrays;
/**
* This is the eighth code. Names and comments should follow my style strictly.
*
* @author: Changyang Hu joe03@foxmail.com
* @date created: 2025-05-09
*/
public class MatrixMultiplication {
/**
*
*********************
* @Title: main
* @Description: The entrance of the program.
*
* @param args Not used now.
* @return void
*********************
*/
public static void main(String args[]) {
matrixMultiplicationTest();
}// Of main
/**
*
*********************
* @Title: multiplication
* @Description: Matrix multiplication. The columns of the first matrix should be equal to the
* rows of the second one.
*
* @param paraFirstMatrix The first matrix.
* @param paraSecondMatrix The second matrix.
* @return The result matrix.
* @return int[][]
*********************
*/
public static int[][] multiplication(int[][] paraFirstMatrix, int[][] paraSecondMatrix) {
int m = paraFirstMatrix.length;
int n = paraFirstMatrix[0].length;
int p = paraSecondMatrix[0].length;
// Step 1. Dimension check
if (paraSecondMatrix.length != n) {
System.out.println("The two matrices cannot be multiplied.");
return null;
} // Of if
// Step 2. The loop
int[][] resultMatrix = new int[m][p];
for (int i = 0; i < m; i++) {
for (int j = 0; j < p; j++) {
for (int k = 0; k < n; k++) {
resultMatrix[i][j] += paraFirstMatrix[i][k] * paraSecondMatrix[k][j];
} // Of for k
} // Of for j
} // Of for i
return resultMatrix;
}// Of multiplication
/**
*
*********************
* @Title: matrixMultplicationTest
* @Description: Unit test for respective method.
*
* @return void
*********************
*/
public static void matrixMultiplicationTest() {
int[][] tempFirstMatrix = new int[2][3];
for (int i = 0; i < tempFirstMatrix.length; i++) {
for (int j = 0; j < tempFirstMatrix[0].length; j++) {
tempFirstMatrix[i][j] = i + j;
} // Of for j
} // Of for i
System.out.println("The first matrix is: \r\n" + Arrays.deepToString(tempFirstMatrix));
int[][] tempSecondMatrix = new int[3][2];
for (int i = 0; i < tempSecondMatrix.length; i++) {
for (int j = 0; j < tempSecondMatrix[0].length; j++) {
tempSecondMatrix[i][j] = i * 10 + j;
} // Of for j
} // Of for i
System.out.println("The second matrix is: \r\n" + Arrays.deepToString(tempSecondMatrix));
int[][] tempThirdMatrix = multiplication(tempFirstMatrix, tempSecondMatrix);
System.out.println("The third matrix is: \r\n" + Arrays.deepToString(tempThirdMatrix));
System.out.println("Trying to multiply the first matrix with itself.\r\n");
tempThirdMatrix = multiplication(tempFirstMatrix, tempFirstMatrix);
System.out.println("The result matrix is: \r\n" + Arrays.deepToString(tempThirdMatrix));
}// Of matrixMultiplicationTest
}// Of class MatrixMultiplication
运行结果如下:
其中,最开始构造两个矩阵相乘得到预期结果,但是矩阵1因为自身并不是方阵,因此无法自乘,故抛出了错误提示。
拓展:Arrays类详解
小结
本篇的第一个核心点在于理清三层循环的逻辑,我们需要再编码过程中有意识的去锻炼多层循环逻辑分析这一能力。一般来说,三重循环式大部分程序的极限,如果有更多层,那你首先需要做的,是回过头分析,是不是复杂了。尽可能简化逻辑,不仅仅是提高代码可读性,同时也是降低复杂度的首要手段。
另外,本篇还提及了一个在编写程序中很重要的反馈——非法输入检查。尤其是在编写大程序时,一定要对输入有严格检查。