高斯消元解方程组学习笔记

以下是正经的搞死小圆学习笔记!


---
数学课上,小圆遇到了一道六元一次方程组的题。颓废的小圆并不想做这道题,作为代课数学老师你决定搞死...不是,帮小圆解答这道题。请你设计程序解任意n元1次方程组。n<=100

【分析】

高斯消元隆重登场。

让我们先来替小圆模拟一下一个三元一次方程的解法(瞎写的,可能系数略恶心)
```math
3x+2y+4z=19   \ \ \ \  (1)

x-y+z=2 \ \ \ \ \ \ \ \ \ \ \ \ (2)

2x+y-3z=-5 \ \ \ \ \ (3)
```
回想小学高年级学到的解方程方法,我们可以通过联立方程消元,从而变成解一元一次方程的问题。

不妨先以(1)为未知数x的基准:
```math
x+ \frac{2}{3}y+\frac{4}{3}=\frac{19}{3} \ \ \   (1)'
```
对于(2)(3),需要在x与基准相同的情况下进行两式相减。一般来说,我们选择用x系数为1的(1)'来“凑”其他待消项的x的系数。

```math
(-1-\frac{2}{3})y+(1-\frac{3}{4}z)=(2-\frac{19}{3})z

→-\frac{5}{3}y-\frac{1}{3}z=-\frac{13}{3} \ \ \ (2)'
```
如此这般,最终消去y,得到z=1

下一步重要的操作是回代,本质是代入消元。

需要注意的是,在刚刚我们用(1)'消去其他方程中的x之后,(1)'就没有再次使用了,而且(1)'中x的系数为1。

遵循同样的方法,我们发现每个方程留下的最后形式,是用它消去的未知数的系数为1的情况,且该方程中的未知数只有这个数及其之后被消的数。

所以,我们回代时只需要用等号右边减去已求的所有数*它们的系数即可。

由于z是第一个被求出来的数,可以发现,回代需要依照倒序。

*考虑几个特殊情况:

1、解方程过程中发现当前未知数的系数在现在及之后的所有方程都为0——也就是说,这个未知数可以取任何值。输出"No Solution"(不存在唯一解)
即可。

2、我们希望关于未知数i的基准是剩余所有方程中i的系数绝对值最大的一个,为了保证答案的精准度。证明戳→[dalao](https://pks-loving.blog.luogu.org/p3389-mu-ban-gao-si-xiao-yuan-fa)

3、想清楚哪些地方应该用double,哪里应该与eps比较(嘤嘤嘤

最后贴代码~
```
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAXN 105
#define eps 1e-7
double map[MAXN][MAXN],ans[MAXN];
//map存方程,ans存未知数的解
int n;
bool work(){
    for(int i=1;i<=n;i++){
        //以下是挑选绝对值最大的系数的过程
        int r=i;
        for(int j=i+1;j<=n;j++){
            if(fabs(map[j][i])>fabs(map[r][i]))
              r=j;
        }
        //情况1.最大都是0???
        if(fabs(map[r][i])<eps)
            return false;
            //无数组解
        if(r!=i)//需要交换
          swap(map[i],map[r]);
        double tmp=map[i][i];
                
        //以下是消元过程
        for(int j=i;j<=n+1;j++){//将基准方程中最前未知数系数化为1
            map[i][j]/=tmp;
        }

        for(int j=i+1;j<=n;j++){//其他方程消元
            tmp=map[j][i];
            for(int k=i;k<=n+1;k++)
              map[j][k]-=tmp*map[i][k];
        }
    }
    
    //以下是回代过程
    ans[n]=map[n][n+1];
    for(int i=n-1;i>=1;i--){
        ans[i]=map[i][n+1];
        for(int j=i+1;j<=n;j++)
          ans[i]-=(ans[j]*map[i][j]);
    }
    return true;
    //成功求出所有未知数
}

void print(){
    for(int i=1;i<=n;i++)
      printf("%.2lf\n",ans[i]);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n+1;j++)
        scanf("%lf",&map[i][j]);
        
    if(!work())
          cout<<"No Solution"<<endl;
    else
        print();
    return 0;
}
```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值