[题解] [笔记]高斯消元 & 洛谷P3389
[题解] [笔记]高斯消元 & 洛谷P3389
算法思路
消元
解决多元方程组的时候我们通常的方法有两个:加减消元和代入消元。高斯消元的原理就是加减消元。那么在解方程的时候如果要加减消元那么首先就要把某一个未知数的系数化成一样的。
放一段消元的代码
for(int i = 1;i <= n;i++){
if(fabs(a[i][i]) < 1e-8){//当系数很小接近于0时视为无解
printf("No Solution");
exit(0);
}
double div = a[i][i];//把方程组中第i个方程的系数化为1
for(int j = i;j <= n + 1;j++)a[i][j] /= div;//处理第i个方程其它项的系数
for(int j = i + 1;j <= n;j++){
double x = a[j][i];
for(int k = i;k <= n + 1;k++)//从第i个未知数开始,因为之前的未知数已经消过元了
a[j][k] -= a[i][k] * x;
}
}
结合这段代码,我们再来理解消元的过程:
\[假设现在有方程组:\begin{cases} 5x-2y=4\\2x+3y=7\end{cases}\\
根据代码,我们先处理第一个方程,把第一个方程的x项系数化为1\\
则第一个方程现在变成了:x-\frac{2}{5}y=\frac{4}{5}\\
以上的过程就是上面代码1~7行的过程。\\
再往下看,接着就是把其余的方程和第i个方程进行加减消元\\
看到上面代码的第9行,此时x=2,到第11行\\
第二个方程的第1个系数就变成了2-1*2=0\\
第二个方程的第2个系数变成3=3-\frac{2}{5}*2=\frac{11}{5}之后的运算同理
\]
回代
消完元就利用已经求解出的未知数带入原来的方程。
回代代码
ans[n] = a[n][n + 1];//所有的元都消完了,那么等号右边的数就是最后一个被消元的未知数的解,可以手推一下
for(int i = n - 1;i;i--){//思考一下可以发现,倒序的原因是消元之后这个未知数就没有了,所以最后消元的未知数,含有它的方程也在越靠后面
ans[i] = a[i][n + 1];
for(int j = i + 1;j <= n;j++)
ans[i] -= a[i][j] * ans[j];
}
放完整代码
#include <bits/stdc++.h>
using namespace std;
int n;
long double ans[110],a[110][110];
void gauss(){
for(int i = 1;i <= n;i++){
if(fabs(a[i][i]) < 1e-8){
printf("No Solution");
exit(0);
}
double div = a[i][i];
for(int j = i;j <= n + 1;j++)a[i][j] /= div;
for(int j = i + 1;j <= n;j++){
double x = a[j][i];
for(int k = i;k <= n + 1;k++)
a[j][k] -= a[i][k] * x;
}
}
ans[n] = a[n][n + 1];
for(int i = n - 1;i;i--){
ans[i] = a[i][n + 1];
for(int j = i + 1;j <= n;j++)
ans[i] -= a[i][j] * ans[j];
}
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n + 1;j++){
double x;
scanf("%lf",&x);
a[i][j] = x;
}
}
gauss();
for(int i = 1;i <= n;i++)
printf("%.2lf\n",(double)ans[i]);
return 0;
}