快速矩阵乘法的算法实现
矩阵乘法
对于两个矩阵的相乘,只有在第一个矩阵的列数和第二个矩阵的行数相同的时候,其乘积才有意义。
最早出现的就是一般的矩阵乘法,并且在很长的一段时间内人们都认为该算法无法继续改进,直到1969年,Strassen发明了一个算法,首次降低了矩阵乘法的时间复杂度,并且该算法也以他的名字命名——Strassen算法。
在后续的几十年里,人们在Strassen算法的基础上不断改进算法,并且在逐步降低矩阵乘法的时间复杂度。比较有里程碑意义的是在1990年,Coppersmith和Winograd两个人对算法进行的改进,其算法也被称为算法。
一般矩阵乘法
一般矩阵乘法,将第一个矩阵一行中的元素与第二个矩阵中一列对应位置上元素的乘积之和,作为第三个矩阵这一行一列对应位置的结果。
对于n*n的矩阵乘法其时间复杂度为O(n3)
/*
* Generate Algorithm
* matA a M*K matrix
* matB a K*N matrix
* matC a M*N matrix
* matC = matA * matB
*/
static void mm_generate(float* matA, float* matB, float* matC, const int M, const int N, const int K)
{
for (int i = 0; i < M;i++)
{
for (int j = 0; j < N;j++)
{
float sum = 0.0f;
for (int k = 0; k < K;k++)
{
sum += matA[i*K + k] * matB[k*N + j];
}
matC[i*N + j] = sum;
}
}
}
分块算法
将矩阵分割成四块来计算
这里需要8个乘法和4个加法
其时间复杂度依然是 O(n3)
Strassen 算法
Strassen在分块的基础上进行改进
这里用了7个乘法和18个加/减法
对于每一个 n * n 矩阵,可以看成有 2 * 2 的小矩阵拼接而来,因此会有 n/2 * n/2 个小矩阵
T(n) = 7 * T(n/2) + O(n2)
T(n) = O(nlg7) + O(n2.81)
其算法的时间复杂度为O(n2.81)
Github:Straseen算法的代码实现
相较与一般算法来说,只有在n很大的时候才会体现出性能上的优势,并且在实现是采用递归调用,每次调用会产生中间7个矩阵,所占用的空间要比一般的算法多。
Coppersmith-Winograd算法
后来Coppersmith Winograd 两个人对算法进行了改进
这里总共用了7个乘法,15个加/减法
其时间复杂度为O(n2.376)
Github:Coppersmith-Winograd算法的代码实现
该算法具体的介绍可以在以下论文中找到
论文链接:
The Coppersmith-Winograd Matrix Multiplication Algorithm
On the Arithmetic Complexity of Strassen-Like Matrix Multiplications