數學篇之高斯消元


眾所周知,高斯消消樂很好玩(並不),並且理解很簡單,代碼也很“簡單”(蒟蒻到聽了n遍還是不會寫代碼,在此寫篇博客記下來)

高斯消元:

高斯消元是用來解線性方程組的,即把一個方程組的系數與方程右邊的數寫成一個矩陣,再解這個矩陣對應的行列式的值,就可以快速的求解

一個行列式有如下幾種初等變換:

1.交換兩行(列),行列式的值變號。

2.某行(列)的所有數,同時乘上k,行列式的值相當於原來的k倍(但某行(列)加上某個數的k倍值是不變的)

3.某行(列)的所有數同時加上a,行列式的值不變

so高斯消元應該怎么消呢?

(高斯消元以列為單位消)

step1:先將第一行第一列的數化為1,並且同一行的數做相同變換

step2:再將其余行的第一列化為0(通過加/減1的k倍),並且同一行的數做相同變換

step3:這時,第一行就處理完了,再將原本的第二行視作新的第一行,且新的第一列為原本的第二列(就是把原來矩陣的第一行第一列扔掉),做相同的變換,一直處理到原本的第n-1行,這樣矩陣就消完了。接下來就是回帶求解

當然,如果有一行全是0的時候會出現無解或有無窮多解,要特判。

因為這是解方程用的,所以輸入的矩陣每一行格式如下:

 系數1   系數2....................系數n    等號右邊的數(這點很重要)

總體格式如下:

|a11................a1n    b1|

|a21...............a2n     b2|

|...................................|

|an1...............ann     bn|

其中a都是系數,b為等號右邊的數

高斯消元的目的是把所有的a組成的矩陣消成單位矩陣,這樣最后消出來的方程就是

a11*x=b1

a22*y=b2

.....

ann*k=bn

所有的a和b都是消完后的數

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,mo=100000007;
double a[1001][1001];
int main()
{    bool sc=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)//讀入沒什么好說的
      for(int j=1;j<=n+1;j++)
         scanf("%lf",&a[i][j]);
     int bj,flag=0;
     for(int i=1;i<=n;i++)
       {bj=i;
          while(a[bj][i]==0&&bj<=n)
          bj+=1;
          if(bj==n+1){ flag=1;continue;//如果有一列全0,那么它就沒有價值了
          }
          for(int j=1;j<=n+1;j++)swap(a[i][j],a[bj][j]);//交換,使第一行第一列的數不為0
          double kk=a[i][i];//確保對角線上的數是1
          for(int j=1;j<=n+1;j++)
           {a[i][j]/=kk;//其余數跟着處理
           }
//接下來這一坨可能會比較難理解,在下面再講一下
for(int j=1;j<=n;j++)//這一坨就是把經過處理對角線后的矩陣消成純正的單位矩陣(就是除了對角線以外的數都清0) {if(i!=j){double k=a[j][i];//k=這行第一個數 for(int m=1;m<=n+1;m++) {a[j][m]-=k*a[i][m];//此行所有的數減去第一個數*k,使第一個數變0,當后面再處理時因為上個數已經是0了,所以沒有影響 } } } if(i==n)//消完輸出 {for(int j=1;j<=n;j++) {for(int m=1;m<=n+1;m++) cout<<a[j][m]<<" "; cout<<endl; sc=1; } } } if(!sc)printf("False");//sc就是輸出啦,如果無輸出,就代表無解 }

補鍋:

上面一坨東西只保證了對角線是1,但其他數還是一撮魑魅魍魎,我們要把這些魑魅魍魎都消成0.

可能用圖來表示要好一些  

這貨是對角線被處理過的玩意-------------->j從1開始,到n,模擬1到n行,同時,列用k代替。

            

    k每跑完1次,j就+1,即進行下一行。但i是不變的,這樣就是以i為“主體”,對i后面的每一行進行操作,使其變為一個更小的方陣。(因為每跑完一遍這一坨,第i列以前就被消成0,除了對角線,那它就消好了,后面就不用管了)這樣對角線確實會變,但下一輪循環i的時候對角線會被維護,且之前消成0的數不會被改變

其思路類似下圖

 第一遍                                                                          第二遍(消陰影部分)

 

                第三遍

 

其實就是把大矩陣消成小矩陣,一直到消完(白色部分是一定不會變化的數,也就是消好的對角線和0)

 補鍋*2:

    很多人問flag是干嘛用的,那是用來判斷是無解還是有無窮多解的東西,不過一時懶腦細胞耗盡沒有寫上

    如何判斷是有無窮解還是無解呢?

    既然我們有了flag=1(系數有一行全是0),那么這個方程組不是無解就是無窮多解

   什么情況下會無解?

    那就是有一行系數全是0,但是方程結果不是0,就會無解。

    這樣我們就可以把消完的矩陣全判一遍,如果都不是無解,就是有無窮多解了

   完整版代碼如下:

   

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,mo=100000007;
double a[1001][1001];
long double wucha=1e-9;
bool pd(int h)
{ if(a[h][n+1]<wucha)return 1;//這里double類型的數會有一個誤差,若這個數等於0,就是小於這個誤差
     for(int i=1;i<=n;i++)
       {if(a[h][i]>wucha)return 1;
        }
        return 0; 
}
int main()
{    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)//讀入沒什么好說的
      for(int j=1;j<=n+1;j++)
         scanf("%lf",&a[i][j]);
     int bj,flag=0;
     for(int i=1;i<=n;i++)
       {bj=i;
          while(a[bj][i]==0&&bj<=n)
          bj+=1;
          if(bj==n+1){ flag=1;continue;//如果有一列全0,那么它就沒有價值了
          }
          for(int j=1;j<=n+1;j++)swap(a[i][j],a[bj][j]);//交換,使第一行第一列的數不為0
          double kk=a[i][i];//確保對角線上的數是1
          for(int j=1;j<=n+1;j++)
           {a[i][j]/=kk;//其余數跟着處理
           }
          for(int j=1;j<=n;j++)//這一坨就是把經過處理對角線后的矩陣消成純正的單位矩陣(就是除了對角線以外的數都清0)
          {if(i!=j){double k=a[j][i];//k=這行第一個數
               for(int m=1;m<=n+1;m++)
               {a[j][m]-=k*a[i][m];//此行所有的數減去第一個數*k,使第一個數變0,當后面再處理時因為上個數已經是0了,所以沒有影響
               }
             }
           }
          if(i==n)//消完輸出
          {for(int j=1;j<=n;j++)
           {for(int m=1;m<=n+1;m++)
             cout<<a[j][m]<<" ";
             cout<<endl;
             
           }
          }
       }
      if(flag)
       {for(int i=1;i<=n;i++)
          if(!pd(i)){printf("No Solution!");return 0;
          }
          printf("Many Many Solutions!");//瞎寫的不要在意那么多細節(比如語法什么的) 
       } 
}

 

吐血三升后肝出來的高消

高斯消消樂真好玩QwQ

諸位大佬們有覺得注釋不詳細的地方說出來好不好


免責聲明!

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



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