高斯消元(Gauss消元)


眾所周知,高斯消元可以用來求 $n$ 元一次方程組的,主要思想就是把一個 $n*(n+1)$ 的矩陣的對角線消成 $1$,除了第 $n+1$ 列(用來存放 $b$ 的)的其他全部元素消成 $0$,是不是聽起來有點不可思議??!

$NO NO NO!$

這不就是初中學的代入消元和加減消元嘛,思路一樣的。

$Step 1:$將所給出的 $n$ 元一次方程組的每個未知數系數和等號后面的常數寫成一個 $n*(n+1)$ 的矩陣

 

比如這個三元一次方程組我們就可以寫成如下 $3×4$ 的矩陣:

$Step 2 :$運用矩陣的各種性質,來將矩陣消成對角線上的元素為 $1$,並且除了第 $n+1$ 列其余元素均為 $0$ 的矩陣,

這樣我們就很容易的得出每個未知數的值:分別是從上到下第 $n+1$ 列的值(因為這時候每個未知數的系數都為 $1$)

 那么是神馬神奇性質吶???找度娘啊

(1) 任意交換矩陣的兩行或兩列,矩陣不變;

(2)矩陣任意行或列 $a_i$ 加上或減去任意 $k$ 倍的任意行或列($a_i$ 行也可以加減 $k$ 倍的 $a_i$ 行),矩陣不變;

………………………………

其余的性質這里就用不到啦,這兩條性質足矣。

好啦,下面說一下怎么個消法(重點 嚶嚶嚶~)

以上面的矩陣為例:

明確我們的目的:把矩陣消成對角線為 $1$,除了第 $n+1$ 列其余元素都為 $0$。

也就是說,每一列都至少有一個元素不為 $0$,若有一列全為 $0$ 肯定有第 $i$ 行第 $i$ 列消不成 $1$,此時無解

不理解的話也可以從方程組的數學角度來思考一下:

我們把每個未知數的系數寫成矩陣,所以矩陣的某一列就是某一未知數的全部系數,

如果全為 $0$,那么不就是沒有這個未知數嗎?那么這個未知數的值就不能確定了,那不就是無解嗎?

知道了這個,我們就可以對這個矩陣進行初步判定:

for(int i=1;i<=n;i++)
    {
        pl=i;                      //從第i行開始往下找,一直找到一個第i列不為0的行
        while(a[pl][i]==0&&pl<=n) 
        pl++;                                    
         // 判斷第i列元素非0的最上行,因為第i行第i列元素不能為0 
        if(pl==n+1) {cout<<"No Solution";return 0;}    
        //一直判到了n+1行,可是一共才只有n行,說明有一列全為0,無解     
                 for(int j=1;j<=n+1;j++)            
                //將第i行元素與第pl行第i列不為0的那一行與當前行交換 
        swap(a[i][j],a[pl][j]);   //保證第i行第i列不為0
        }

這樣一來,我們就保證了第 $i$ 行第 $i$ 列的元素不為 $0$,可是我們要讓第 $i$ 行第 $i$ 列的值整成 $1$ 啊,我們可以用性質 $(2)$,讓第i行的每個元素都除以第 $i$ 行第 $i$ 列的值

注意:這里用到了除法,就有可能出現小數,所以我們要用 $double$ 類型定義二維數組矩陣

        double k=a[i][i];                          //讓第i行每個元素都除以a[i][i]使得a[i][i]為1 
        for(int j=1;j<=n+1;j++)
        a[i][j]=a[i][j]/k;                         //將第i行第i列的元素消成1,注意同行進行同樣的操作

我們就讓第 $i$ 行第 $i$ 列的元素搞成 $1$ 列,繼續完成接下來的任務:順便把第 $i$ 列的其他元素搞成 $0$;

我們已經把第 $i$ 行的搞成了$1$,所以我們只要把其余行的每個元素都減去本行的首元素*第 $i$ 行的對應元素(為什么是第 $i$ 行呢?仗着第 $i$ 行第 $i$ 列的元素是 $1$ 比較好消)

        for(int j=1;j<=n;j++)
        {
            if(i!=j)                        //將第i列除了第i行的元素全消成0 
            {                               //方法是第j行每個元素a[j][m]都減去a[j][1]*a[i][m] 
                double ki=a[j][i];
                for(int m=1;m<=n+1;m++)
                a[j][m]=a[j][m]-ki*a[i][m];
            }
        }
    

到這里就 $OK$ 啦,最后輸出第 $n+1$ 列的元素就是每個未知數的解啦!

完整代碼如下:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,pl;
double a[1001][1001];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
       for(int j=1;j<=n+1;j++)
       cin>>a[i][j];
    for(int i=1;i<=n;i++)
    {
        pl=i;
        while(a[pl][i]==0&&pl<=n) 
        pl++;                                    
         // 判斷第i列首元素非0的最上行,因為第i行第i列元素不能為0 
        if(pl==n+1) {cout<<"No Solution";return 0;}    
        //一直判到了n+1行,可是一共才只有n行,說明有一列全為0,無解 
        for(int j=1;j<=n+1;j++)             //將第i行第i列元素不為0的那一行與當前行交換 
        swap(a[i][j],a[pl][j]);
        double k=a[i][i];                          //讓第i行每個元素都除以a[i][i]使得a[i][i]為1 
        for(int j=1;j<=n+1;j++)
        a[i][j]=a[i][j]/k;                         //將第i行第i列的元素消成1,注意同行進行同樣的操作 
        for(int j=1;j<=n;j++)
        {
            if(i!=j)                        //將第i列除了第i行的元素全消成0 
            {                               //方法是第j行每個元素a[j][m]都減去a[j][1]*a[i][m] 
                double ki=a[j][i];
                for(int m=1;m<=n+1;m++)
                a[j][m]=a[j][m]-ki*a[i][m];
            }
        }
    }
    for(int i=1;i<=n;i++)
    printf("%.2lf\n",a[i][n+1]);
    return 0;
}

大家一定躍躍欲試了吧,給大家推薦一個洛谷板子題,鞏固一下吧。

【模板】高斯消元法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM