高斯消元学习笔记

高斯-约旦消元学习笔记

高斯消元本来很早之前就学了的,但最近才用上,所以现在才来写学习笔记

入手——二元一次方程组

首先,相信各位都很熟悉这样一个东西,那就是二元一次方程组,它长这个样子:

\[\left\{\begin{matrix} a_1x + b_1y = c_1 \\ a_2x + b_2y = c_2 \end{matrix}\right. \]

这里的 \(a, b, c\) 都是系数哈,也就是题目告诉我们的。

相信各位都应该知道这个东西该怎么解吧。其实有两种解法,一个是代入法,一个是加减消元法,这里选择加减消元法求解。

举个例子:

\[\left\{\begin{matrix} 3x + 2y = 18 \\ 5x + 7y = 41 \end{matrix}\right.\\ \]

先将两个式子的 \(x\) 的系数转换为相同的:

\[\left\{\begin{matrix} 15x + 10y = 90 \\ 15x + 21y = 123 \end{matrix}\right.\\ \]

再根据等式的性质将上下两个式子相减把 \(x\) 消掉得到:

\[15x - 15x + 21y - 10y = 123 - 90\\ \therefore 11y = 33\\ \therefore y = 3 \]

那么接着再把 \(y = 3\) 带入之前方程组的其中一个方程就可以得到 \(x = 4\)

所以,原方程的解为:

\[\left\{\begin{matrix} x = 4 \\ y = 4 \end{matrix}\right.\\ \]

推广——高斯-约旦消元

根据上述的解二元一次方程组的过程中,可以发现对于多元一次方程组的一个通解。

及每次选择一个方程组用其消掉其他方程组中的某一个未知数,以此类推,最后在倒推其他位置的解。

但这会出现一个问题。

如果只是放在平常的数学考试中,这个方法当然是够用的了,但对于 \(oi\) 来说,这个方法的精度实在是过于低劣了。

在往回推的过程中,不可避免的会产生许多精度的误差,所以需要在此基础上进行一个细微的优化。

考虑能否有这样一种消元方法,使得最后的方程组中的每个方程都是一个一元一次方程呢?

答案是肯定的。

可以钦定第 \(i\) 个方程最后留下来的是第 \(i\) 个未知数,那么只需要在消元的时候把其他所有的方程中的该未知数全部消掉就可以啦。

但还有一个问题,无解和多解该怎么判?

这也很简单,只需要记住两点即可:

  1. 当出现如下形式时无解:

    \[0 = a(a \ne 0) \]

    这里的 \(a\) 是系数,也就是说,当你左边的未知数全部消完的时候,右边还有值。

  2. 当出现如下形式时无解:

    \[0 = 0 \]

    也就是说,当你左边的未知数全部消完的时候,右边的值也没有了。

代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int read(){
	int x = 0, w = 1; char ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
	while(isdigit(ch)) {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * w;
}
const int maxn = 110;
double eps = 1e-8;
int n;
double a[maxn][maxn];
signed 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[Max][i]) < fabs(a[j][i])) Max = j;
    	for(int j = 1 ; j <= n + 1 ; j++)
    	    swap(a[Max][j], a[i][j]);
    	if(fabs(a[i][i]) <= eps){//判无解和多解
    		printf("No Solution");
    		return 0;
		}
		for(int j = 1 ; j <= n ; j++){
			if(i == j) continue;
			double x = a[j][i] / a[i][i];
			for(int k = i + 1 ; k <= n + 1 ; k++)
			    a[j][k] -= a[i][k] * x;
		}

	}
	for(int i = 1 ; i <= n ; i++){
        printf("%.2lf\n", a[i][n + 1] / a[i][i]);
    }
	return 0;
}

其实背板即可,理解只是为了让背板更顺畅。

练习题

P3389 【模板】高斯消元法

P2455 [SDOI2006] 线性方程组

P3232 [HNOI2013] 游走

posted @ 2023-11-30 07:44  Populus_euphratica  阅读(31)  评论(0)    收藏  举报