前言
学了才发现这玩意贼简单虽然据说是复习。
直接来
高斯消元用于解一个线性方程组,就是:
{ a 1 , 1 ⋅ x 1 + a 1 , 2 ⋅ x 2 + ⋯ + a 1 , n ⋅ x n = a 1 , n + 1 a 2 , 1 ⋅ x 1 + a 2 , 2 ⋅ x 2 + ⋯ + a 2 , n ⋅ x n = a 2 , n + 1 ⋯ a n , 1 ⋅ x 1 + a n , 2 ⋅ x 2 + ⋯ + a n , n ⋅ x n = a n , 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}⎩⎪⎪⎪⎨⎪⎪⎪⎧a1,1⋅x1+a1,2⋅x2+⋯+a1,n⋅xn=a1,n+1a2,1⋅x1+a2,2⋅x2+⋯+a2,n⋅xn=a2,n+1⋯an,1⋅x1+an,2⋅x2+⋯+an,n⋅xn=an,n+1(其中a aa是已知量)
我们接下来都用系数表达式(注意所有的a i , n + 1 a_{i,n+1}ai,n+1与其他a aa的含义有不同,但我们将它们放在一起):
a 1 , 1 a 1 , 2 a 1 , 3 ⋯ a 1 , n − 1 a 1 , n a 1 , n + 1 a 2 , 1 a 2 , 2 a 2 , 3 ⋯ a 2 , n − 1 a 2 , n a 2 , n + 1 a 3 , 1 a 3 , 2 a 3 , 3 ⋯ a 3 , n − 1 a 3 , n a 3 , n + 1 ⋯ a n , 1 a n , 2 a n , 3 ⋯ a n , n − 1 a n , n a n , 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}a1,1a2,1a3,1an,1a1,2a2,2a3,2an,2a1,3a2,3a3,3an,3⋯⋯⋯⋯⋯a1,n−1a2,n−1a3,n−1an,n−1a1,na2,na3,nan,na1,n+1a2,n+1a3,n+1an,n+1
高斯消元的目的是把原方程变成这样:
1 a 1 , 2 ′ a 1 , 3 ′ ⋯ a 1 , n − 1 ′ a 1 , n ′ a 1 , n + 1 ′ 0 1 a 2 , 3 ′ ⋯ a 2 , n − 1 ′ a 2 , n ′ a 2 , n + 1 ′ 0 0 1 ⋯ a 3 , n − 1 ′ a 3 , n ′ a 3 , n + 1 ′ ⋯ 0 0 0 ⋯ 0 1 a n , 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}1000a1,2′100a1,3′a2,3′10⋯⋯⋯⋯⋯a1,n−1′a2,n−1′a3,n−1′0a1,n′a2,n′a3,n′1a1,n+1′a2,n+1′a3,n+1′an,n+1′
那么从下往上依次回代,就能得到所有的x 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];
}
所以我们枚举i ii:将a i , i a_{i,i}ai,i变为1 11(①),然后把a j , i ( j > i ) a_{j,i}\ (j>i)aj,i (j>i)全部变成0 00(②)。
- 对于①操作,非常简单,把第i ii行的系数全部除以a i , i a_{i,i}ai,i即可;
- 对于②操作,你要根据加减消元法,用第i ii行的系数去把a j , i ( i + 1 ≤ j ≤ n ) a_{j,i}\ (i+1\leq j\leq n)aj,i (i+1≤j≤n)消为0 00,其他不管。那就给把a i , i a_{i,i}ai,i变成a j , i a_{j,i}aj,i(i ii行其他系数按等式的性质依次变),再将第j jj行的每个系数a j , k a_{j,k}aj,k减掉a i , k a_{i,k}ai,k。即:第j jj行的每个系数a j , k a_{j,k}aj,k都减去a j , i a i , i ⋅ a i , k = a j , i ⋅ a i , k \dfrac{a_{j,i}}{a_{i,i}}\cdot a_{i,k}=a_{j,i}\cdot a_{i,k}ai,iaj,i⋅ai,k=aj,i⋅ai,k。
以上就是高斯消元的过程,自己照着代码打一遍,模拟一下过程就能够很好地理解了。
还有一个细节,我们每次在操作①和操作②之前把max j = i n a j , i \max\limits_{j=i}^{n}a_{j,i}j=imaxnaj,i所在的那行跟第i ii行换一个位置,可以减少精度误差。
还有一个细节,判断无解:若当前a i , i = 0 a_{i,i}=0ai,i=0,那么高斯消元就无法继续了(操作②无法进行),且x i x_ixi也不能确定,输出No Solution
(但并不一定无解)。
代码
#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;
}