引入
参考文献:https://www.geeksforgeeks.org/strassens-matrix-multiplication/
目标:给定两个形状为
n
n
n的方阵
A
A
A和
B
B
B,求解它们的矩阵相乘结果
1 朴素方法
就是两个矩阵相乘咯:
#include <iostream>
/*
* 常规方阵乘法
* :param X: 方阵1
* :param Y: 方阵2
* :retutn 矩阵相乘结果
*/
int** matrix_multiply(int** X, int** Y, int n);
/*
* 展示方阵
* :param X: 待展示方阵
* :param n: 方阵大小
*/
void matrix_show(int** X, int n);
int main()
{
// 方阵初始化
int**X = new int* [2], **Y = new int*[2];
for (int i = 0; i < 2; i++)
{
X[i] = new int[2];
Y[i] = new int[2];
for (int j = 0; j < 2; j++)
{
X[i][j] = i + j;
Y[i][j] = i - j;
}
}
std::cout << "方阵X:\n";
matrix_show(X, 2);
std::cout << "方阵Y:\n";
matrix_show(Y, 2);
std::cout << "计算结果:\n";
int** Z = matrix_multiply(X, Y, 2);
matrix_show(Z, 2);
return 0;
}
int** matrix_multiply(int** X, int** Y, int n)
{
int** ret = new int* [n];
for (int i = 0; i < n; i++)
{
ret[i] = new int [n]();
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
for (int k = 0; k < n; k++)
{
ret[i][j] += X[i][k] * Y[k][j];
}
}
}
return ret;
}
void matrix_show(int** X, int n)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
std::cout << X[i][j] << " ";
}
std::cout << std::endl;
}
}
显然时间复杂度为 O ( n 3 ) O(n^3) O(n3)
2 Strassen矩阵乘法
对于方阵乘法,一个可能的想法是:
1)划分两个方阵
A
A
A和
B
B
B为四个子方阵
2)分别计算四个子区域的值
此时我们需要计算8次子方阵的乘法和4次子方阵的加法,而每两个子方阵之间的乘法消耗
O
(
N
3
8
)
O(\frac{N^3}{8})
O(8N3),再来4次加法,这不更多了吗?
显然,这样的分治不是我们想要的,因为每一次递归都有8次子递归操作。
对此,Strassen方法设计以下操作,使得递归次数减少1次:
其中:
看起来还是复杂哈哈哈,不过最终的时间复杂度约为
O
(
N
2.8074
)
O(N^{2.8074})
O(N2.8074)。需要说明的是,该方法在以下情况下不适用:
1)使用的常数多,对于典型应用,依然是朴素方法更佳
2)稀疏矩阵有专门的方法
3)递归的子矩阵需要额外的空间
4)由于计算机对非整数值的运算精度有限,因此累计误差更大