矩阵、高斯消元、行列式及相关结论学习笔记(没写完)

矩阵、高斯消元、行列式及相关结论学习笔记

矩阵的概念

矩阵可以简单理解为二维数组。在 OI 中,矩阵的元素一般为整数或实数。

矩阵 \(\boldsymbol A\) 的第 \(i\) 行、第 \(j\) 列元素记作 \(a_{ij}\)

仅有一行的矩阵称为行向量,只有一列的矩阵称为列向量。

矩阵左上到右下的对角线称为主对角线,右上到左下的对角线称为副对角线。

矩阵的运算

矩阵加法

矩阵加法就是对应位置相加。只有同型矩阵才能进行加法运算。

例如:

\[\begin{bmatrix} 1 & 2 & 1 \\ 0 & 3 & 2 \\ \end{bmatrix} + \begin{bmatrix} 3 & 0 & 1 \\ 1 & 1 & 0 \\ \end{bmatrix} = \begin{bmatrix} 1+3 & 2+0 & 1+1 \\ 0+1 & 3+1 & 2+0 \\ \end{bmatrix} = \begin{bmatrix} 4 & 2 & 2 \\ 1 & 4 & 2 \\ \end{bmatrix} \]

矩阵加法满足:

  • 结合律:\((\boldsymbol A+\boldsymbol B)+\boldsymbol C=\boldsymbol A+(\boldsymbol B+\boldsymbol C)\)
  • 交换律:\(\boldsymbol A+\boldsymbol B=\boldsymbol B+\boldsymbol A\)

矩阵减法

矩阵减法就是对应位置相减。只有同型矩阵才能进行减法运算。

矩阵数乘

矩阵数乘就是每个位置相乘。

例如:

\[2 \cdot \begin{bmatrix} 2 & 0 & 3 \\ 1 & 2 & 1 \\ \end{bmatrix} = \begin{bmatrix} 2\cdot 2 & 2\cdot 0 & 2\cdot 3 \\ 2\cdot 1 & 2\cdot 2 & 2\cdot 1 \\ \end{bmatrix} = \begin{bmatrix} 4 & 0 & 6 \\ 2 & 4 & 2 \\ \end{bmatrix} \]

矩阵数乘满足:

  • \(\lambda(\mu\boldsymbol A)=\mu(\lambda\boldsymbol A)=(\lambda\mu)\boldsymbol A\)
  • \((\lambda+\mu)\boldsymbol A=\lambda\boldsymbol A+\mu\boldsymbol A\)
  • \(\lambda(\boldsymbol A+\boldsymbol B)=\lambda\boldsymbol A+\lambda\boldsymbol B\)

矩阵乘法

矩阵乘法有意义当且仅当第一个矩阵 \(\boldsymbol A\) 的列数与第二个矩阵 \(\boldsymbol B\) 的行数相等。设 \(\boldsymbol A,\boldsymbol B\) 的大小分别为 \(n\times m,m\times k\),则 \(\boldsymbol C=\boldsymbol A\boldsymbol B\) 的大小为 \(n\times k\)。其中:

\[c_{ij}=\sum\limits_{r=1}^ma_{ir}b_{rj} \]

例如:

\[\begin{bmatrix} 1 & 0 & 2 \\ -1 & 3 & 1 \\ \end{bmatrix} \times \begin{bmatrix} 3 & 1 \\ 2 & 1 \\ 1 & 0 \\ \end{bmatrix} = \begin{bmatrix} 1\cdot 3+0\cdot 2+2\cdot 1 & 1\cdot 1+0\cdot 1+2\cdot 0 \\ -1\cdot 3+3\cdot 2+1\cdot 1 & -1\cdot 1+3\cdot 1+1\cdot 0 \\ \end{bmatrix} = \begin{bmatrix} 5 & 1 \\ 4 & 2 \\ \end{bmatrix} \]

矩阵乘法满足:

  • 结合律:\((\boldsymbol A\boldsymbol B)\boldsymbol C=\boldsymbol A(\boldsymbol B\boldsymbol C)\)
  • 对加法的左分配律:\((\boldsymbol A+\boldsymbol B)\boldsymbol C=\boldsymbol A\boldsymbol C+\boldsymbol B\boldsymbol C\)
  • 对加法的右分配律:\(\boldsymbol C(\boldsymbol A+\boldsymbol B)=\boldsymbol C\boldsymbol A+\boldsymbol C\boldsymbol B\)

矩阵乘法不满足交换律。

行数、列数相同的矩阵有单位矩阵,其中 \(e_{ij}=[i=j]\)。例如,大小为 \(3\times 3\) 的单位矩阵如下:

\[\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} \]

单位矩阵 \(\boldsymbol E\) 满足:

  • \(\boldsymbol E\boldsymbol A=\boldsymbol A\boldsymbol E=\boldsymbol A\)

矩阵的幂

矩阵的幂有意义当且仅当行数、列数相同。

矩阵 \(\boldsymbol A\)\(k\) 次幂 \(\boldsymbol A^k\) 等于 \(\boldsymbol A\) 自乘 \(k\) 次的结果。

显然,矩阵的幂可以使用快速幂加速。

矩阵转置

将矩阵 \(\boldsymbol A\) 的行和列交换得到转置矩阵 \(\boldsymbol A^T\)

例如:

\[\begin{bmatrix} 2 & 0 & 3 \\ 1 & 2 & 1 \\ \end{bmatrix}^T = \begin{bmatrix} 2 & 1 \\ 0 & 2 \\ 3 & 1 \\ \end{bmatrix} \]

矩阵转置满足:

  • \((\boldsymbol A^T)^T=\boldsymbol A\)
  • \((\lambda\boldsymbol A)^T=\lambda\boldsymbol A^T\)
  • \((\boldsymbol A\boldsymbol B)^T=\boldsymbol B^T\boldsymbol A^T\)

