【高斯消元】学习笔记

洛谷端文章:https://www.luogu.com.cn/article/n5rjrsdw,如无法访问访问https://www.luogu.com.cn/article/n5rjrsdw。

理论

这个算法可以求 n n n 元一次方程组的解。

举个例子,现在已知有三元一次方程:

{ x + 2 y + 3 z = 36 − x + y + 2 z = 17 x + 2 z = 17 \begin{cases} x + 2y + 3z = 36 \\ -x + y + 2z = 17 \\ x + 2z = 17 \end{cases} x+2y+3z=36x+y+2z=17x+2z=17

解得:
{ x = 3 y = 6 z = 7 \begin{cases} x = 3 \\ y = 6 \\ z = 7 \end{cases} x=3y=6z=7

将问题扩展为 n n n 个未知数的方程组:
{ a 1 , 1 x 1 + a 1 , 2 x 2 + ⋯ + a 1 , n x n = b 1 a 2 , 1 x 1 + a 2 , 2 x 2 + ⋯ + a 2 , n x n = b 2 ⋮ a n , 1 x 1 + a n , 2 x 2 + ⋯ + a n , n x n = b n \begin{cases} a_{1,1}x_1 + a_{1,2}x_2 + \cdots + a_{1,n}x_n = b_1 \\ a_{2,1}x_1 + a_{2,2}x_2 + \cdots + a_{2,n}x_n = b_2 \\ \vdots \\ a_{n,1}x_1 + a_{n,2}x_2 + \cdots + a_{n,n}x_n = b_n \end{cases} a1,1x1+a1,2x2++a1,nxn=b1a2,1x1+a2,2x2++a2,nxn=b2an,1x1+an,2x2++an,nxn=bn

对应的矩阵表示为:
[ a 1 , 1 a 1 , 2 ⋯ a 1 , n b 1 a 2 , 1 a 2 , 2 ⋯ a 2 , n b 2 ⋮ ⋮ ⋱ ⋮ ⋮ a n , 1 a n , 2 ⋯ a n , n b n ] \begin{bmatrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n} & b_1 \\ a_{2,1} & a_{2,2} & \cdots & a_{2,n} & b_2 \\ \vdots & \vdots & \ddots & \vdots & \vdots \\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} & b_n \\ \end{bmatrix} a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,nb1b2bn


高斯消元步骤分解

我们以刚才的例子。

初始矩阵:

[ 1 2 3 36 − 1 1 2 17 1 0 2 17 ] \begin{bmatrix} 1 & 2 & 3 & 36 \\ -1 & 1 & 2 & 17 \\ 1 & 0 & 2 & 17 \\ \end{bmatrix} 111210322361717

  • 处理第一列

消去第二、三行的第一列:

[ 1 2 3 36 0 3 5 53 0 − 2 − 1 − 19 ] \begin{bmatrix} 1 & 2 & 3 & 36 \\ 0 & 3 & 5 & 53 \\ 0 & -2 & -1 & -19 \\ \end{bmatrix} 100232351365319

  • 处理第二列

第二行归一化并消去其他行:

[ 1 0 − 1 3 14 3 0 1 5 3 53 3 0 0 7 3 49 3 ] \begin{bmatrix} 1 & 0 & -\frac{1}{3} & \frac{14}{3} \\ 0 & 1 & \frac{5}{3} & \frac{53}{3} \\ 0 & 0 & \frac{7}{3} & \frac{49}{3} \\ \end{bmatrix} 100010313537314353349

  • 处理第三列:

第三行归一化并回代:

[ 1 0 0 3 0 1 0 6 0 0 1 7 ] \begin{bmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & 6 \\ 0 & 0 & 1 & 7 \\ \end{bmatrix} 100010001367

具体操作步骤

  • 对每个 x i x_i xi,选取系数绝对值最大的一行,交换至第 i i i 行并归一化。
  • 用主行消去其他行中 i i i 的系数。
  • 回代:从最后一行倒序求解变量值,公式为:
    在这里插入图片描述

时间复杂度 O ( n 3 ) \text{O}(n^3) O(n3)

特殊情况分析

无解情况

形如这样的,方程一样但是答案不一样,比如:

[ 3 7 5 10 3 7 5 20 ] \begin{bmatrix} 3 & 7 & 5 & 10 \\ 3 & 7 & 5 & 20 \\ \end{bmatrix} [3377551020]

是不行的。


还有形如这样的:
[ 0 0 0 1 − 4 4 1 13 ] \begin{bmatrix} 0 & 0 & 0 & 1 \\ -4 & 4 & 1 & 13 \\ \end{bmatrix} [040401113]
存在矛盾方程 0 x + 0 y + 0 z ≠ 0 0x+0y+0z \ne 0 0x+0y+0z=0,方程组无解。

一般情况:一整行的系数都为 0 0 0,答案不为 0 0 0


无穷解情况

[ 0 1 2 18 0 6 − 3 4 ] \begin{bmatrix} 0 & 1 & 2 & 18 \\ 0 & 6 & -3 & 4 \\ \end{bmatrix} [001623184]
第一列全零,自由变量存在,方程组有无穷多解。

形如这样的,一整列的系数都为 0 0 0,说明这个数没有参与任何方程。是多少都无所谓,与答案没有关联。

模板

P2455 [SDOI2006] 线性方程组

#include
using namespace std;
const int N=160;
const double eps=1e-6;
double a[N][N];
int n;
int gauss()
{
	int c,r;//C:当前处理X_c,R:处理了 r 个未知数 
	for(c=0,r=0;c<n;c++)
	{
		int t=r;
		for(int i=r;i<n;i++)
		{
			if(fabs(a[i][c])>fabs(a[t][c]))//如果这个系数更大 
			{
				t=i;//更新 
			}
		}
		if(fabs(a[t][c])<eps)
		{//如果最大的数还是 0,则代表所有方程这个项的系数都是 0。 
			continue;//就不管 
		}
		for(int i=c;i<n+1;i++)
		{
			swap(a[r][i],a[t][i]);//将其交换至第 c 行
		}
		for(int i=n;i>=c;i--)
		{
			a[r][i]/=a[r][c];//除以系数 
		}
		for(int i=0;i<n;i++)//消消乐 
		{
			if(fabs(a[i][c])>eps && i!=r)//如果这一项系数不为 0 且不是系数最大的一行 
			{
				for(int j=n;j>=c;j--)
				{
					a[i][j]-=a[i][c]*a[r][j];//消元 
				}
			}
		}
		r++;//新处理了一个未知数 
	}
	if(r<n)//如果没有处理完所有的未知数 
	{
		for(int i=r;i<n;i++)//检查剩下的方程 
		{
			if(fabs(a[i][n])>eps)
			{//如果有最终答案>=0说明无解 
				return -1;
			}
		}
		return 0;//反之无解 
	}
	return 1;//有唯一解 
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<=n;j++)
		{
			cin>>a[i][j];
		}
	}
	int ans=gauss();
	if(ans==1)
	{
		for(int i=0;i<n;i++)
		{
			printf("x%d=%.2f\n",i+1,a[i][n]);
		}
	}
	else
	{
		cout<<ans;
	}
	return 0;
}

P3389 【模板】高斯消元法

这个 gauss ⁡ \operatorname{gauss} gauss 是经典void类型的。

#include
using namespace std;
const int MAXN=100;
int n;
double a[MAXN+5][MAXN+5];
void gauss(int x)
{
	for(int i=0;i<x;i++)
	{
		int row=i;
		for(int j=i;j<x;j++)
		{
			if(fabs(a[row][i])<fabs(a[j][i]))
			{
				row=j;
			}
		}
		if(row!=i)
		{
			swap(a[row],a[i]);
		}
		double div1=a[i][i];
		for(int j=0;j<=x;j++)
		{
			a[i][j]/=div1;
		}
		for(int j=i+1;j<x;j++)
		{
			double div2=a[j][i];
			for(int k=0;k<=x;k++)
			{
				a[j][k]-=a[i][k]*div2;
			}
		}
	}
	//-----------------------------复原 
	for(int i=x-1;i>=0;i--)
	{
		for(int j=i-1;j>=0;j--)
		{
			a[j][x]-=a[j][i]*a[i][x];
			a[j][i]=0;
		}
	}
	//------------------------------
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<=n;j++)
		{
			cin>>a[i][j];
		}
	}
	gauss(n);
	for(int i=0;i<n;i++)
	{
		bool flag=1;
		for(int j=0;j<n;j++)
		{
			if(fabs(a[i][j])>1e-8)
			{
				flag=0;
			}
		}
		if(flag)
		{
			cout<<"No Solution";
			return 0;
		}
	}
	for(int i=0;i<n;i++)
	{
		cout<<fixed<<setprecision(2)<<a[i][n]<<'\n';
	}
	return 0;
}

你可能感兴趣的:(算法,学习,笔记,线性代数)