高斯消元详解

题目传送门:【模板】高斯消元法[SDOI2006]线性方程组

引入

给定一个 \(n\) 元一次方程组:

\[\large \begin{cases} a_{1,1}x_1+a_{1,2}x_2+a_{1,3}x_3+\cdots+a_{1,n}x_n=b_1\\ a_{2,1}x_1+a_{2,2}x_2+a_{2,3}x_3+\cdots+a_{2,n}x_n=b_2\\ a_{3,1}x_1+a_{3,2}x_2+a_{3,3}x_3+\cdots+a_{3,n}x_n=b_3\\ \cdots\\ a_{n,1}x_1+a_{n,2}x_2+a_{n,3}x_3+\cdots+a_{n,n}x_n=b_n \end{cases} \]

其中,\(a_{i,j},b_i\) 均为常数,求解 \(x_1,x_2,x_3,\cdots,x_n\)

那么,我们便可以利用高斯消元法 \(\mathcal O\left(n^3\right)\) 求解。

高斯消元法

基本思想

首先将方程的增广矩阵利用行初等变换化为行最简形,然后以线性无关为准则对自由未知量赋值,最后列出表达方程组通解。

上面这句话看不懂忽略,下文有解释。

过程

其实就可以理解为加减消元法求解后回代

例如对于方程组:

\[\begin{cases} 2x+4y+8z=34\\ 3x+9y+27z=102\\ 5x+25y+125z=430\\ \end{cases} \]

\(x\) 项系数化为 \(1\)

\[\begin{cases} x+2y+4z=17\\ x+3y+9z=34\\ x+5y+25z=86\\ \end{cases} \]

上下相减得:

\[\begin{cases} y+5z=17\\ 2y+16z=52\\ \end{cases} \]

\(y\) 项系数化为 \(1\)

\[\begin{cases} y+5z=17\\ y+8z=26\\ \end{cases} \]

上下相减:

\[3z=9 \]

\(z\) 项系数化为 \(1\)

\[z=3 \]


以上为加减消元,以下为回代。


\(z=3\) 带入 \(y+8z=26\) 得:

\[y+8\times3=26 \]

解得:

\[y=2 \]

\(\begin{cases}y=2\\z=3\end{cases}\) 带入 \(x+2y+4z\) 得:

\[x+2\times2+4\times3=17 \]

解得:

\[x=1 \]

思想解释

首先将方程的增广矩阵利用行初等变换化为行最简形,然后以线性无关为准则对自由未知量赋值,最后列出表达方程组通解。

  • “方程的增广矩阵”即系数矩阵后加了一列作为结果。
  • “利用行初等变换化为行最简形”即如过程中的依次化当前项系数为 \(1\)
  • “以线性无关为准则对自由未知量赋值”即加减消元法。
  • “列出表达方程组通解”即回代求解。

实现

首先,为了便于程序存储和分析,我们使用矩阵的形式来代替方程组。

同样是上面那个例子,得出矩阵:

\[\begin{pmatrix} 2&4&8&\vert&34\\ 3&9&27&\vert&102\\ 5&25&125&\vert&430 \end{pmatrix} \]

高斯消元的加减消元的前提是主项系数都是 \(1\)

因此我们得到:

\[\begin{pmatrix} 1&2&4&\vert&17\\ 1&3&9&\vert&34\\ 1&5&25&\vert&86 \end{pmatrix} \]

相减:

\[\begin{pmatrix} 1&2&4&\vert&17\\ 0&1&5&\vert&17\\ 0&2&16&\vert&52 \end{pmatrix} \]

化系数为 \(1\)

\[\begin{pmatrix} 1&2&4&\vert&17\\ 0&1&5&\vert&17\\ 0&1&8&\vert&26 \end{pmatrix} \]

再次相减:

\[\begin{pmatrix} 1&2&4&\vert&17\\ 0&1&5&\vert&17\\ 0&0&3&\vert&9 \end{pmatrix} \]

系数化为 \(1\)

\[\begin{pmatrix} 1&2&4&\vert&17\\ 0&1&5&\vert&17\\ 0&0&1&\vert&3 \end{pmatrix} \]

至此,你可以发现此时的矩阵是一个“上三角“,即主对角线上系数均为 \(1\),主对角线下均为 \(0\)

这种时候,考虑如何求解 \(x_i\)

很明显,当我们求得了 \(x_{i+1}\sim x_n\) 后,我们仅仅需要使 \(\large b_i\leftarrow b_i-\sum\limits_{j=i+1}^na_{i,j}\times x_j\),然后 \(b_i\) 即答案。

因为 \(a_{i,1}\sim a_{i,i-1}\) 均为 \(0\),自然不需要考虑。

