C++高斯消元詳解

前言

學了才發現這玩意賊簡單雖然據說是複習。

直接來

高斯消元用於解一個線性方程組,就是:
{a1,1x1+a1,2x2++a1,nxn=a1,n+1a2,1x1+a2,2x2++a2,nxn=a2,n+1an,1x1+an,2x2++an,nxn=an,n+1\begin{cases} a_{1,1}\cdot x_1+a_{1,2}\cdot x_2+\cdots+a_{1,n}\cdot x_n=a_{1,n+1}\\ a_{2,1}\cdot x_1+a_{2,2}\cdot x_2+\cdots+a_{2,n}\cdot x_n=a_{2,n+1}\\ \quad\quad\quad\quad\quad\quad\quad\quad\quad\cdots\\ a_{n,1}\cdot x_1+a_{n,2}\cdot x_2+\cdots+a_{n,n}\cdot x_n=a_{n,n+1}\\ \end{cases}(其中aa是已知量)


我們接下來都用係數表達式(注意所有的ai,n+1a_{i,n+1}與其他aa的含義有不同,但我們將它們放在一起):
a1,1a1,2a1,3a1,n1a1,na1,n+1a2,1a2,2a2,3a2,n1a2,na2,n+1a3,1a3,2a3,3a3,n1a3,na3,n+1an,1an,2an,3an,n1an,nan,n+1\begin{matrix} a_{1,1} & a_{1,2} & a_{1,3} & \cdots & a_{1,n-1} & a_{1,n} & a_{1,n+1}\\ a_{2,1} & a_{2,2} & a_{2,3} & \cdots & a_{2,n-1} & a_{2,n} & a_{2,n+1}\\ a_{3,1} & a_{3,2} & a_{3,3} & \cdots & a_{3,n-1} & a_{3,n} & a_{3,n+1}\\ & & & \cdots\\ a_{n,1} & a_{n,2} & a_{n,3} & \cdots & a_{n,n-1} & a_{n,n} & a_{n,n+1}\\ \end{matrix}


高斯消元的目的是把原方程變成這樣:
1a1,2a1,3a1,n1a1,na1,n+101a2,3a2,n1a2,na2,n+1001a3,n1a3,na3,n+100001an,n+1\begin{matrix} 1 & {a_{1,2}}' & {a_{1,3}}' & \cdots & {a_{1,n-1}}' & {a_{1,n}}' & {a_{1,n+1}}'\\ 0 & 1 & {a_{2,3}}' & \cdots & {a_{2,n-1}}' & {a_{2,n}}' & {a_{2,n+1}}'\\ 0 & 0 & 1 & \cdots & {a_{3,n-1}}' & {a_{3,n}}' & {a_{3,n+1}}'\\ & & & \cdots\\ 0 & 0 & 0 & \cdots & 0 & 1 & {a_{n,n+1}}'\\ \end{matrix}

那麼從下往上依次回代,就能得到所有的xx值了,代碼如下:

for(int i = N; i >= 1; i--) {
    Ans[i] = A[i][N + 1];
    for(int j = N; j >= i + 1; j--)
        Ans[i] -= Ans[j] * A[i][j];
}

所以我們枚舉ii:將ai,ia_{i,i}變爲11(①),然後把aj,i (j>i)a_{j,i}\ (j>i)全部變成00(②)。

  • 對於①操作,非常簡單,把第ii行的係數全部除以ai,ia_{i,i}即可;
  • 對於②操作,你要根據加減消元法,用第ii行的係數去把aj,i (i+1jn)a_{j,i}\ (i+1\leq j\leq n)消爲00,其他不管。那就給把ai,ia_{i,i}變成aj,ia_{j,i}ii行其他係數按等式的性質依次變),再將第jj行的每個係數aj,ka_{j,k}減掉ai,ka_{i,k}。即:第jj行的每個係數aj,ka_{j,k}都減去aj,iai,iai,k=aj,iai,k\dfrac{a_{j,i}}{a_{i,i}}\cdot a_{i,k}=a_{j,i}\cdot a_{i,k}

以上就是高斯消元的過程,自己照着代碼打一遍,模擬一下過程就能夠很好地理解了。


還有一個細節,我們每次在操作①和操作②之前把maxj=inaj,i\max\limits_{j=i}^{n}a_{j,i}所在的那行跟第ii行換一個位置,可以減少精度誤差。


還有一個細節,判斷無解:若當前ai,i=0a_{i,i}=0,那麼高斯消元就無法繼續了(操作②無法進行),且xix_i也不能確定,輸出No Solution(但並不一定無解)。

代碼

洛谷 P3389【模板】高斯消元法

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

int Read() {
    int x = 0;
    char c = getchar();
    while (c < '0' || c > '9')
        c = getchar();
    while (c >= '0' && c <= '9')
        x = x * 10 + (c ^ 48), c = getchar();
    return x;
}

const double eps = 1e-7;

inline double Abs(const double &x) {
    return x < 0 ? -x : x;
}

inline int Comp(const double &x,const double &y) {
    if (Abs(x-y) < eps)
        return 0;
    return Abs(x) > Abs(y) ? 1 : -1;
}

const int MAXN = 100;

int N;
double A[MAXN + 5][MAXN + 5], Ans[MAXN + 5];

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 pos = i;
        for(int j = i + 1; j<= N; j++)
            if(Comp(A[j][i], A[pos][i]) == 1)
                pos = j;
        // 這裏找A[j][i]最大的是爲了減少精度誤差 與算法正確性無關
        if(Comp(A[pos][i], 0) == 0) {
            puts("No Solution");
            return 0;
        }
        for(int j = 1; j <= N + 1; j++)
            std::swap(A[pos][j], A[i][j]);
        // 把A[j][i]最大的那行換上去
        double tmp = A[i][i];
        for(int j = i; j <= N + 1; j++)
            A[i][j] /= tmp;
        // 操作一
        for(int j = i + 1; j <= N; j++){
            tmp = A[j][i];
            for(int k = i; k <= N + 1; k++)
                A[j][k] -= A[i][k] * tmp;
        }
        // 操作二
    }
    for(int i = N; i >= 1; i--) {
        Ans[i] = A[i][N + 1];
        for(int j = N; j >= i + 1; j--)
            Ans[i] -= Ans[j] * A[i][j];
    }
    for(int i = 1; i <= N; i++)
        printf("%.2f\n", Ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章