高斯消元学习笔记

高斯消元是一个用来求解多元一次方程的算法,它求解 nn 元一次方程的时间复杂度是 O(n3)O(n^3)

做法

我们考虑这样的一个三元一次方程组:

{x1+2x2=3x2+x3=5x1+3x3=10\begin{cases}x_1+2x_2=3\\x_2+x_3=5\\ x_1+3x_3=10\end{cases}

把每一条方程的未知数补全:

{x1+2x2+0x3=30x1+x2+x3=5x1+0x2+3x3=10\begin{cases}x_1+2x_2+0x_3=3\\0x_1+x_2+x_3=5\\ x_1+0x_2+3x_3=10\end{cases}

然后我们就可以不关心具体的未知数了,只用关心系数。方程组也就可以写成一个矩阵:

[1203011510310]\begin{bmatrix}1&2&0&3\\0&1&1&5\\1&0&3&10\end{bmatrix}

其中前三列表示系数,最后一列是方程右边的常数。

考虑方程的求解。很明显可以先加减消元,让某个方程只剩下一个未知数。再代入消元,求出所有未知数

首先我们让矩阵第三行的方程减去第一行的方程,消掉 x1x_1

[120301150237]\begin{bmatrix}1&2&0&3\\0&1&1&5\\0&-2&3&7\end{bmatrix}

然后让矩阵第三行的方程减去 2-2 倍的第二行的方程,消掉 x2x_2

[1203011500517]\begin{bmatrix}1&2&0&3\\0&1&1&5\\0&0&5&17\end{bmatrix}

接下来我们就可以求出 x3x_3 的值了,然后再一步一步往回代,就可以解出所有未知数了

模板题代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

const long long MS=105;
const double zeo=0.0001;

int n;
double a[MS][MS],res[MS];

inline bool slove()
{
	// 加减消元过程 
	for(int i=1;i<=n;i++) // 枚举每一个方程,用第 i 个方程消去未知数 x[i] 
	{
		if(fabs(a[i][i])<=zeo) // 如果当前这个方程不含未知数 x[i](系数为 0),那么去找一个含有它的方程交换一下 
		{
			bool f=false; // 是否能找到 
			for(int j=i+1;j<=n;j++)
			{
				if(fabs(a[j][i])>zeo) // 找到了 
				{
					for(int k=1;k<=n+1;k++) // 交换 
					{
						swap(a[i][k],a[j][k]);
					}
					f=true;
					break;
				}
			}
			if(!f) // 条件不足,无法解出方程 
			{
				return false;
			}
		}
		for(int j=i+1;j<=n;j++) // 消去后面的方程中的未知数 x[i] 
		{
			double bs=a[j][i]/a[i][i]; // 第 j 个方程需要减去第 i 个方程的 bs 倍 
			for(int k=1;k<=n+1;k++) // 减去 
			{
				a[j][k]-=a[i][k]*bs;
			}
		}
	}
	// 代入消元过程 
	for(int i=n;i>=1;i--)
	{
		for(int j=i+1;j<=n;j++) // x[i+1...n] 已经计算完成,直接代入 
		{
			a[i][n+1]-=a[i][j]*res[j];
		}
		res[i]=a[i][n+1]/a[i][i]; // 消完元后只剩下 x[i],可以直接求出 
	}
	return true;
}

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]);
		}
	}
	if(!slove())
	{
		puts("No Solution");
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		printf("%.2lf\n",res[i]);
	}
	return 0;
}

练习题目