高斯消元。。。當初以為自己學會了。。。後來。。。
話說這個東西好像最早出現於《九章算術》誒(古代人就是強)
廢話不說,進入正題。。。
前置知識
高斯消元法是解線性方程組的方法之一
首先,線性方程組瞭解一下:
可認為線性方程組就是一次方程組。如圖:
如果存在常數c1,c2,c3,...,cn代替x1,x2,x3,...,xn,使上圖的每個方程成立,則稱(c1,c2,c3,...,cn)為方程組的一個解. 解方程就是求其全部解
顯然對線性方程組進行以下三種變換,所得方程組解集不變(這三種變換被稱為同解變換)
(1) 對換兩個方程(換法變換)(其實就是交換兩個方程的位置,上下交換) ;
(2) 用非零數 c 乘以某一個方程(倍法變換);
(3) 把某一個方程的 k 倍加到另一個方程上去( 消法變換) .
的確顯然吧...
再來了解一下矩陣(=_=):
由 s*n 個數 aij ( i = 1, 2, … , s; j = 1, 2, … , n) 排成的矩形表(可理解為長是n,寬是s的二維陣列。。),稱為 s 行 n 列矩陣 .aij稱為矩陣的( i , j) 元 .通常用大寫字母或 ( aij )sn 表示矩陣 .如果 s = n(長等於寬)稱A是 n 階方陣或 n 階矩陣 .
全零矩陣記做Osn,或O;
n階單位矩陣記做Inn,或I(單位矩陣是個方陣,從左上角到右下角的對角線(稱為主對角線)上的元素均為1。除此以外全都為0。)
對於矩陣(aij)sn、(bij)ns,若aij=bji(1<=i<=s,1<=j<=n)則稱B為A的轉置矩陣,記做B=AT
另:係數矩陣和增廣矩陣
把線性方程組的係數aij列入矩陣,稱為係數矩陣。
如圖:
而在係數矩陣右側加入一列常數項,稱其為增廣矩陣。
如圖:
(好吧增廣矩陣就是類似把未知數扣掉了再扔到矩陣裡。。)
對矩陣的行(列)做 線性方程組 的三種同解變換(上面的),稱為矩陣的初等變換。
高斯消元法
對方程組的增廣矩陣做初等變換,將係數部分化為對角線,上三角形,或階梯形
思路:找到一個不為零的係數,設其所在行為pos,列為i,然後拿這個數去消掉其它行,但同列的數;但是消元的時候要有一定順序
步驟:
先把一個個方程扔到矩陣中,構建增廣矩陣
然後從第i行(i從1到n,按順序)開始,在i+1行到n中的第i列(上面的方程已經符合上三角性質了)選取一個不為零的係數,記其所在行為pos,
交換第pos行與第i行,然後pos行變成了第i行(注:後面的第i行為交換完後的,即交換前的pos行)
至於為什麼要找最大的,這樣有利於提高精度。。(學長說的。。)
於是拿第i行的第i項去消i+1行到n行的第i項
這樣可以消成上三角矩陣;
然後從最後一行開始,向上消去不需要的係數(最後一行只有一個係數,可以直接計算答案)
程式碼(Luogu 模板):
#include<cstdio> #include<iostream> #define R register int using namespace std; const double eps=1E-11; inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } int n; double a[110][110]; inline bool ck0(double x) {return x<eps&&x>-eps;} inline void Gauss() { for(R i=1,pos;i<=n;++i) { pos=i; for(R j=i+1;j<=n;++j) if(ck0(a[pos][i])||a[pos][i]<a[j][i]) pos=j; if(pos!=i) for(R j=1;j<=n+1;++j) swap(a[i][j],a[pos][j]); if(ck0(a[i][i])) {printf("No Solution\n"); return ;} for(R j=i+1;j<=n;++j) { register double tmp=a[j][i]/a[i][i]; for(R k=1;k<=n+1;++k) a[j][k]-=tmp*a[i][k]; } } for(R i=n;i>=1;--i) { for(R j=n;j>i;--j) a[i][n+1]-=a[i][j]*a[j][n+1];//把這行係數代表的答案消掉 a[i][n+1]/=a[i][i]; } for(R i=1;i<=n;++i) printf("%.2lf\n",a[i][n+1]); } signed main() { n=g(); for(R i=1;i<=n;++i) for(R j=1;j<=n+1;++j) a[i][j]=g(); Gauss(); }
(例題)
Luogu P3389 【模板】高斯消元法
Luogu P4035 [JSOI2008]球形空間產生器
POJ1830
etc.
2019.05.10