快1个小时写了一个分治方法求解矩阵的算法,最后的结果还没运行出来,还是靠Deepseek才检查出来的问题,
DeepSeek用了不到30s就给出了答案,再一次感到挫败感,但是如果是搜索引擎查找copy的话,也是是和AI解答
一样,失去了练手和增加写代码能力的机会,所以无需自责,只是锻炼自己的能力罢了。
分治法求解矩阵
普通递归分块矩阵解决
主体思想是,把一个2^n次方阶数的矩阵,逐步细分,每次递归按照n/2逐步划分,直到n/2变成1*1的时候直接
返回成{{A[0][0]*B[0][0]}}的二维数组,为什么返回的是二维数组呢?我刚开始没想通,就理所当然的认为
必须返回个二维数组,但是我写这篇文章的时候又细想了一下,分治的思想是逐步缩小规模,原始规模是一个二维
数组的矩阵,所以缩小规模到维数为1维,也就是A[0][0]一个元素时,相当于一个值,计算与B[0][0]相乘后,获得
的是一个值,但是我们要把它看作一个1*1维的矩阵,因为这只是一个中间步骤,逐步划分,是要求划分后的C11,
C11=A11*B11+A12*B21。
当我们逐步缩小规模到2*2矩阵时,此时A11是2*2矩阵左上角的1维元素,同理其他的,
现在算C11,C11=M(A11,A12)+M(A12,A21),先进入第一个M,再次划分,变成1*1维的,这时不再划分A11,A12...
直接返回,比如{{0}},同理再求第二个M,然后比如返回{{1}},相加后{{1}},是C11也就是二维的左上角的二维矩阵
同理C12,C21,C22,然后再统一按二维数组的遍历去组装C,然后返回,这个实际上二维的矩阵就返回成功了。
更多维的比如4维,8维,还是同样的思路,但是都是深入到底层,然后逐渐返回上来。
像这种递归,分治的这种算法,如果处处都深入分析,那么还不如去循环,掌握它的每一处,递归只要注意到
界限条件和缩小的情况,然后胆大地去写
时间复杂度是8(n/2)+Θ(n2),8(n/2)是划分了8个规模缩小1/2的递归,n2是矩阵相加
master方法求解后时间复杂度的n^3
几个编程注意的点:
-
{{A[0][0]*B[0][0]}},这种是初始化列表,return返回这个列表时,如果返回值是数组就会赋给数组,如果
返回给vector就会初始化vector -
在编程时吃了C++11,初始化列表的大苦头。
vector<int> v1(10); // 10个元素的vectorvector<int> v2{10}; // 1个元素(值为10)//vector如果使用{}统一初始化作为构造函数时,会调用有初始化列表的构造函数//要显式调用某个构造函数的话还是使用()尽管会有隐式转换等问题struct Data {Data(int) {}Data(std::initializer_list<double>) {}};Data d(5); // 调用Data(int)Data d{5}; // 调用initializer_list(优先匹配)
- vector<vector
> C11 = matrixAdd(matrixMultiply(A11,B11),matrixMultiply(A12,B21));
这里直接使用matrixMultiply(xxx)函数的返回值作为参数,这里是直接返回的右值,也就是临时值,我之前
的函数参数是vector<vector>& A,没加const,编译器会报错的,不能将右值直接赋给没加const的引用
C++代码
#include<iostream>
#include<vector>using namespace std;vector<vector<int>> matrixAdd(const vector<vector<int>>&s1,const vector<vector<int>>&s2){int length = s1.size();vector<vector<int>> s3{length,vector<int>{length,0}};for(int i=0;i<length;i++){for(int j=0;j<length;j++){s3[i][j]=s1[i][j]+s2[i][j];}}return s3;
} void matrixPrint(const vector<vector<int>>& s){for(int i=0;i<s.size();i++){cout<<"[ ";for(int j=0;j<s.size();j++){cout<<s[i][j]<<" ";}cout<<"]"<<endl;}
}vector<vector<int>> matrixMultiply(const vector<vector<int>>&A,const vector<vector<int>>&B){int length = A.size()/2;if(A.size()==1){return {{A[0][0]*B[0][0]}};}vector<vector<int>> A11(length,vector<int>(length,0));vector<vector<int>> A12(length,vector<int>(length,0));vector<vector<int>> A21(length,vector<int>(length,0));vector<vector<int>> A22(length,vector<int>(length,0));vector<vector<int>> B11(length,vector<int>(length,0));vector<vector<int>> B12(length,vector<int>(length,0));vector<vector<int>> B21(length,vector<int>(length,0));vector<vector<int>> B22(length,vector<int>(length,0));for(int i=0;i<length;i++){for(int j=0;j<length;j++){//修改左侧A11-B22的下标全部变成[i][j] A11[i][j]=A[i][j];A12[i][j]=A[i][j+length];A21[i][j]=A[i+length][j];A22[i][j]=A[i+length][j+length];B11[i][j]=B[i][j];B12[i][j]=B[i][j+length];B21[i][j]=B[i+length][j];B22[i][j]=B[i+length][j+length];}}vector<vector<int>> C11 = matrixAdd(matrixMultiply(A11,B11),matrixMultiply(A12,B21));vector<vector<int>> C12 = matrixAdd(matrixMultiply(A11,B12),matrixMultiply(A12,B22));vector<vector<int>> C21 = matrixAdd(matrixMultiply(A21,B11),matrixMultiply(A22,B21));vector<vector<int>> C22 = matrixAdd(matrixMultiply(A21,B12),matrixMultiply(A22,B22));vector<vector<int>> C(A.size(),vector<int>(A.size(),0));for(int i=0;i<length;i++){for(int j=0;j<length;j++){C[i][j]=C11[i][j];C[i][j+length]=C12[i][j];C[i+length][j]=C21[i][j];C[i+length][j+length]=C22[i][j];}}return C;
}int main(){vector<vector<int>> A{{1,2,3,4},{1,2,3,4},{2,1,3,4},{2,2,3,4}};vector<vector<int>> B{{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};vector<vector<int>> C = matrixMultiply(A,B);matrixPrint(C);return 0;
}
Strassen's method求解矩阵乘法
主要思想是通过加法去减少乘法的次数
步骤就去BaiDu好了,这里时间复杂度是O(nlog27)
用了近2个小时,时间效率好低,这才只是开始啊,好想回到大一,那个时候几乎有打吧的时间(可能现在这么认为),但是
回不到过去,只能现在做到最好