矩阵
  • 板块学术版
  • 楼主封禁用户
  • 当前回复6
  • 已保存回复6
  • 发布时间2025/1/17 20:14
  • 上次更新2025/1/18 08:53:08
查看原帖
矩阵
1430277
封禁用户楼主2025/1/17 20:14

禁止废话!没错,本水贴将带你了解矩阵,我们先看一下矩阵(本帖有些是高等数学,建议在看本帖之前先把数学搞好,我不会告诉你我是学了来发贴的):

a11a12a13a21a22a23a31a32a33 \begin{matrix} a11&a12 &a13 …\\ a21&a22 &a23 …\\ a31&a32 &a33… \end{matrix}

看到矩阵是不是有点那啥,但他就是本贴的主角,相信看了本贴后你会从一无所知变为精通矩阵

1.矩阵是什么

2.矩阵有个什么毛线用

3.矩阵有些啥

4.编程实现

一.矩阵是什么

矩阵是由英国数学家凯莱发明的,顾名思义,就是一坨额一堆数字形成的方块,是线性空间变换的一种过程,连我们解方程组时我们其实做的是矩阵乘法,向量空间的一种基本

二.矩阵的用处

矩阵可以解线性方程组,确定解的个数,线性变换过程,拉伸、旋转、平移,求特征值和特征向量,稳定性分析,图论(这是让我意想不到的),人工智能,图像素点处理,优化,加密,甚至充当降维武器!举个例子:

3x+2y=124x+9y=35 \begin{matrix} 3x+2y=12\\ 4x+9y=35 \end{matrix}

解:

3249×xy=1235 \begin{vmatrix} 3&2 \\ 4&9 \end{vmatrix} \times \begin{vmatrix} x\\ y \end{vmatrix} =\begin{vmatrix} 12\\ 35 \end{vmatrix} xy=13249×1235 \begin{vmatrix} x\\ y \end{vmatrix} =\frac{1}{\begin{vmatrix} 3&2 \\ 4&9 \end{vmatrix}} \times \begin{vmatrix} 12\\ 35 \end{vmatrix} xy=919219419319×1235\begin{vmatrix} x\\ y \end{vmatrix} ={\begin{vmatrix} \frac{9}{19}&-\frac{2}{19} \\ -\frac{4}{19}&\frac{3}{19} \end{vmatrix}} \times \begin{vmatrix} 12\\ 35 \end{vmatrix} xy=23\begin{vmatrix} x\\ y \end{vmatrix} = \begin{vmatrix} 2\\ 3 \end{vmatrix}

所以x=2,y=3x=2,y=3

三.矩阵有些啥

如果单看矩阵本身,那么他有行数,列数,行列式(detdet),秩(rankrank),迹(trtr),而他的延伸板伴随矩阵(^*),降维矩阵(det=0det=0),转置矩阵(T^T),逆矩阵(1^{-1})

如果看矩阵这个大类,那么有单位矩阵(II),零矩阵(OO),对角矩阵(DD),对称矩阵(SS)

前面那一堆第四章会详细讲,也就是我们要用代码求的,讲一讲后四个:

单位矩阵:

1000010000100001 \begin{vmatrix} 1&0 &0 &0 \\ 0&1 &0 &0 \\ 0&0 &1 &0 \\ 0&0 &0 &1 \end{vmatrix}

对角线上全是1,其余全是0

零矩阵:

0000000000000000\begin{vmatrix} 0&0 &0 &0 \\ 0&0 &0 &0 \\ 0&0 &0 &0 \\ 0&0 &0 &0 \end{vmatrix}

全是零

对称矩阵:

a11a12a13a14a12a22a23a24a13a23a33a34a14a24a34a44\begin{vmatrix} a11&a12 &a13 &a14 \\ a12&a22 &a23 &a24 \\ a13&a23 &a33 &a34 \\ a14&a24 &a34 &a44 \end{vmatrix}

沿对角线"对折"能完全重合

对角矩阵

a110000a220000a330000a44 \begin{vmatrix} a11&0 &0 &0 \\ 0&a22 &0 &0 \\ 0&0 &a33 &0 \\ 0&0 &0 &a44 \end{vmatrix}

除对角线外全是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\sum_{i=0}^{n} a_{ii}

说白了就是对角线相加,代码如下:

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.对于这个"第一主元",他所在的列上的元素全变00,计算消元因子,即这一行与下面那一行的元素的比值,拿这个元素乘消元因子,再从下面那一行减掉这个乘积,反正rankrank都加上了,管他的,这也是“消元”的体现

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)=bdet(A)=b的A矩阵中,能让一个实打实的二维平面变为一维直线,让三维空间变为二维平面,而这个bb就等于0,这个武器在小说三体中被清除太阳系,可在数学里就是一个矩阵,好吧,为了避免有水贴嫌疑,我就直接开始讲了,任意一个n阶矩阵,行列式这样算:

j=1naij(1)i+jMij \sum_{j=1}^{n} a_{ij}(-1)^{i+j}M_{ij}

其中,MijM_{ij}是代数余子式,即去掉该行该列后子矩阵的行列式,所以,行列式本身是一个高阶递归,不过,既然计算里有一个1-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(-1)^{i+j}吗?i又去哪了?别忘了行列式只跟第一行有关,跟其他行连11000000\frac{1}{1000000}毛线关系都没有,在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 A_{ij}=(-1)^{i+j}M_{ij}

不多说,上代码!

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.计算逆矩阵

这个有特判,因为逆矩阵是这样计算的:

A1=A/det(A)A^{-1}=A^{*}/det(A)

AA^{*}是伴随矩阵,detdet是行列式,所以AA是降维矩阵时,矩阵不可逆,上代码:

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;
}
2025/1/17 20:14
加载中...