就此,高斯消元实现。

无解、定解与无数解

定解

这应该是最好判断的,不是其余两种就行。

这不废话吗

无解

明显地,\(0=b_i\land b_i\ne0\) 是不成立的,即无解。

放到高斯消元中,就是若某行前 \(n\) 个数(所有系数)均为 \(0\),但最终结果 \(b_i\) 却不为 \(0\),则无解。

无数解

一般来讲,”无数解“都是至最终解出来了一个恒等式。那么在一个方程中,若等式左边的未知项等于一个常数,则未知项的值是为 \(0\) 的。那么未知项的系数也就是 \(0\)。即:若某行前 \(n\) 个数和最终结果 \(b_i\) 均为 \(0\),则有无数解。

存储

一般来讲,为了方便运算,我们都不会单独放一个 \(b\) 数组用于存储答案,而是直接存储增广矩阵。

高斯消元明显是一个 \(n\times n\) 的系数矩阵与 \(b\) 数组形成的一个 \(n\times(n+1)\) 的增广矩阵。

开一个数组 \(a[N+1][N+1+1]\) 即可。

消元

一般来讲,”化系数为 \(1\)“时,我们都会找当前项系数绝对值最大的将其化为 \(1\)

为什么?看到无解、无数解的判定。为了更快得出是否有解。

[SDOI2006]线性方程组 AC 代码

具体参见注释。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const double dx=1e-6;//dx:因为double精度问题,可近似视为0
const int N=100;
int n;
double a[N+1][N+1],x[N+1];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n+1;j++)scanf("%lf",&a[i][j]);
	}
	int r,c;
    //r:当前方程
    //c:当前项
	for(r=c=1;r<=n&&c<=n;r++,c++){
        //找系数绝对值最大的方程
		int u=r;
		for(int i=r+1;i<=n;i++){
			if(abs(a[i][c])>abs(a[u][c]))u=i;
		}if(u!=r)swap(a[r],a[u]);//交换(便于处理)
        //这一步可以使循环结束后运行r++,c++时,r并没有增加,即直接跳过这一项,先计算后面的项
		if(abs(a[r][c])<dx){
			r--;
			continue;
		}//这一步其实合并了化系数为1和加减消元,实在不能理解手推一下看看
		for(int i=r+1;i<=n;i++){
			if(abs(a[i][c])>dx){
				double pl=a[i][c]/a[r][c];
				for(int j=c;j<=n+1;j++)a[i][j]-=a[r][j]*pl;
				a[i][c]=0;
			}
		}
	}//若剩余方程存在且剩余方程内有非0系数,则无解
    for(int i=r;i<=n;i++){
		if(abs(a[i][c])>dx){
			printf("-1\n");
			return 0;
		}
	}//无数解
	if(r<=n){
		printf("0\n");
		return 0;
	}//回代还原
	for(int i=n;i>=1;i--){
		for(int j=i+1;j<=n;j++)a[i][n+1]-=a[i][j]*x[j];
		x[i]=a[i][n+1]/a[i][i];
	}//输出
	for(int i=1;i<=n;i++)printf("x%d=%.2lf\n",i,x[i]);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}

【模板】高斯消元法 AC 代码

和上面几乎一摸一样......

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
const double dx=1e-6;
const int N=100;
int n;
double a[N+1][N+1],x[N+1];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n+1;j++)scanf("%lf",&a[i][j]);
	}
	int r,c;
	for(r=c=1;r<=n&&c<=n;r++,c++){
		int u=r;
		for(int i=r+1;i<=n;i++){
			if(abs(a[i][c])>abs(a[u][c]))u=i;
		}
		if(u!=r)swap(a[r],a[u]);
		if(abs(a[r][c])<dx){
			r--;
			continue;
		}
		for(int i=r+1;i<=n;i++){
			if(abs(a[i][c])>dx){
				double pl=a[i][c]/a[r][c];
				for(int j=c;j<=n+1;j++)a[i][j]-=a[r][j]*pl;
				a[i][c]=0;
			}
		}
	} 
    for(int i=r;i<=n;i++){
		if(abs(a[i][c])>dx){
			printf("No Solution\n");//改动1
			return 0;
		}
	}
	if(r<=n){
		printf("No Solution\n");//改动2
		return 0;
	}
	for(int i=n;i>=1;i--){
		for(int j=i+1;j<=n;j++)a[i][n+1]-=a[i][j]*x[j];
		x[i]=a[i][n+1]/a[i][i];
	}
	for(int i=1;i<=n;i++)printf("%.2lf\n",x[i]);//改动3
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-07-20 13:59  TH911  阅读(15)  评论(0)    收藏  举报