高斯-约当消元法(Gauss-Jordan Elimination)

简介

高斯消元法是用于求解线性方程组的经典算法。

初等行变换

矩阵的初等行变换包括:

  • 交换矩阵的两行。
  • 以一个非零数 \(k\) 乘矩阵的某一行。
  • 将矩阵的某一行的 \(k\) 倍加到另一行。

同理可以定义初等列变换。

增广矩阵

对于线性方程组:

\[\begin{cases} a_{11}x_1+a_{12}x_2+a_{13}x_3+\cdots+a_{1n}x_n=b_1\\ a_{21}x_1+a_{22}x_2+a_{23}x_3+\cdots+a_{2n}x_n=b_2\\ a_{31}x_1+a_{32}x_2+a_{33}x_3+\cdots+a_{3n}x_n=b_3\\ \vdots\\ a_{n1}x_1+a_{n2}x_2+a_{n3}x_3+\cdots+a_{nn}x_n=b_n\\ \end{cases} \]

定义它的增广矩阵为:

\[\begin{bmatrix} a_{11} & a_{12} & a_{13} & \cdots & a_{1n} & b_1 \\ a_{21} & a_{22} & a_{23} & \cdots & a_{2n} & b_2 \\ a_{31} & a_{32} & a_{33} & \cdots & a_{3n} & b_3 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ a_{n1} & a_{n2} & a_{n3} & \cdots & a_{nn} & b_n \\ \end{bmatrix} \]

高斯消元

容易发现,对增广矩阵进行初等行变换不会改变方程组的解。

我们的目标就是利用初等行变换将增广矩阵变为如下形式:

\[\begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & x_1 \\ 0 & 1 & 0 & \cdots & 0 & x_2 \\ 0 & 0 & 1 & \cdots & 0 & x_3 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & 1 & x_n \\ \end{bmatrix} \]

则最右侧的一列就是方程组的解。如果无法变为该形式,则方程无解或无唯一解,可以进一步进行判定。

高斯消元的流程是:

  • 初始时令 \(i=1\)
  • 在第 \(i\sim n\) 行找出一行,使得第 \(i\) 列不为 \(0\),并将该行与第 \(i\) 行交换(为减小浮点数运算导致的精度误差,通常选取绝对值最大的数那一行)。
  • 将该行的若干倍加到每一行,使得其余行的第 \(i\) 列均为 \(0\)
  • \(i < n\),令 \(i\) 自增一,回到第二步;否则,进行下一步。
  • 对于每一行 \(i\) 计算得 \(x_i=\frac{b_i}{a_{ii}}\)

时间复杂度为 \(\mathcal O(n^3)\)

代码
const double eps = 1e-6;
bool gauss() {
	for(int i = 1; i <= n; i++) {
		int u = i;
		for(int j = i + 1; j <= n; j++) if(fabs(a[j][i]) > fabs(a[u][i])) u = j;
		swap(a[i], a[u]);
		if(fabs(a[i][i]) < eps) return 0; // 无解或无唯一解
		for(int j = 1; j <= n; j++) {
			if(i != j) {
				double mt = a[j][i] / a[i][i];
				for(int k = 1; k <= n + 1; k++) a[j][k] -= mt * a[i][k];
			}
		}
	}
	for(int i = 1; i <= n; i++) a[i][n+1] /= a[i][i];
	return 1; 
}

矩阵求逆

\(n\times n\) 的矩阵 \(\boldsymbol A\) 的乘法逆元存在,当且仅当该矩阵的秩为 \(n\)

矩阵 \(\boldsymbol A\) 的秩 \(\operatorname{rk}\boldsymbol A\) 是它的线性无关的行数。

类似于高斯消元的增广矩阵,矩阵求逆只需要在原矩阵 \(\boldsymbol A\) 右侧加上一个 \(n\times n\) 的单位阵 \(\boldsymbol I\),然后进行初等行变换将左侧消成单位阵 \(\boldsymbol I\),此时右侧的矩阵即为 \(\boldsymbol A^{-1}\)

代码
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
const int N = 805, mod = 1e9+7;

int n, a[N][N];
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
int qpow(int x, int y) {
	int ans = 1;
	for(;y;y>>=1,x=1LL*x*x%mod) if(y & 1) ans = 1LL * ans * x % mod;
	return ans;
}
int inv(int x) {return qpow(x, mod-2);}
bool gauss() {
	rep(i, 1, n) {
		int u = i;
		rep(j, i+1, n) if(a[j][i]) u = j;
		swap(a[i], a[u]);
		if(!a[i][i]) return 0;
		rep(j, 1, n) {
			if(i == j) continue;
			int mt = (-1LL * a[j][i] * inv(a[i][i]) % mod + mod) % mod;
			rep(k, i, 2*n) a[j][k] = (a[j][k] + 1LL * mt * a[i][k] % mod) % mod;
		}
	}
	rep(i, 1, n) {
		rep(j, n+1, 2*n) {
			a[i][j] = 1LL * a[i][j] * inv(a[i][i]) % mod;
		}
	}
	return 1;
}

int main() {
	scanf("%d", &n);
	rep(i, 1, n) rep(j, 1, n) scanf("%d", &a[i][j]);
	rep(i, 1, n) rep(j, n+1, 2*n) a[i][j] = (i + n == j);
	if(gauss()) {
		rep(i, 1, n) {
			rep(j, n+1, 2*n) {
				printf("%d%c", a[i][j], " \n"[j==2*n]);
			}
		}
	}
	else puts("No Solution");
    return 0;
}

咕咕咕

posted @ 2023-03-10 20:40  rui_er  阅读(145)  评论(1编辑  收藏  举报