矩阵、高斯消元、行列式及相关结论学习笔记(没写完)
矩阵、高斯消元、行列式及相关结论学习笔记
矩阵的概念
矩阵可以简单理解为二维数组。在 OI 中,矩阵的元素一般为整数或实数。
矩阵 \(\boldsymbol A\) 的第 \(i\) 行、第 \(j\) 列元素记作 \(a_{ij}\)。
仅有一行的矩阵称为行向量,只有一列的矩阵称为列向量。
矩阵左上到右下的对角线称为主对角线,右上到左下的对角线称为副对角线。
矩阵的运算
矩阵加法
矩阵加法就是对应位置相加。只有同型矩阵才能进行加法运算。
例如:
矩阵加法满足:
- 结合律:\((\boldsymbol A+\boldsymbol B)+\boldsymbol C=\boldsymbol A+(\boldsymbol B+\boldsymbol C)\)。
- 交换律:\(\boldsymbol A+\boldsymbol B=\boldsymbol B+\boldsymbol A\)。
矩阵减法
矩阵减法就是对应位置相减。只有同型矩阵才能进行减法运算。
矩阵数乘
矩阵数乘就是每个位置相乘。
例如:
矩阵数乘满足:
- \(\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\)。其中:
例如:
矩阵乘法满足:
- 结合律:\((\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\) 的单位矩阵如下:
单位矩阵 \(\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\)。
例如:
矩阵转置满足:
- \((\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\) 倍加到另一行。
同理可以定义初等列变换。
增广矩阵
对于线性方程组:
定义它的增广矩阵为:
高斯消元
容易发现,对增广矩阵进行初等行变换不会改变方程组的解。
我们的目标就是利用初等行变换将增广矩阵变为如下形式:
则最右侧的一列就是方程组的解。如果无法变为该形式,则方程无解或无唯一解,可以进一步进行判定。
高斯消元的流程是:
- 初始时令 \(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;
}