禁止废话!没错,本水贴将带你了解矩阵,我们先看一下矩阵(本帖有些是高等数学,建议在看本帖之前先把数学搞好,我不会告诉你我是学了来发贴的):
看到矩阵是不是有点那啥,但他就是本贴的主角,相信看了本贴后你会从一无所知变为精通矩阵
1.矩阵是什么
2.矩阵有个什么毛线用
3.矩阵有些啥
4.编程实现
一.矩阵是什么
矩阵是由英国数学家凯莱发明的,顾名思义,就是一坨额一堆数字形成的方块,是线性空间变换的一种过程,连我们解方程组时我们其实做的是矩阵乘法,向量空间的一种基本等
二.矩阵的用处
矩阵可以解线性方程组,确定解的个数,线性变换过程,拉伸、旋转、平移,求特征值和特征向量,稳定性分析,图论(这是让我意想不到的),人工智能,图像素点处理,优化,加密,甚至充当降维武器!举个例子:
3x+2y=124x+9y=35解:
3429×xy=1235 xy=34291×1235 xy=199−194−192193×1235 xy=23所以x=2,y=3
三.矩阵有些啥
如果单看矩阵本身,那么他有行数,列数,行列式(det),秩(rank),迹(tr),而他的延伸板有伴随矩阵(∗),降维矩阵(det=0),转置矩阵(T),逆矩阵(−1)
如果看矩阵这个大类,那么有单位矩阵(I),零矩阵(O),对角矩阵(D),对称矩阵(S)
前面那一堆第四章会详细讲,也就是我们要用代码求的,讲一讲后四个:
单位矩阵:
1000010000100001对角线上全是1,其余全是0
零矩阵:
0000000000000000全是零
对称矩阵:
a11a12a13a14a12a22a23a24a13a23a33a34a14a24a34a44沿对角线"对折"能完全重合
对角矩阵
a110000a220000a330000a44除对角线外全是0
四.编程实现 这个要分好几个环节了 我们的程序是啥呢? 输入:
5
1 5 8 2 9
2 6 0 8 4
1 6 5 8 0
2 5 8 7 9
0 4 3 8 2
输出:
diagonal 0or1:0
zero 0or1:0
isldentity 0or1:0
isSymmetric 0or1:0
determinant:2778
rank:5
trace:21
Transpose:
1 2 1 2 0
5 6 6 5 4
8 0 5 8 3
2 8 8 7 8
9 4 0 9 2
Adjoint:
-948 320 670 1208 -1810
696 357 270 -840 -66
-60 -390 312 264 -138
-366 -64 -134 314 362
162 127 -472 28 280
Inverse:
-0.341253 0.115191 0.241181 0.434845 -0.651548
0.25054 0.12851 0.0971922 -0.302376 -0.0237581
-0.0215983 -0.140389 0.112311 0.0950324 -0.049676
-0.131749 -0.0230382 -0.0482361 0.113031 0.13031
0.0583153 0.0457163 -0.169906 0.0100792 0.100792
可以看到,这个程序可以判断各种矩阵(就是刚才那四个矩阵),determinant是行列式,rank是秩,tranc是迹,Transpose是转置矩阵,Adjoint是伴随矩阵,Inverse是逆矩阵
好,我们来一个一个分析(可能顺序不一样,由易到难):
1.判断矩阵
这个可以说是最简单的了,连注释都不想打,直接上代码!
bool dia(vector<vector<double>> A){//对角矩阵
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j&&A[i][j]!=0){
return false;
}
}
}
return true;
}
bool zero(vector<vector<double>> A){//零矩阵
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(A[i][j]!=0){
return false;
}
}
}
return true;
}
bool isl(vector<vector<double>> A){//单位矩阵
int n=A.size();
if(n==0) return false;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j&&A[i][j]!=1) return false;
if(i!=j&&A[i][j]!=0) return false;
}
}
return true;
}
bool isS(vector<vector<double>> A){//对称矩阵
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(A[i][j]!=A[j][i]){
return false;
}
}
}
return true;
}
在开始讲迹,秩,行列式之前,我要先讲一下函数返回类以及普通数组的拷贝性和vector(动态数组)的知识
函数一般写成以下形式:
#include<bits/stdc++.h>
using namespace std;
int Function(....)//int可以换
{
//函数内容
return ....;//是void就不要写return
}
可以看见,类型是啥就返回啥,比如类型是int就返int形变量
vector是动态数组,与普通数组不同的是,普通数组要麻烦的多:建一个数组要定义大小;而vector不用定义大小vector<int> a;要定义大小只需加括号,而且普通数组大小不变,不能作为返回值拷贝,而vector就可以;
2.计算迹
好,一堆杂七杂八的话说完后,该计算了,一个矩阵的迹可以这样计算:
∑i=0naii
说白了就是对角线相加,代码如下:
int jical(vector<vector<double>> A){
int n=A.size();
int ji=0;
for(int i=0;i<n;i++){//遍历对角线
ji+=A[i][i];
}
return ji;
}//在main函数里打印
3.计算秩
到这里就开始高等数学了,胆小勿入!
迹我们用高斯消元法来计算,高斯消元法是这样运作的:
1.找到这一行的“主元”(非零元素)
2.在搜索过程中,若有零行(这一行元素都为0),跳过不要
3.在找到非零元素后,证明这一行对矩阵的秩有贡献,秩+1
4.对于这个"第一主元",他所在的列上的元素全变0,计算消元因子,即这一行与下面那一行的元素的比值,拿这个元素乘消元因子,再从下面那一行减掉这个乘积,反正rank都加上了,管他的,这也是“消元”的体现
5.每一行都这么处理
好,上代码!
double rankcal(vector<vector<double>> A)
{
int n=A.size();
int m=A[0].size();
int rank=0;
for(int i=0;i<n;i++){
int pivot=-1;
for(int j=0;j<m;j++){
if(A[i][j]!=0)
{
pivot=j;
break;
}
}
if(pivot==-1){
continue;
}
rank++;
for(int k=i+1;k<n;k++){
double factor=A[k][pivot]/A[i][pivot];
for(int j=pivot;j<m;j++){
A[k][j]-=factor*A[i][j];
}
}
}
return rank;
}
这里的pivot是记索引的如果pivot是-1则证明该行全是0,跳过就行了最后rank就是矩阵的秩
4.计算行列式
这里就不得不提一下了,我们前面是不是还有个"降维矩阵"没判断,其实这取决于行列式,前面讲过了,矩阵是线性空间的一种变换过程,而一个det(A)=b的A矩阵中,能让一个实打实的二维平面变为一维直线,让三维空间变为二维平面,而这个b就等于0,这个武器在小说三体中被清除太阳系,可在数学里就是一个矩阵,好吧,为了避免有水贴嫌疑,我就直接开始讲了,任意一个n阶矩阵,行列式这样算:
j=1∑naij(−1)i+jMij其中,Mij是代数余子式,即去掉该行该列后子矩阵的行列式,所以,行列式本身是一个高阶递归,不过,既然计算里有一个−1则,行列是可能大到离谱,也有可能负道微分,所以才定义double,然后就没啥可说的了,上代码! 不对?哦,原来不是每个元素都要高阶递归,只用第一行的展开就行,不然CPU可能要……上代码!
double detcal(vector<vector<double>> A){
int n=A.size();
if(n==1) return A[0][0];
if(n==2) return A[0][0]*A[1][1]-A[0][1]*A[1][0];
double det=0;
for(int j=0;j<n;j++){
vector<vector<double>> subA(n-1,vector<double>(n-1));
for(int i=1;i<n;i++)
{
for(int k=0;k<n;k++){
if(k<j) subA[i-1][k]=A[i][k];
if(k>j) subA[i-1][k-1]=A[i][k];
}
}
det+=(j%2==0 ? 1:-1)*A[0][j]*detcal(subA);
}
return det;
}
再补充一些哈(我不会告诉你只是为了解读两个)if
二阶矩阵就是两条对角线相减(主对角线-副对角线)
一阶就是这个元素
第一层循环就是遍历第一行
subA是代数余子式的子矩阵
第二层循环是代数余子式的遍历
第三层循环就是把原矩阵除去主元(和秩不一样)的拷贝循环
难理解的可能就是那个det+=(j%2==0 ? 1:-1)*A[0][j]*detcal(subA); detcal就是递归,可那个三目运算符又是个啥玩意?不是(−1)i+j吗?i又去哪了?别忘了行列式只跟第一行有关,跟其他行连10000001毛线关系都没有,在c++里自然就是0了,0还管个毛线啊,所以判断j就行了
5.计算转置矩阵
这个比行列式还简单,把原数组带过来(怎么带用vector),再用一个新数组转置,注意不是Transpose[i][j]那就不是转置了,就是一毛一样输出了,应该是Transpose[j][i],最后把Transpose作为返回值就可以了,代码:
vector<vector<double>> Transposecal(vector<vector<double>> A){
int n=A.size();
vector<vector<double>>Transpose(n,vector<double>(n));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
Transpose[j][i]=A[i][j];
}
}
return vector<vector<double>>(Transpose);
}
6.输出伴随矩阵
这玩意根行列式差不多,也是调用行列式来算,就是
Aij=(−1)i+jMij不多说,上代码!
vector<vector<double>> Adjointcal(vector<vector<double>> A){
int n=A.size();
vector<vector<double>> adjugate(n,vector<double>(n));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
vector<vector<double>> minor(n-1,vector<double>(n-1));
for(int row=0,r=0;row<n;row++){
if(row==i){
continue;
}
for(int col=0,c=0;col<n;col++){
if(col==j){
continue;
}
minor[r][c]=A[row][col];
c++;
}
r++;
}
double cofactor=pow(-1,i+j)*detcal(minor);
adjugate[j][i]=cofactor;
}
}
return vector<vector<double>>(adjugate);
}
不废话了,没有什么很难的地方
7.计算逆矩阵
这个有特判,因为逆矩阵是这样计算的:
A−1=A∗/det(A)A∗是伴随矩阵,det是行列式,所以A是降维矩阵时,矩阵不可逆,上代码:
vector<vector<double>> Inverse(vector<vector<double>> A)
{
int n=A.size();
vector<vector<double>> E(n,vector<double>(n));
E=Adjointcal(A);
vector<vector<double>> F(n,vector<double>(n,0.0));
double det1=detcal(A);
if(det1==0){
cout<<"det zero.";
return vector<vector<double>>(F);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
F[i][j]=E[i][j]/det1;
}
}
return vector<vector<double>>(F);
}
这样我们的判断,计算等函数就完了,main函数就是输出,没啥可打的,就不打了
8.完整代码(有亿点点长):
#include<bits/stdc++.h>
using namespace std;
double rankcal(vector<vector<double>> A)
{
int n=A.size();
int m=A[0].size();
int rank=0;
for(int i=0;i<n;i++){
int pivot=-1;
for(int j=0;j<m;j++){
if(A[i][j]!=0)
{
pivot=j;
break;
}
}
if(pivot==-1){
continue;
}
rank++;
for(int k=i+1;k<n;k++){
double factor=A[k][pivot]/A[i][pivot];
for(int j=pivot;j<m;j++){
A[k][j]-=factor*A[i][j];
}
}
}
return rank;
}
double detcal(vector<vector<double>> A){
int n=A.size();
if(n==1) return A[0][0];
if(n==2) return A[0][0]*A[1][1]-A[0][1]*A[1][0];
double det=0;
for(int j=0;j<n;j++){
vector<vector<double>> subA(n-1,vector<double>(n-1));
for(int i=1;i<n;i++)
{
for(int k=0;k<n;k++){
if(k<j) subA[i-1][k]=A[i][k];
if(k>j) subA[i-1][k-1]=A[i][k];
}
}
det+=(j%2==0 ? 1:-1)*A[0][j]*detcal(subA);
}
return det;
}
int jical(vector<vector<double>> A){
int n=A.size();
int ji=0;
for(int i=0;i<n;i++){
ji+=A[i][i];
}
return ji;
}
bool dia(vector<vector<double>> A){
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j&&A[i][j]!=0){
return false;
}
}
}
return true;
}
bool zero(vector<vector<double>> A){
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(A[i][j]!=0){
return false;
}
}
}
return true;
}
bool isl(vector<vector<double>> A){
int n=A.size();
if(n==0) return false;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j&&A[i][j]!=1) return false;
if(i!=j&&A[i][j]!=0) return false;
}
}
return true;
}
bool isS(vector<vector<double>> A){
int n=A.size();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(A[i][j]!=A[j][i]){
return false;
}
}
}
return true;
}
vector<vector<double>> Transposecal(vector<vector<double>> A){
int n=A.size();
vector<vector<double>>Transpose(n,vector<double>(n));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
Transpose[j][i]=A[i][j];
}
}
return vector<vector<double>>(Transpose);
}
vector<vector<double>> Adjointcal(vector<vector<double>> A){
int n=A.size();
vector<vector<double>> adjugate(n,vector<double>(n));
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
vector<vector<double>> minor(n-1,vector<double>(n-1));
for(int row=0,r=0;row<n;row++){
if(row==i){
continue;
}
for(int col=0,c=0;col<n;col++){
if(col==j){
continue;
}
minor[r][c]=A[row][col];
c++;
}
r++;
}
double cofactor=pow(-1,i+j)*detcal(minor);
adjugate[j][i]=cofactor;
}
}
return vector<vector<double>>(adjugate);
}
vector<vector<double>> Inverse(vector<vector<double>> A)
{
int n=A.size();
vector<vector<double>> E(n,vector<double>(n));
E=Adjointcal(A);
vector<vector<double>> F(n,vector<double>(n,0.0));
double det1=detcal(A);
if(det1==0){
cout<<"det zero.";
return vector<vector<double>>(F);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
F[i][j]=E[i][j]/det1;
}
}
return vector<vector<double>>(F);
}
int main()
{
int n;
cin>>n;
vector<vector<double>> A(n,vector<double>(n));//Original matrix
vector<vector<double>> B(n,vector<double>(n));//Transpose matrix
vector<vector<double>> C(n,vector<double>(n));//Accompanying matrix
vector<vector<double>> D(n,vector<double>(n));//Inverse matrix
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>A[i][j];
B[i][j]=A[i][j];
C[i][j]=A[i][j];
D[i][j]=A[i][j];
}
}
cout<<"diagonal 0or1:"<<dia(A)<<endl;
cout<<"zero 0or1:"<<zero(A)<<endl;
cout<<"isldentity 0or1:"<<isl(A)<<endl;
cout<<"isSymmetric 0or1:"<<isS(A)<<endl;
cout<<"determinant:"<<detcal(A)<<endl;
cout<<"rank:"<<rankcal(A)<<endl;
cout<<"trace:"<<jical(A)<<endl;
cout<<"Transpose:"<<endl;
B=Transposecal(B);
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++){
cout<<B[i][j]<<" ";
}
cout<<endl;
}
cout<<"Adjoint:"<<endl;
C=Adjointcal(C);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<C[i][j]<<" ";
}
cout<<endl;
}
cout<<"Inverse:"<<endl;
D=Inverse(D);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<D[i][j]<<" ";
}
cout<<endl;
}
return 0;
}