浅谈高斯消元

线性代数相关定义

线型方程组

设有 n 个未知数 m 个方程的线性方程组

{a11x1+a12x2++a1nxn=b1a21x1+a22x2++a2nxn=b2am1x1+am2x2++amnxx=bm

其中 aij 为第 i 个方程第 j 个未知数的系数,bi 是第 i 个方程的常数项。
当常数项 b1,b2,b3,,bm 不全为零时,该线性方程组叫做 n 元非齐次线性方程组
b1,b2,b3,,bm 全为零时,该线性方程组

{a11x1+a12x2++a1nxn=0a21x1+a22x2++a2nxn=0am1x1+am2x2++amnxx=0

称为 n 元齐次线性方程组

n 元线性方程组往往简称为线性方程组或方程组。

对于 n 元齐次线性方程组,x1=x2=x3=xn=0 一定是它的解,这个解叫齐次线性方程组的零解。如果有一组不全为零的数是它的解,则叫做齐次线性方程组的非零解。

齐次线性方程组一定有零解,不一定有非零解。


矩阵

对于非齐次线性方程组

{a11x1+a12x2++a1nxn=b1a21x1+a22x2++a2nxn=b2am1x1+am2x2++amnxx=bm

有如下几个矩阵:

A=(aij),x=(x1x2xn),b=(b1b2bm),B=(a11a12a1nb1a21a22a2nb2am1am2amnbm),

其中 A 成为系数矩阵,x 称为未知数矩阵,b 称为常数项矩阵,B 称为增广矩阵。高斯消元就是在增广矩阵上进行的。


矩阵的初等变换

下面三种变换称为矩阵的初等行变换

  1. 对换两行(对换 i,j 两行,记作 rirj
  2. 以非零数 k 乘上某一行中所有元(第 i 行乘以 k,记作 ri×k
  3. 把某一行数所有元的 k 倍加到另一行的所有元上去(第 j 行的 k 倍加到第 i 行上,记作 ri+k×rj

把矩阵的初等行变换的“行”换成“列”,就得到了矩阵的初等列变换。
矩阵的初等行变换和矩阵的初等列变换,统称为初等变换

高斯消元

主要思想

反复运用初等变换将原矩阵变换为形如

(1a12a13a1nb11a23a2nb21bn)

的上三角矩阵,然后从下至上回带求解。
而且可以观察到每一行对应主元之下,该主元的系数都为 0

主要步骤

  • 找到当前主元系数不为 0 的一行(这里通常也找系数绝对值较大的一行)。
  • 将这一行与当前处理到的行交换。
  • 运用初等变换将主元的系数变为 1
  • 运用初等变换将其余行(不包含当前行上面的行)的主元的系数变为 0(目的是变成上三角矩阵)。

最后通过上三角矩阵回带求解,得到答案。

无解 & 无穷解

通过初中知识可知,n 个未知数要有 n 个方程才能求解,如果在找主元不为 0 的一行是找不到了(即这个主元的所有系数都为 0),这是肯定是会出现两种情况。

  1. 无解,出现 0=k0 时。
  2. 无穷解,出现 0=0 时。

故处理时如果出现找不到主元时先跳过这一行,最后在所有系数都为 0 的行中判断常数即可。

无解举例

{3x1+7x25x3=47x1+4x2+x3=582x1+3x26x3=5(37547141582365)(3754705812700016)

此时无解。

无穷解举例

{3x1+7x25x3=47x1+4x2+x3=582x1+3x26x3=11(375471415823611)(375470581270000)

此时有无数解。

解释

下面通过几组样例,在程序中插入 printf 语句,追踪一下高斯消元的执行过程。


唯一解

输入:

3
2 -1 1 1
4 1 -1 5
1 1 1 0

输出:

x1=1.00
x2=0.00
x3=-1.00

唯一解


无穷解
输入:

4
0 0 2 4 6
0 0 1 1 2
0 0 4 8 12
1 1 1 1 0

输出:

0


无解
输入:

4
0 0 2 1 2
0 0 1 1 1
0 0 0 1 1
0 0 0 0 0

输出:

-1

无解

代码实现

模板题:
P2455 [SDOI2006] 线性方程组

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <ctime>

using namespace std;

const int maxn = 105;
const double eps = 1e-7;//精度
double a[maxn][maxn];
double ans[maxn];
int n;

void print()
{
	puts("");
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n + 1; j++) cout << a[i][j] << " ";
		puts("");
	}
	puts("");
}//调试,输出矩阵

void gauss()
{
	//row 行 col 列
	int col = 1, row = 1;
	for (col = 1; col <= n; col++)//枚举主元所在列(1~n)
	{
		//step1 找到主元非0的一行
		int t = row;
		for (int i = row; i <= n; i++)
		{
			if (fabs(a[i][col]) > eps)
			{
				t = i;
				break;
			}
		}
		// step2
		if (fabs(a[t][col]) < eps) continue;//主元系数为0,即产生了自由元,先暂且不管
		swap(a[row], a[t]);//否则替换两行

		//step3 将主元系数置1,注意枚举顺序
		for (int i = n + 1; i >= row; i--)
		{
			a[row][i] /= a[row][col];
		}

		//step4 将下面所有的主元系数消为0
		for (int i = row + 1; i <= n; i++)
		{
			for (int j = n + 1; j >= col; j--)
			{
				a[i][j] -= a[i][col] * a[row][j];
			}
		}

		//step5 处理下一个主元
		row++;
	}

	//处理的主元个数小于n,产生了自由元,一定是无解/无穷解
	if (row <= n)
	{
		for (int i = row; i <= n; i++)
		{
			if (fabs(a[i][n + 1]) > eps)
			{
				cout << "-1" << endl;
				exit(0);
			}
		}
		puts("0");
		exit(0);
		return;
	}
	else
	{
		//step6 回代求解答案
		for (int i = n; i >= 1; i--)
		{
			ans[i] = a[i][n + 1];
			for (int j = i + 1; j <= n; j++)
			{
				ans[i] -= ans[j] * a[i][j];
			}
		}
		for (int i = 1; i <= n; i++) printf("x%d=%.2lf\n", i, ans[i]);
	}
}




int main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
	//freopen("in.txt","r",stdin);
#endif
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n + 1; j++)
		{
			scanf("%lf", &a[i][j]);
		}
	}
	gauss();
#ifdef LOCAL
	fprintf(stderr, "%f\n", 1.0 * clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}



高斯-约当消元法

主要思想

与高斯消元思想相近,但略有不同。
反复运用初等变换将系数矩阵变换为形如

(100001000001)

的单位矩阵。

主要步骤

  • 找到当前主元系数不为 0 的一行(这里通常也找系数绝对值较大的一行)。
  • 将这一行与当前处理到的行交换。
  • 运用初等变换将主元的系数变为 1
  • 运用初等变换将其余行的主元的系数变为 0

解释

追踪高斯-约当消元的执行过程。

输入:

3
1 3 4 5
1 4 7 3
9 3 2 2

输出

-0.97
5.18
-2.39

高斯-约当消元

代码实现

相比高斯消元只需改动一点,具体细节见代码。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <ctime>

using namespace std;

const int maxn = 105;
const double eps = 1e-7;//精度
double a[maxn][maxn];
double ans[maxn];
int n;

void print()
{
	puts("");
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n + 1; j++) cout << a[i][j] << " ";
		puts("");
	}
	puts("");
}//调试,输出矩阵

void gauss()
{
	//row 行 col 列
	int col = 1, row = 1;
	for (col = 1; col <= n; col++)//枚举主元所在列(1~n)
	{
		//step1 找到主元非0的一行
		int t = row;
		for (int i = row; i <= n; i++)
		{
			if (fabs(a[i][col]) > eps)
			{
				t = i;
				break;
			}
		}
		// step2
		if (fabs(a[t][col]) < eps) continue;//主元系数为0,即产生了自由元,先暂且不管
		swap(a[row], a[t]);//否则替换两行

		//step3 将主元系数置1,注意枚举顺序
		for (int i = n + 1; i >= row; i--)
		{
			a[row][i] /= a[row][col];
		}

		//step4 将所有的主元系数消为0
		for (int i = 1; i <= n; i++)
		{
			if (i == row) continue;
			for (int j = n + 1; j >= col; j--)
			{
				a[i][j] -= a[i][col] * a[row][j];
			}
		}
		//cout<<row<<" "<<col<<endl;
		//print();
		//step5 处理下一个主元
		row++;
	}

	//处理的主元个数小于n,产生了自由元,一定是无解/无穷解
	if (row <= n)
	{
		for (int i = row; i <= n; i++)
		{
			if (fabs(a[i][n + 1]) > eps)
			{
				cout << "No Solution" << endl;
				exit(0);
			}
		}
		puts("No Solution");
		exit(0);
		return;
	}
	else
	{
		//step6 输出答案
		for (int i = 1; i <= n; i++) printf("%.2lf\n", a[i][n + 1]);
	}
}




int main()
{
#ifndef ONLINE_JUDGE
#define LOCAL
	//freopen("in.txt","r",stdin);
#endif
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n + 1; j++)
		{
			scanf("%lf", &a[i][j]);
		}
	}
	gauss();
	puts("");
#ifdef LOCAL
	fprintf(stderr, "%f\n", 1.0 * clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}


例题

本文作者:vanueber

本文链接:https://www.cnblogs.com/vanueber/p/18669234

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   vanueber  阅读(16)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起