目录
前言
今天是坚持写博客的第十天,坚持的天数上两位数了,路漫漫其修远兮,继续坚持,今天也要继续加油!
矩阵乘法是线性代数当中非常让大家头疼的一环,所以我们今天也斗胆来看看数据结构与算法中的矩阵乘法问题。
矩阵乘法在数据结构与算法中主要分为两种方法,分别是传统for循环求解法和Strassen算法,即分治法求解矩阵乘法。接下来请听我娓娓道来,为您献上两种方法的使用手册。
for循环求解
分析
在求解矩阵乘法时,最常见的方法必然是我们使用三层循环进行求解,逐步将A矩阵的行与B矩阵的列相乘后相加,最终得到结果并存入矩阵C中,最后打印。但是我们一定要注意矩阵乘法的核心问题:矩阵A的列数必须等于矩阵B的行数,否则矩阵乘法将无法进行。
代码
由于for循环的方法属于暴力解法,属于常规思路之一,非常好理解,因此我也不做过多解释,具体的示例代码如下:
void matrixMultiplication(int rowsA, int colsA, int A[rowsA][colsA],
int rowsB, int colsB, int B[rowsB][colsB],
int rowsC, int colsC, int C[rowsC][colsC]) {
// 确保A的列数等于B的行数,否则无法进行乘法
if(colsA != rowsB) {
printf("矩阵A的列数必须等于矩阵B的行数。\n");
return;
}
// 初始化结果矩阵C为0
for(int i = 0; i < rowsC; i++)
for(int j = 0; j < colsC; j++)
C[i][j] = 0;
// 矩阵乘法的核心计算
for(int i = 0; i < rowsA; i++) {
for(int j = 0; j < colsB; j++) {
for(int k = 0; k < colsA; k++) { // 注意这里是colsA,因为是A的列数
C[i][j] += A[i][k] * B[k][j]; // 对应位置相乘累加
}
}
}
}
// 打印矩阵的函数
void printMatrix(int rows, int cols, int matrix[rows][cols]) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++)
printf("%d\t", matrix[i][j]);
printf("\n");
}
}
没想到吧,这里没有寄术主播的灵魂画图手法
但是需要注意的是,for循环的求解方法由于存在三层循环,时间复杂度会很高,为O(n^3),因此一旦数据量增大,则时间会增长很快。
分治法求解
分析
书接上文,因为三层for循环的时间复杂度很高,因此基于分治法思想的Strassen算法出现了,这个算法可以快速求解矩阵乘法,提升效率。
Strassen算法给予分治思想,通过将大矩阵分解为较小的子矩阵,然后对这些子矩阵进行较少数量的乘法运算,再合并结果,从而减少了整体的计算量。
Strassen算法的主要步骤如下:
1、将矩阵A、B及结果矩阵C分别划分为四个子矩阵A11, A12, A21, A22和B11, B12, B21, B22以及C11, C12, C21, C22。如下图:
2、通过7次乘法计算7个中间矩阵M1至M7:
- M1 = (A11 + A22) x (B11 + B22)
- M2 = (A21 + A22) x B11
- M3 = A11 x (B12 - B22)
- M4 = A22 x (B21 - B11)
- M5 = (A11 + A12) x B22
- M6 = (A21 - A11) x (B11 + B12)
- M7 = (A12 - A22) x (B21 + B22)
3、将M1-M7进行合并,得到矩阵C:
- C11 = M1 + M4 - M5 + M7
- C12 = M3 + M5
- C21 = M2 + M4
- C22 = M1 - M2 + M3 + M6
4、进行递归合并,在n>2时,不断递归重复上述步骤,否则得出结果。
Strassen算法的时间复杂度为O(n^log2(7)),约等于O(n^2.807),相较于传统算法的O(n^3),时间复杂度有一定优化,效率提升,尤其是在处理大矩阵时。
结束语
分治法求解矩阵乘法在数据结构中的地位虽然没有DFS等高,但是仍然是大家值得掌握的算法。如果本文对您有帮助,希望您可以为我点赞或关注,这对我很重要,谢谢!