高斯消元(学习笔记)

高斯消元

高斯消元是一种求解线性方程组的方法.所谓线性方程组,是由M个N元一次方程共同构成的.(其实正常来讲,应该是N个N元一次方程)

线性方程组的所有系数可以写成一个M行N列的"系数矩阵",再加上每个方程等号右侧的常数,可以写成一个M行N+1列的"增广矩阵".

\(x_1+2x_2-x_3=-6\)

\(2x_1+x_2-3x_3=-9\)

\(-x_1-x_2+2x_3=7\)

上面的三元一次方程组可写为如下增广矩阵:

\([ \begin{matrix} 1 & 2 & -1 & -6\\ 2 & 1 & -3 & -9\\ -1 & -1 & 2 & 7\\ \end{matrix} ] \)

求解这种方程组的步骤可以概括成对增广矩阵的三类操作:

1 用一个非零的数乘某一行;

2 把其中一行的若干倍加到另一行上;

3 交换两行的位置;

我们把这三类操作称为矩阵的初等行变换;

[1 2 -1 | -6]

[2 1 -3 | -9]

[-1 -1 2 | 7]

对上例方程组经过若干次“初等行变换”后最后可以得到如下阶梯矩形阵,它的系数矩阵部分被称为“上三角矩阵”,名字来源于其形状;

\([ \begin{matrix} &x_1 + 2x_2-x_3=-6\\ &x_2+x_3=1\\ &x_3=3\\ \end{matrix} ] \)

其实应该是右对齐的,但我不会打(这个很重要,右对齐!!!)(相同的未知量在同一列)

因此,知道了最后一个未知量的值后,从下往上依次代回方程组,即可得到每个未知量的解;

事实上,该矩阵还可以进一步化简为“简化阶梯型矩阵”,它的系数矩阵部分是一个对角矩阵,如下所示;

\([ \begin{matrix} 1 & 2 & -1 & -6\\ & 1 & 1 & 1\\ & & 1 & 3\\ \end{matrix} ] \)

通过初等行变换把增广矩阵变为简化阶梯型矩阵的线性方程组求解算法就是高斯消元算法;

直接结合高斯消元的模板题来讲解吧(如果真的想好好学高斯消元,建议自己在纸上手写模拟一遍整个步骤,本蒟蒻就是这样学的)

洛谷模板题

基本步骤(构造一个上三角矩阵):

1 选定未被选择过的,\(x_i\)系数的绝对值最大的一行,将该行式子除以\(x_i\)的系数(即把\(x_i\)的系数化为1),同时把这一行交换至第i行;

2 将未被选择过的行中的该项全部按照系数相应的减去当前选定的那行的系数(即把剩下的其他行\(x_i\)的系数化为0)

经过上述步骤后,已经构造出了一个上三角矩阵;

3 倒序求解,将已经求出的未知数的值一层一层往上代回,求出未求出的未知数的值(即将已知解代入式子求未知解);

#include<bits/stdc++.h>
using namespace std;
#define gt getchar()
inline int read(){
    int s=0,w=1;
    char ch=gt;
    while(ch<'0'||ch>'9'){
    	if(ch=='-') w=-1;
        ch=gt;
    }
    while(ch>='0'&&ch<='9'){
    	s=s*10+ch-'0';
        ch=gt;
    }
    return s*w;
}
int n;
double a[105][105];
int main(){
    n=read();
    for(int i=1;i<=n;i++)
	for(int j=1;j<=n+1;j++)
	    a[i][j]=read();
//读入只有系数和常数的增广矩阵
//n行n+1列,第n+1列是常数项
//表示求解n元一次方程
    for(int i=1;i<=n;i++){
		int now=i;
		for(int j=i+1;j<=n;j++)
	    if(fabs(a[now][i])<fabs(a[j][i]))
			now=j;
//选定未被选择过的,xi系数的绝对值最大的一行
		for(int j=i;j<=n+1;j++)
	    	swap(a[now][j],a[i][j]);
//把该行和第i行交换
		if(a[i][i]==0){
	    	printf("No Solution\n");
	    	return 0;
		}
//如果此时的最大系数是0,说明方程无解
		for(int j=i+1;j<=n+1;j++)
	    	a[i][j]/=a[i][i];
		a[i][i]=1;
//将该行(此时第i行)式子除以xi的系数(即把xi的系数化为1)
//我们要将该行其它项的系数除以第i项的系数
//所以我们不能一开始就让它化为1,下面同理
		for(int j=i+1;j<=n;j++){
	    	for(int k=i+1;k<=n+1;k++){
			 	a[j][k]-=a[j][i]*a[i][k];
         }
	    	a[j][i]=0;
		}
//将未被选择过的行中的该项全部按照系数
//相应的减去当前选定的那行的系数
//(这里不自己在纸上模拟,可能有点难理解)
//即把剩下的其他行xi的系数化为0
    }//经过这个循环操作后,已经构造了一个上三角矩阵
    for(int i=n;i>=1;i--){//最后倒序求解
		for(int j=i+1;j<=n;j++){
	    	a[i][n+1]-=a[i][j]*a[j][n+1];
	    	a[i][j]=0;
		}
//内循环是在把已知解代入来求未知解的过程
//(这里不自己在纸上模拟也很难理解)
		//a[i][n+1]/=a[i][i];
		//a[i][i]=1;
//这两步其实是没有必要的,因为经过上面循环操作后
//第i行第i项的系数(即a[i][i])已经为1
    }
    for(int i=1;i<=n;i++)
		printf("%.2lf\n",a[i][n+1]);
    return 0;
}

posted on 2018-10-29 14:41  PPXppx  阅读(181)  评论(0编辑  收藏  举报