「筆記」高斯消元


消元法

先來看一下百度百科的定義:

消元法是指將許多關系式中的若干個元素通過有限次地變換,消去其中的某些元素,從而使問題獲得解決的一種解題方法。

可能不好懂。

回想一下小學數學中解二元一次方程的方法

比如下面這個二元一次方程:

\[\begin{cases} x + y = 10\\ x - y = 6 \end{cases} \]

通過兩式相加,可以得到

\[2x = 16 \]

進而解得

\[x=8 \]

\(x\) 代回就可以得到

\[y = 2 \]

發現,在解這個二元一次方程時,通過式子加減,我們把其中一個元 \(y\) 消掉,只剩下了 \(x\),變成了我們可以直接得出解的形式。

這個解題方法就是消元法

高斯消元其實就是在模擬這個消元過程。

只不過元可能更多,式子可能更復雜,但好在這是計算機考慮的問題,與我們沒有什么關系。

高斯消元

高斯-約旦消元法

與后面高斯消元法相比沒有回帶過程,實現起來也更加簡單,精度方面也更加優秀

高斯消元的一般步驟是:

  • 列出要進行消元的矩陣
  • 枚舉每一列
  • 設枚舉到第 \(i\) 列,找到第 \(i\) 列中系數最大的方程並將其交換到第 \(i\)
  • 同過確定系數,加減消元等方法,將其他方程中的這個元的系數化為 \(0\)
  • 求出每個未知數的解

進行高斯消元后是一個三角矩陣,而高斯-約旦消元法的結果是一個對角線矩陣

舉個例子:

求解下面這個方程組

規定從上到下三個式子分別是 \(A\), \(B\), \(C\)

\[\begin{cases} 3x + 2y + z = 10 \\ 5x + y +6z = 25 \\ 2x + 3y + 4z = 20 \end{cases} \]

把它轉化成矩陣形式

\[\begin{bmatrix} 3 & 2 & 1 & \mid & 10 \\ 5 & 1 & 6 & \mid & 25 \\ 2 & 3 & 4 & \mid & 20 \end{bmatrix} \]

左邊是每個未知數的常數項,右邊是該式結果,中間用一條豎線隔開

我們從第一列開始消元

找到第一列最大的一個,與第一行交換

\[\begin{bmatrix} 5 & 1 & 6 & \mid & 25 \\ 3 & 2 & 1 & \mid & 10 \\ 2 & 3 & 4 & \mid & 20 \end{bmatrix} \]

我們要將第一列的其他兩項化為 \(0\)

\(B = B - \frac{3}{5}A\)

\(C = C - \frac{2}{5}A\)

然后變成這樣:

\[\begin{bmatrix} 5 & 1 & 6 & \mid & 25 \\ 0 & \frac{7}{5} & -\frac{13}{5} & \mid & -5 \\ 0 & \frac{12}{5} & \frac{2}{5} & \mid & 5 \end{bmatrix} \]

然后在看第二列,把第二列系數最大的那個方程換到第二列。

\[\begin{bmatrix} 5 & 1 & 6 & \mid & 25 \\ 0 & \frac{12}{5} & \frac{2}{5} & \mid & 5 \\ 0 & \frac{7}{5} & -\frac{13}{5} & \mid & -5 \end{bmatrix} \]

繼續進行消元操作,不過這次我們要把方程 \(A\) 的第二列的元也消掉

\(A = A - \frac{5}{12}B\)

\(C = C - \frac{7}{12}B\)

矩陣變為:

\[\begin{bmatrix} 5 & 0 & \frac{35}{6} & \mid & \frac{275}{12} \\ 0 & \frac{12}{5} & \frac{2}{5} & \mid & 5 \\ 0 & 0 & \frac{17}{6} & \mid & \frac{95}{12} \end{bmatrix} \]

繼續對第三列進行相同操作,矩陣變為:

\[\begin{bmatrix} 5 & 0 & 0 & \mid & \frac{225}{34} \\ 0 & \frac{12}{5} & 0 & \mid & 5 \\ 0 & 0 & \frac{17}{6} & \mid & \frac{66}{17} \end{bmatrix} \]

到此消元結束,發現每個方程只剩下一個變量,直接解出來輸出結果即可。

如果不懂的話可以看一下代碼實現。

/*
Work by: Suzt_ilymics
Problem: P3389 【模板】高斯消元法
Knowledge: 高斯-約旦消元法 
Time: O(n^3)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 105;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n;
double a[MAXN][MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int main()
{
    n = read();
    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 Max = i; 
        for(int j = i + 1; j <= n; ++j) if(fabs(a[j][i]) > fabs(a[Max][i])) Max = j; // 選出該列最大系數 
        for(int j = 1;j <= n + 1; ++j) swap(a[i][j], a[Max][j]);  // 交換這兩行 
        if(!a[i][i]) { // 最大值等於 0 則說明該列都為 0,肯定無解 
            puts("No Solution");
            return 0;
        }
        for(int j = 1; j <= n; ++j) { // 對每一行進行加減消元 
            if(j != i) {
                double tmp = a[j][i] / a[i][i];
                for(int k = i + 1; k <= n + 1; ++k) a[j][k] -= a[i][k] * tmp;
            }
        }
    }
    for(int i = 1; i <= n; ++i) printf("%.2lf\n", a[i][n + 1] / a[i][i]);
    return 0;
}

高斯消元法

沒寫過。

實現起來與高斯-約旦類似,添了一個回帶的過程

在這里粘一個 皎月半撒花 大佬的板子

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
double map[111][111];
double ans[111];
double eps=1e-7;
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
            scanf("%lf",&map[i][j]);
    for(int i=1;i<=n;i++){
        int r=i;
        for(int j=i+1;j<=n;j++)
            if(fabs(map[r][i])<fabs(map[j][i]))
                r=j;//find_the_biggest_number_of_the_first_column(at present) 
        if(fabs(map[r][i])<eps){
            printf("No Solution");
            return 0;
        }
        if(i!=r)swap(map[i],map[r]);//對換一行或一列,屬於找最大當前系數的其中一步。(這樣就可以只處理當前行的系數啦!) 
        double div=map[i][i];
        for(int j=i;j<=n+1;j++)
            map[i][j]/=div;
        for(int j=i+1;j<=n;j++){
            div=map[j][i];
            for(int k=i;k<=n+1;k++)
                map[j][k]-=map[i][k]*div;
        }
    }
    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]-=(map[i][j]*ans[j]);
    }//回帶操作
    for(int i=1;i<=n;i++)
        printf("%.2lf\n",ans[i]);
}


免責聲明!

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



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