消元法
先來看一下百度百科的定義:
消元法是指將許多關系式中的若干個元素通過有限次地變換,消去其中的某些元素,從而使問題獲得解決的一種解題方法。
可能不好懂。
回想一下小學數學中解二元一次方程的方法
比如下面這個二元一次方程:
通過兩式相加,可以得到
進而解得
把 \(x\) 代回就可以得到
發現,在解這個二元一次方程時,通過式子加減,我們把其中一個元 \(y\) 消掉,只剩下了 \(x\),變成了我們可以直接得出解的形式。
這個解題方法就是消元法
高斯消元其實就是在模擬這個消元過程。
只不過元可能更多,式子可能更復雜,但好在這是計算機考慮的問題,與我們沒有什么關系。
高斯消元
高斯-約旦消元法
與后面高斯消元法相比沒有回帶過程,實現起來也更加簡單,精度方面也更加優秀
高斯消元的一般步驟是:
- 列出要進行消元的矩陣
- 枚舉每一列
- 設枚舉到第 \(i\) 列,找到第 \(i\) 列中系數最大的方程並將其交換到第 \(i\) 行
- 同過確定系數,加減消元等方法,將其他方程中的這個元的系數化為 \(0\)
- 求出每個未知數的解
進行高斯消元后是一個三角矩陣,而高斯-約旦消元法的結果是一個對角線矩陣
舉個例子:
求解下面這個方程組
規定從上到下三個式子分別是 \(A\), \(B\), \(C\)。
把它轉化成矩陣形式
左邊是每個未知數的常數項,右邊是該式結果,中間用一條豎線隔開
我們從第一列開始消元
找到第一列最大的一個,與第一行交換
我們要將第一列的其他兩項化為 \(0\)
\(B = B - \frac{3}{5}A\)
\(C = C - \frac{2}{5}A\)
然后變成這樣:
然后在看第二列,把第二列系數最大的那個方程換到第二列。
繼續進行消元操作,不過這次我們要把方程 \(A\) 的第二列的元也消掉
\(A = A - \frac{5}{12}B\)
\(C = C - \frac{7}{12}B\)
矩陣變為:
繼續對第三列進行相同操作,矩陣變為:
到此消元結束,發現每個方程只剩下一個變量,直接解出來輸出結果即可。
如果不懂的話可以看一下代碼實現。
/*
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]);
}