前言
仍然是自闭的一天。
学一学搞死高斯消元弄昏一下脑子。
概述
高斯消元主要用于求解线性方程组,也可以求矩阵的秩,矩阵的逆等。该算法的时间复杂度与方程组个数,未知数个数等有关。一般的时间复杂度为 O ( n 3 ) O(n^3) O(n3)
线性方程组
含有多个一次未知数的方程组,形如
{
a
1
,
1
x
1
+
a
1
,
2
x
2
+
a
1
,
3
x
3
+
.
.
.
+
a
1
,
m
x
m
=
b
1
a
2
,
1
x
1
+
a
2
,
2
x
2
+
a
2
,
3
x
3
+
.
.
.
+
a
2
,
m
x
m
=
b
2
a
3
,
1
x
1
+
a
3
,
2
x
2
+
a
3
,
3
x
3
+
.
.
.
+
a
3
,
m
x
m
=
b
3
⋯
a
n
,
1
x
1
+
a
n
,
2
x
2
+
a
n
,
3
x
3
+
.
.
.
+
a
n
,
m
x
m
=
b
n
\left\{ \begin{aligned} &a_{1,1}x_1+a_{1,2}x_2+a_{1,3}x_3+...+a_{1,m}x_m =b_1\\ &a_{2,1}x_1+a_{2,2}x_2+a_{2,3}x_3+...+a_{2,m}x_m =b_2\\ &a_{3,1}x_1+a_{3,2}x_2+a_{3,3}x_3+...+a_{3,m}x_m =b_3\\ & \quad \quad\quad\quad\quad\quad\quad\quad\quad \cdots \\ &a_{n,1}x_1+a_{n,2}x_2+a_{n,3}x_3+...+a_{n,m}x_m =b_n\\ \end{aligned} \right.
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧a1,1x1+a1,2x2+a1,3x3+...+a1,mxm=b1a2,1x1+a2,2x2+a2,3x3+...+a2,mxm=b2a3,1x1+a3,2x2+a3,3x3+...+a3,mxm=b3⋯an,1x1+an,2x2+an,3x3+...+an,mxm=bn
记作矩阵形式:
A = [ a 1 , 1 a 1 , 2 a 1 , 3 ⋯ a 1 , m a 2 , 1 a 2 , 2 a 2 , 3 ⋯ a 2 , m a 3 , 1 a 3 , 2 a 3 , 3 ⋯ a 3 , m ⋯ ⋯ ⋯ ⋯ ⋯ a n , 1 a n , 2 a n , 3 ⋯ a n , m ] , B = [ b 1 b 2 b 3 ⋯ b n ] , A x = B A = \begin{bmatrix} a_{1,1} &a_{1,2} &a_{1,3} &\cdots &a_{1,m} \\ a_{2,1} &a_{2,2} &a_{2,3} &\cdots &a_{2,m} \\ a_{3,1} &a_{3,2} &a_{3,3} &\cdots &a_{3,m} \\ \cdots &\cdots &\cdots &\cdots &\cdots\\ a_{n,1} &a_{n,2} &a_{n,3} &\cdots &a_{n,m} \\\end{bmatrix},\ \ \ \ B = \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ \cdots \\ b_n\end{bmatrix},\ \ \ Ax=B A=⎣⎢⎢⎢⎢⎡a1,1a2,1a3,1⋯an,1a1,2a2,2a3,2⋯an,2a1,3a2,3a3,3⋯an,3⋯⋯⋯⋯⋯a1,ma2,ma3,m⋯an,m⎦⎥⎥⎥⎥⎤, B=⎣⎢⎢⎢⎢⎡b1b2b3⋯bn⎦⎥⎥⎥⎥⎤, Ax=B
单位矩阵
在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1,除此以外全都为0。
根据单位矩阵的特点,任何矩阵与单位矩阵相乘都等于本身。
单位矩阵
增广矩阵
又称扩增矩阵,就是在系数矩阵的右边添上一列,这一列是线性方程组的等号右边的值。
矩阵的初等变换
矩阵的初等变换分为矩阵的初等行变换和矩阵的初等列变换。矩阵的初等行变换和矩阵的初等列变换统称为初等变换。
等价
如果矩阵 B B B可以由 A A A通过一系列初等变换得到,则称矩阵 A A A和 B B B等价。
初等行变换
数域 P P P上的初等行变换的定义:下面三种变换:
- 以 P P P中某个非 0 0 0数乘矩阵的某一行
- 把矩阵某一行的 C C C倍加到另一行, C C C为P中任意一个数
- 互换矩阵某两行的位置
若矩阵 A A A通过初等行变换变成了矩阵 B B B,写作 A → B A \rightarrow B A→B
任意矩阵通过一系列初等行变换都能转化为阶梯矩阵;若该矩阵为方阵,则为上三角矩阵。
初等列变换
与初等行变换进行类比,得到定义:
- 以 P P P中某个非 0 0 0数乘矩阵的某一列
- 把矩阵某一行的 C C C倍加到另一列, C C C为P中任意一个数
- 互换矩阵某两列的位置
初等矩阵
定义:由单位矩阵
E
E
E经过一系列初等变换后得到的矩阵
E
′
E'
E′,称作初等矩阵。
引理:
对一个
n
∗
m
n*m
n∗m的矩阵
A
A
A作一次初等行变换,等价于在
A
A
A左边乘上一个
n
∗
n
n*n
n∗n的相应初等矩阵
对一个
n
∗
m
n*m
n∗m的矩阵
A
A
A作一次初等列变换,等价于在
A
A
A右边乘上一个
m
∗
m
m*m
m∗m的相应初等矩阵
高斯消元法
具体步骤
得到一个方程组。例如:
{
2
x
1
+
1
x
2
+
1
x
3
=
1
4
x
1
+
7
x
2
+
8
x
3
=
31
5
x
1
+
2
x
2
+
1
x
3
=
4
\left\{ \begin{aligned} &2x_1+1x_2+1x_3=1\\ &4x_1+7x_2+8x_3=31\\ &5x_1+2x_2+1x_3=4\\ \end{aligned} \right.
⎩⎪⎨⎪⎧2x1+1x2+1x3=14x1+7x2+8x3=315x1+2x2+1x3=4
将它转换为增广矩阵:
[
2
1
1
1
4
7
8
31
5
2
1
4
]
\left[ \begin{array}{ccc|c} 2 &1 &1 & 1 \\ 4 & 7 & 8 & 31 \\ 5 & 2& 1 & 4 \end{array} \right]
⎣⎡2451721811314⎦⎤
- 首先,找到第一个第一列不为 0 0 0的行(一般找绝对值最大的,如果都为 0 0 0就跳过当前步)。
- 然后把这个数带入下面的方程组,把下面的第一列换掉代入,那么下面方程组的第一列都化为 0 0 0了。
例如这个方程,先找最大的第一列
[
5
2
1
4
4
7
8
31
2
1
1
1
]
\left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 4 & 7 & 8 & 31 \\ 2 &1 &1 & 1 \end{array} \right]
⎣⎡5422711814311⎦⎤
(具体的步骤就不说了)代入消元:
[
5
2
1
4
0
27
5
36
5
139
5
0
1
5
3
5
−
3
5
]
\left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 0 & \frac{27}{5} & \frac{36}{5} & \frac{139}{5} \\ 0 &\frac{1}{5} &\frac{3}{5} & -\frac{3}{5} \end{array} \right]
⎣⎡50025275115365345139−53⎦⎤
3. 除了最后一列,对于每列都这样找一行消掉。因为有解的方程组是个方阵,最终会转换为上三角矩阵。
消成上三角矩阵:
[
5
2
1
4
0
27
5
36
5
139
5
0
0
−
9
27
−
776
135
]
\left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 0 & \frac{27}{5} & \frac{36}{5} & \frac{139}{5} \\ 0 & 0 & -\frac{9}{27} & -\frac{776}{135} \end{array} \right]
⎣⎡500252701536−27945139−135776⎦⎤
很显然,就可以从下往上,依次得到
x
3
,
x
2
,
x
1
x_3,x_2,x_1
x3,x2,x1了。
Code:
普通的(湘妹儿的):
bool gauss(int n,db a[][M])
{
bool flag=1;
for (int i = 1;i <= n; ++ i)
{
int Max = i;
for (int r = i + 1; r <= n; ++ r)
if (cmp(a[Max][i], a[r][i]) == -1)
Max = r;
swap(a[i], a[Max]);
if (cmp(a[i][i], 0) == 0)
{
flag = 0;
continue;
}
for (int j = 1; j <= n; ++ j)
{
if (i == j || cmp(a[j][i], 0) == 0)
continue;
for (int k = i + 1; k <= n + 1; ++ k)
a[j][k] -= a[i][k] * a[j][i] / a[i][i];
a[j][i] = 0;
}
}
return flag;
}
异或的:
```cpp
LL Gauss(LL N,LL M)
{
LL tra, col;
for (tra = 1, col = 1; tra <= N && col <= M; ++ tra, ++ col)
{
int Maxx = tra;
for (Int i = tra; i <= N; ++ i)
if (B[i][col])
{
Maxx = i;
break;
}
if (! B[Maxx][col])
{
tra --;
continue;
}
if (Maxx != tra)
for (Int i = col; i <= M + 1; ++ i)
Swap(B[Maxx][i], B[tra][i]);
for (Int i = tra + 1; i <= N; ++ i)
{
if (! B[i][col])
continue;
for (Int j = col; j <= M + 1; ++ j)
B[i][j] ^= B[tra][j];
}
}
for (Int i = tra; i <= N; ++ i)
if (B[i][col])
return -1;
return N - tra;
}
高斯-约旦消元法
特点
精度更高,代码更短,不用回带。
基本思想
模拟加减消元,将每个方程转化为 k i x i = b i k_ix_i=b_i kixi=bi的形式,然后带回求得每个 x i x_i xi
具体思路
- 选择一个没被解决过的未知数作为主元。再选择包含这个未知数的一个方程
- 将这个方程主元的系数化为 1 1 1
- 将消元的式子带入其他方程加减消元,消去这个主元。(包括当前方程,这也是高斯-约旦消元法和普通高斯消元法的区别所在)
- 重复步骤,将方程组化为对角矩阵(即系数在对角线上,而普通高斯消元则是转化为上三角矩阵)。然后求解即可。
其余操作与普通高斯消元相似。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define MAXN 105
#define LL long long
#define Int register int
using namespace std;
DB A[MAXN][MAXN];
DB FABS(DB x)
{
if (x > 0)
return x;
return -x;
}
void Swap(DB &x,DB &y)
{
DB temp = x;
x = y;
y = temp;
}
int main()
{
int n;
scanf("%d", &n);
for (Int i = 1; i <= n; ++ i)
for (Int j = 1; j <= n + 1; ++ j)
scanf("%lf", &A[i][j]);
for (Int i = 1; i <= n; ++ i) // 枚举列
{
int Maxx = i;
for (Int j = i + 1; j <= n; ++ j) // 找绝对值最大的
if (FABS(A[j][i]) > FABS(A[Maxx][i]))
Maxx = j;
for (Int j = 1; j <= n + 1; ++ j)
Swap(A[Maxx][j], A[i][j]);
if (! A[i][i]) // 没有这个未知数,无解(或无数解)
return !printf("No Solution\n");
for (Int j = 1; j <= n; ++ j)
{
if (j != i)
{
DB temp = A[j][i] / A[i][i];
for (Int k = i + 1; k <= n + 1; ++ k)
A[j][k] -= A[i][k] * temp;
}
}
}
for (Int i = 1; i <= n; ++ i)
printf("%.2lf\n", A[i][n + 1] / A[i][i]);
return 0;
}