第三十四章 数论——高斯消元解线性方程组

一、高斯消元

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 nn的矩阵。如果在加上方程右侧的结果,我们就能写出一个 n ∗ ( n − 1 ) n*(n-1) n(n1)的矩阵。如下图所示:

{ 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;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值