第三十四章 数论——高斯消元解线性方程组
- 一、高斯消元
- 1、线性方程组
- 2、高斯消元步骤
- (1)数学知识铺垫
- 增广矩阵和阶梯矩阵
- 初等变换
- (2)高斯消元步骤
- 二、代码模板
- 1、问题:
- 2、代码
一、高斯消元
1、线性方程组
我们在小学的时候接触过二元一次方程组,我们可以用带入消元的方式去解方程,当然我们也可以通过对式子做等价变形,然后两个式子通过加减法的运算消元。
不管我们采取哪一种方式,我们的目的就是转化为一元一次方程组,这样我们就能够解出来了。
那么当我们的元数增多的时候,也就是我们的未知数增多的时候,我们的方程数目也会增多,那么我们现在先不管运算结果,我们先来讨论一下,多元一次方程组什么时候有解,什么时候无解。
很明显,n个未知数需要n个不同的方程求解。
如果说其中两个方程矛盾了,那么就是无解。
如果我们方程组中,有几个方程是一样的,那么说明我们的方程的数量是少于我们的未知数的数量的,此时我们的方程就有多个解。那么什么叫方程是一样的呢?
比如:
x
+
y
=
1
x+y=1
x+y=1和
2
x
+
2
y
=
2
2x+2y=2
2x+2y=2这两个方程就是一样的,我们可以通过后面的这个方程推导出前面的这个方程。
那么此时就有多个解。
如果说我们恰好n个未知数,n个不同的方程,那么此时就有唯一解。
了解了解的个数之后,我们来思考一下,如何去解呢?
其实本质还是如何利用消元去解方程。而高斯消元算法就给出了一个统一的步骤消元。我们接着往下看。
2、高斯消元步骤
(1)数学知识铺垫
增广矩阵和阶梯矩阵
对于方程组:
{ a 11 x 1 + a 12 x 2 + . . . + a 1 n x n = b 1 a 21 x 1 + a 22 x 2 + . . . + a 2 n x n = b 2 . . . a n 1 x 1 + a n 2 x 2 + . . . + a n n x n = b n \begin{cases} a_{11}x_1+a_{12}x_2+...+a_{1n}x_n=b_1\\ a_{21}x_1+a_{22}x_2+...+a_{2n}x_n=b_2\\ ...\\ a_{n1}x_1+a_{n2}x_2+...+a_{nn}x_n=b_n \end{cases} ⎩ ⎨ ⎧a11x1+a12x2+...+a1nxn=b1a21x1+a22x2+...+a2nxn=b2...an1x1+an2x2+...+annxn=bn
我们将每个方程的系数拿出来,我们能写出一个 n ∗ n n*n n∗n的矩阵。如果在加上方程右侧的结果,我们就能写出一个 n ∗ ( n − 1 ) n*(n-1) n∗(n−1)的矩阵。如下图所示:
{ a 11 a 12 . . . a 1 n b 1 a 21 a 22 . . . a 2 n b 2 . . . a n 1 a n 2 . . . a n n b n \begin{cases} a_{11}\;\;a_{12}\;\;...\;\;a_{1n}\;\;b_1\\ a_{21}\;\;a_{22}\;\;...\;\;a_{2n}\;\;b_2\\ ...\\ a_{n1}\;\;a_{n2}\;\;...\;\;a_{nn}\;\;b_n\\ \end{cases} ⎩ ⎨ ⎧a11a12...a1nb1a21a22...a2nb2...an1an2...annbn
我们可以通过方程之间的等价变形,加减消元,消去一些系数,然后将增广矩阵中的一些数字去掉,就构成了我们的阶梯矩阵,如下图所示。
{ a 11 a 12 . . . a 1 n b 1 a 22 . . . a 2 n b 2 . . . . . . a n n b n \begin{cases} a_{11}\;\;a_{12}\;\;...\;\;a_{1n}\;\;b_1\\ \;\;\;\;\;\;\;a_{22}\;\;...\;\;a_{2n}\;\;b_2\\ ......\\ \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;a_{nn}\;\;b_n\\ \end{cases} ⎩ ⎨ ⎧a11a12...a1nb1a22...a2nb2......annbn
当变成阶梯矩阵的时候,我们最后一个方程是不是就可以求解了。然后我们依次向上逐步求解方程。
而将增广矩阵转化为阶梯矩阵的过程就需要用到我们的高斯消元。
初等变换
初等变换可以理解为对上述的增广矩阵做等价变形。
以下三种变换是不影响结果的:
- 把某一行乘一个非零的数字。
- 交换某两行。
- 把某行的若干倍加到另一行上去。
上述三种初等变换是高斯消元的基础,大家务必要理解。其实对矩阵变换,就是对方程组变换,因此,大家通过方程组去理解这三行就会发现是等价的了。
(2)高斯消元步骤
枚举增广矩阵的每一列C:
- step1:找到该列绝对值最大的一行
- step2:将该行换到最上面(未确定阶梯型的行,并不一定是第一行)
- step3:将该行的第一个数变成1
- step4:将下面所有行的第C列消成0
- step5:当转化为阶梯矩阵的时候,从最后一行开始,向上捯,将上面行的该位变成0,当前n列只含有一个1,这个1所对的未知数就等于第n+1列的值。
二、代码模板
1、问题:
2、代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
const double eps = 1e-8;
int n;
double a[N][N];
int gauss()
{
int c, r;//c是当前系数需要变成1的列,r是未确定的行。
for (c = 0, r = 0; c < n; c ++ )
{
int t = r;//默认当前行就是C列的系数的绝对值最大的行。
//去遍历剩下的行,寻找C列系数的绝对值最大的行
for (int i = r; i < n; i ++ )
if (fabs(a[i][c]) > fabs(a[t][c]))
t = i;
/*如果第C列的最大值是0,说明无需进行后续的操作了。说明该行所对的方程与别的方程重复了,或者是矛盾的。
若重复了,则是多个解,若矛盾了则是无解。
*/
if (fabs(a[t][c]) < eps) continue;
//将系数绝对值最大的行放到r行。
for (int i = c; i <= n; i ++ ) swap(a[t][i], a[r][i]);
//将该行的第C列所对的系数变成1
for (int i = n; i >= c; i -- ) a[r][i] /= a[r][c];
//变成1之后,将下面行的该列变成0
for (int i = r + 1; i < n; i ++ )
if (fabs(a[i][c]) > eps)
for (int j = n; j >= c; j -- )
a[i][j] -= a[r][j] * a[i][c];
//该行确定了,r++
r ++ ;
}
//如果r<n,说明有几个方程是不确定的,那么我们就看是矛盾了还是多解了。
if (r < n)
{
/*0-n-1列都是0,如果n列不是0,说明0=一个数,则矛盾,无解*/
for (int i = r; i < n; i ++ )
if (fabs(a[i][n]) > eps)
return 2;//矛盾返回2
return 1; //反之有无限个解。
}
/*若有解,从最后一行网上计算消元,最后第n-1列就是答案*/
for (int i = n - 1; i >= 0; i -- )
for (int j = i + 1; j < n; j ++ )
a[i][n] -= a[i][j] * a[j][n];
return 0;
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n + 1; j ++ )
scanf("%lf", &a[i][j]);
int t = gauss();
if (t == 2) puts("No solution");
else if (t == 1) puts("Infinite group solutions");
else
{
for (int i = 0; i < n; i ++ )
{
if (fabs(a[i][n]) < eps) a[i][n] = 0;
printf("%.2lf\n", a[i][n]);
}
}
return 0;
}