[线性代数]高斯消元法

前言

仍然是自闭的一天。
学一学搞死高斯消元弄昏一下脑子。

概述

高斯消元主要用于求解线性方程组,也可以求矩阵的秩,矩阵的逆等。该算法的时间复杂度与方程组个数,未知数个数等有关。一般的时间复杂度为 O ( n 3 ) O(n^3) O(n3)

线性方程组

含有多个一次未知数的方程组,形如
{ a 1 , 1 x 1 + a 1 , 2 x 2 + a 1 , 3 x 3 + . . . + a 1 , m x m = b 1 a 2 , 1 x 1 + a 2 , 2 x 2 + a 2 , 3 x 3 + . . . + a 2 , m x m = b 2 a 3 , 1 x 1 + a 3 , 2 x 2 + a 3 , 3 x 3 + . . . + a 3 , m x m = b 3 ⋯ a n , 1 x 1 + a n , 2 x 2 + a n , 3 x 3 + . . . + a n , m x m = b n \left\{ \begin{aligned} &a_{1,1}x_1+a_{1,2}x_2+a_{1,3}x_3+...+a_{1,m}x_m =b_1\\ &a_{2,1}x_1+a_{2,2}x_2+a_{2,3}x_3+...+a_{2,m}x_m =b_2\\ &a_{3,1}x_1+a_{3,2}x_2+a_{3,3}x_3+...+a_{3,m}x_m =b_3\\ & \quad \quad\quad\quad\quad\quad\quad\quad\quad \cdots \\ &a_{n,1}x_1+a_{n,2}x_2+a_{n,3}x_3+...+a_{n,m}x_m =b_n\\ \end{aligned} \right. a1,1x1+a1,2x2+a1,3x3+...+a1,mxm=b1a2,1x1+a2,2x2+a2,3x3+...+a2,mxm=b2a3,1x1+a3,2x2+a3,3x3+...+a3,mxm=b3an,1x1+an,2x2+an,3x3+...+an,mxm=bn
记作矩阵形式:

A = [ a 1 , 1 a 1 , 2 a 1 , 3 ⋯ a 1 , m a 2 , 1 a 2 , 2 a 2 , 3 ⋯ a 2 , m a 3 , 1 a 3 , 2 a 3 , 3 ⋯ a 3 , m ⋯ ⋯ ⋯ ⋯ ⋯ a n , 1 a n , 2 a n , 3 ⋯ a n , m ] ,      B = [ b 1 b 2 b 3 ⋯ b n ] ,     A x = B A = \begin{bmatrix} a_{1,1} &a_{1,2} &a_{1,3} &\cdots &a_{1,m} \\ a_{2,1} &a_{2,2} &a_{2,3} &\cdots &a_{2,m} \\ a_{3,1} &a_{3,2} &a_{3,3} &\cdots &a_{3,m} \\ \cdots &\cdots &\cdots &\cdots &\cdots\\ a_{n,1} &a_{n,2} &a_{n,3} &\cdots &a_{n,m} \\\end{bmatrix},\ \ \ \ B = \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ \cdots \\ b_n\end{bmatrix},\ \ \ Ax=B A=a1,1a2,1a3,1an,1a1,2a2,2a3,2an,2a1,3a2,3a3,3an,3a1,ma2,ma3,man,m,    B=b1b2b3bn,   Ax=B

单位矩阵

在矩阵的乘法中,有一种矩阵起着特殊的作用,如同数的乘法中的1,这种矩阵被称为单位矩阵。它是个方阵,从左上角到右下角的对角线(称为主对角线)上的元素均为1,除此以外全都为0。

根据单位矩阵的特点,任何矩阵与单位矩阵相乘都等于本身。
单位矩阵

增广矩阵

又称扩增矩阵,就是在系数矩阵的右边添上一列,这一列是线性方程组的等号右边的值。

矩阵的初等变换

矩阵的初等变换分为矩阵的初等行变换和矩阵的初等列变换。矩阵的初等行变换和矩阵的初等列变换统称为初等变换。

等价

如果矩阵 B B B可以由 A A A通过一系列初等变换得到,则称矩阵 A A A B B B等价。

初等行变换

数域 P P P上的初等行变换的定义:下面三种变换:

  1. P P P中某个非 0 0 0数乘矩阵的某一行
  2. 把矩阵某一行的 C C C倍加到另一行, C C C为P中任意一个数
  3. 互换矩阵某两行的位置

若矩阵 A A A通过初等行变换变成了矩阵 B B B,写作 A → B A \rightarrow B AB

任意矩阵通过一系列初等行变换都能转化为阶梯矩阵;若该矩阵为方阵,则为上三角矩阵。

初等列变换

与初等行变换进行类比,得到定义:

  1. P P P中某个非 0 0 0数乘矩阵的某一列
  2. 把矩阵某一行的 C C C倍加到另一列, C C C为P中任意一个数
  3. 互换矩阵某两列的位置

初等矩阵

定义:由单位矩阵 E E E经过一系列初等变换后得到的矩阵 E ′ E' E,称作初等矩阵。
引理:
对一个 n ∗ m n*m nm的矩阵 A A A作一次初等行变换,等价于在 A A A左边乘上一个 n ∗ n n*n nn的相应初等矩阵
对一个 n ∗ m n*m nm的矩阵 A A A作一次初等列变换,等价于在 A A A右边乘上一个 m ∗ m m*m mm的相应初等矩阵

高斯消元法

具体步骤

得到一个方程组。例如:
{ 2 x 1 + 1 x 2 + 1 x 3 = 1 4 x 1 + 7 x 2 + 8 x 3 = 31 5 x 1 + 2 x 2 + 1 x 3 = 4 \left\{ \begin{aligned} &2x_1+1x_2+1x_3=1\\ &4x_1+7x_2+8x_3=31\\ &5x_1+2x_2+1x_3=4\\ \end{aligned} \right. 2x1+1x2+1x3=14x1+7x2+8x3=315x1+2x2+1x3=4
将它转换为增广矩阵:
[ 2 1 1 1 4 7 8 31 5 2 1 4 ] \left[ \begin{array}{ccc|c} 2 &1 &1 & 1 \\ 4 & 7 & 8 & 31 \\ 5 & 2& 1 & 4 \end{array} \right] 2451721811314

  1. 首先,找到第一个第一列不为 0 0 0的行(一般找绝对值最大的,如果都为 0 0 0就跳过当前步)。
  2. 然后把这个数带入下面的方程组,把下面的第一列换掉代入,那么下面方程组的第一列都化为 0 0 0了。

例如这个方程,先找最大的第一列
[ 5 2 1 4 4 7 8 31 2 1 1 1 ] \left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 4 & 7 & 8 & 31 \\ 2 &1 &1 & 1 \end{array} \right] 5422711814311
(具体的步骤就不说了)代入消元:
[ 5 2 1 4 0 27 5 36 5 139 5 0 1 5 3 5 − 3 5 ] \left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 0 & \frac{27}{5} & \frac{36}{5} & \frac{139}{5} \\ 0 &\frac{1}{5} &\frac{3}{5} & -\frac{3}{5} \end{array} \right] 5002527511536534513953
3. 除了最后一列,对于每列都这样找一行消掉。因为有解的方程组是个方阵,最终会转换为上三角矩阵。
消成上三角矩阵:
[ 5 2 1 4 0 27 5 36 5 139 5 0 0 − 9 27 − 776 135 ] \left[ \begin{array}{ccc|c} 5 & 2& 1 & 4 \\ 0 & \frac{27}{5} & \frac{36}{5} & \frac{139}{5} \\ 0 & 0 & -\frac{9}{27} & -\frac{776}{135} \end{array} \right] 50025270153627945139135776
很显然,就可以从下往上,依次得到 x 3 , x 2 , x 1 x_3,x_2,x_1 x3,x2,x1了。

Code:

普通的(湘妹儿的):

bool gauss(int n,db a[][M])
{
	bool flag=1;
	for (int i = 1;i <= n; ++ i)
	{
		int Max = i;
		for (int r = i + 1; r <= n; ++ r)
			if (cmp(a[Max][i], a[r][i]) == -1)
				Max = r;
		swap(a[i], a[Max]);
		if (cmp(a[i][i], 0) == 0)
		{
			flag = 0;
			continue;
		}
		for (int j = 1; j <= n; ++ j)
		{
			if (i == j || cmp(a[j][i], 0) == 0)
				continue;
			for (int k = i + 1; k <= n + 1; ++ k)
				a[j][k] -= a[i][k] * a[j][i] / a[i][i];
			a[j][i] = 0;
		}
	}
	return flag;
}

异或的:
```cpp
LL Gauss(LL N,LL M)
{
	LL tra, col;
    for (tra = 1, col = 1; tra <= N && col <= M; ++ tra, ++ col)
    {
        int Maxx = tra;
        for (Int i = tra; i <= N; ++ i)
            if (B[i][col])
            {
            	Maxx = i;
            	break;
			}
        if (! B[Maxx][col])
		{
            tra --;
            continue;
        }
        if (Maxx != tra)
            for (Int i = col; i <= M + 1; ++ i)
				Swap(B[Maxx][i], B[tra][i]);
        for (Int i = tra + 1; i <= N; ++ i)
		{
            if (! B[i][col])
				continue;
            for (Int j = col; j <= M + 1; ++ j)
                B[i][j] ^= B[tra][j]; 
        }
    }
    for (Int i = tra; i <= N; ++ i)
        if (B[i][col])
			return -1;
    return N - tra;
}

高斯-约旦消元法

特点

精度更高,代码更短,不用回带。

基本思想

模拟加减消元,将每个方程转化为 k i x i = b i k_ix_i=b_i kixi=bi的形式,然后带回求得每个 x i x_i xi

具体思路

  1. 选择一个没被解决过的未知数作为主元。再选择包含这个未知数的一个方程
  2. 将这个方程主元的系数化为 1 1 1
  3. 将消元的式子带入其他方程加减消元,消去这个主元。(包括当前方程,这也是高斯-约旦消元法和普通高斯消元法的区别所在)
  4. 重复步骤,将方程组化为对角矩阵(即系数在对角线上,而普通高斯消元则是转化为上三角矩阵)。然后求解即可。
    其余操作与普通高斯消元相似。
Code

题目

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define MAXN 105
#define LL long long
#define Int register int
using namespace std;
DB A[MAXN][MAXN];
DB FABS(DB x)
{
	if (x > 0)
		return x;
	return -x;
}
void Swap(DB &x,DB &y)
{
	DB temp = x;
	x = y;
	y = temp;
}
int main()
{
	int n;
	scanf("%d", &n);
	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 Maxx = i;
		for (Int j = i + 1; j <= n; ++ j) // 找绝对值最大的
			if (FABS(A[j][i]) > FABS(A[Maxx][i]))
				Maxx = j;
		for (Int j = 1; j <= n + 1; ++ j)
			Swap(A[Maxx][j], A[i][j]);
		if (! A[i][i]) // 没有这个未知数,无解(或无数解) 
			return !printf("No Solution\n");
		for (Int j = 1; j <= n; ++ j)
		{
			if (j != i)
			{
				DB temp = A[j][i] / A[i][i];
				for (Int k = i + 1; k <= n + 1; ++ k)
					A[j][k] -= A[i][k] * temp;
			}
		}
	}
	for (Int i = 1; i <= n; ++ i)
		printf("%.2lf\n", A[i][n + 1] / A[i][i]);
	return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值