高斯消元1

高斯消元

高斯消元是线性代数规划中的一个算法,可用来为线性方程组求解。所谓线性方程组,是由 M M M N N N元一次方程共同构成的。线性方程组的所有系数可以写成一个 M M M N N N列的“系数矩阵”,再加上每个方程等号右侧的常数,可以写成一个 M M M N + 1 N+1 N+1列 的“增广矩阵”
对“增广矩阵”进行的操作有三种

  1. 用一个非零的数乘其一行
  2. 把其中一行的若干倍加到另一行上
  3. 交换两行的位置

称为“初等行变换”,得到“上三角矩阵”或“下三角矩阵”,进一步化简得到“简化阶梯形矩阵”。通过初等行变换把增广矩阵变为简化阶梯形矩阵的线性方程组求解算法就是高斯消元算法
方程的解写作形如 { x 1 = 4 − 2 x 2 x 3 = 1 \begin{cases}x_1=4-2x_2\\x_3=1\end{cases} {x1=42x2x3=1的情况下,我们把 x 1 , x 3 x_1,x_3 x1,x3这样的未知量称为“主元” ,把 x 2 x_2 x2这样的未知量称为“自由元”。
系数和常数构成 n ∗ ( n + 1 ) n ∗ ( n + 1 ) n(n+1) 的增广矩阵,有三种特殊情况:

  1. 最后有 n n n个主元,那么有唯一解。
  2. k k k个主元,自由元 n − k n-k nk个,有无穷多个解。
  3. 存在系数全为零、常数不为零的行,无解。

以下为两道板子题

P3389 【模板】高斯消元法

题目链接

P3389 【模板】高斯消元法

Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int n ;
double a[110][110] ;

inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
	return f*x;
}

void gauss()
{
	for ( int i = 1 ; i <= n; ++i )
	{
		for ( int j = i + 1 ; j <= n ; ++j )
			if ( fabs(a[j][i]) > fabs(a[i][i]) )
				for ( int k = 1 ; k <= n + 1 ; ++k )
					swap( a[i][k] , a[j][k] ) ;
		if ( fabs(a[i][i]) < 1e-8 ) continue ;
		for ( int j = 1 ; j <= n ; ++j )
		{
			if ( i == j ) continue ;
			if ( fabs(a[j][i]) < 1e-8 ) continue ;
			double r = a[i][i] / a[j][i] ;
			for ( int k = 1 ; k <= n + 1 ; ++k )
				a[j][k] = a[i][k] - a[j][k] * r ;
		}
	}
}

int main()
{
	n = read() ;
	for ( int i = 1 ; i <= n ; ++i )
		for ( int j = 1 ; j <= n + 1 ; ++j )
			scanf("%lf",&a[i][j]) ;
	gauss() ;
	int ok = 0 ;
	for ( int i = 1 ; i <= n ; ++i )
		if ( fabs(a[i][i]) < 1e-8 && fabs(a[i][n+1]) < 1e-8 )
		{
			ok = 1 ; break ;
		}
	if ( ok ) printf("No Solution\n") ;
	else for ( int i = 1 ; i <= n ; ++i ) printf("%.2lf\n",a[i][n+1] / a[i][i]) ; ;
}

P2455 [SDOI2006]线性方程组

题目链接

P2455 [SDOI2006]线性方程组

题目分析

本题不同于上一题,要判断是无解还是无穷多解,形如

2
0 2 3
0 0 0

一类数据理应为无穷多个解,程序会判为无解,应遵循以下规则进行变换

对于第 i i i列,设 j ∈ [ i + 1 , n ] j\in[i+1,n] j[i+1,n],如果 b [ j ] [ i ] > b [ i ] [ i ] b[j][i] > b[i][i] b[j][i]>b[i][i],直接交换。但是如果相等的话,则按照后面位的系数,将绝对值较小的进行交换。
判断无解的情况要放在判断无限解的情况之前。

Code
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int n ;
double a[105][105] ;

inline int read()
{
	int x=0,f=1; char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
	return f*x;
}

bool cmp(int i, int j)
{
	if(fabs(a[j][i]) > fabs(a[i][i])) return 1;
	else if (fabs(a[j][i]) < fabs(a[i][i])) return 0;
	// 如果b[j][i]和b[i][i]绝对值相等,取后面的位数系数绝对值小的
	for (int k = i + 1; k <= n; ++k)
		if(fabs(a[j][k]) < fabs(a[i][k])) return 1;
		else if (fabs(a[j][k]) > fabs(a[i][k])) return 0;
	return 0;
}

void gauss()
{
	for (int i = 1; i <= n; ++i)
	{
		for (int j = i + 1; j <= n; ++j)
			if (fabs(a[j][i]) > fabs(a[i][i]))
				for (int k = 1; k <= n + 1; ++k)
					swap(a[i][k], a[j][k]);
		if (fabs(a[i][i]) < 1e-8) continue;
		for (int j = 1; j <= n; ++j)
		{
			if (i == j) continue;
			if (fabs(a[j][i]) < 1e-8) continue;
			double r = a[i][i] / a[j][i];
			for (int k = 1; k <= n + 1; ++k)
				a[j][k] = a[i][k] - a[j][k] * r;
		}
	}
}

int main()
{
	n = read() ;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n + 1; ++j)
			scanf("%lf",&a[i][j]);
	gauss();
	int ok = 0;
	for (int i = 1; i <= n; ++i)
		if (fabs(a[i][i]) < 1e-8 && fabs(a[i][n+1]) > 1e-8)
		{
			ok = 1; break;
		}
	if ( ok != 1 )
	{
		for (int i = 1; i <= n; ++i)
		{
			if (fabs(a[i][i]) < 1e-8 && fabs(a[i][n+1]) < 1e-8)
			{
				ok = 2; break;
			}
		}
	}
	if (ok == 1) printf("-1\n");
	else if (ok == 2) printf("0\n");
	else for (int i = 1; i <= n; ++i)
	{
		double x = a[i][n+1] / a[i][i];
		if (fabs(x) < 1e-8) x = -x;
		printf("x%d=%.2lf\n",i,x);
	}
}
总结与反思
  1. 实型运算会出现-0.00的情况,需要特判
  2. 实型运算中极易出现精度丢失,比较关系不能直接使用 = = == ==等运算符,要用绝对值做差与一极小值比较判断大小关系,防止精度丢失
  3. 处理细节问题,判断是无解或无穷多个解
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值