高斯消元是线代中的一个算法。今天的文章将介绍高斯消元以及如何在编程中实现。
前置知识->初等行列变换:
(1)将某行整体乘一个常数;
(2)交换某两行;
(3)某行加上或减去某行的常数倍。
高斯消元的应用:解n元一次方程组。
方程组:
线代中对应增广矩阵:
在增广矩阵中进行初等行列式变换不会影响解。
利用行列式变换将矩阵转化为阶梯型矩阵。
再将阶梯型矩阵转化为最简型矩阵。
因为这里重点是讲算法而不是线代所以就不讲如何数学中转化矩阵,而是说我们在算法中是如何做的。
解方程方法:
首先,我们要将这个增广矩阵转化为阶梯型矩阵。那么我们应该如何做呢。
我们应当一列一列的进行消除,所以第一步是枚举列(col)。
然后再在一列当中找到绝对值最大的数,将其与我们当前正在处理的一行进行交换(行列式变换1)。
然后将这一行的第一个数也就是我们交换后的当前行的第一个数变成1,也就是除以数本身,注意这一行所有的数都应当进行一致的操作(行列式变换2)。
然后利用行列式变换3将下面的这一列下面的其他行的数消成0。枚举到最后一列时结束。
算法步骤:
先枚举列,然后
1.找到当前列绝对值最大的一行
2.用初等行变换(2) 把这一行换到最上面(未确定阶梯型的行,并不是第一行)
3.用初等行变换(1) 将该行的第一个数变成 1(其余所有的数字依次跟着变化)
4.用初等行变换(3) 将下面所有行的当且列的值变成 0
例题
输入一个包含 n 个方程 n 个未知数的线性方程组。
方程组中的系数为实数。
求解这个方程组。
下图为一个包含 m 个方程 n 个未知数的线性方程组示例:
输入格式
第一行包含整数 n。
接下来 n 行,每行包含 n+1个实数,表示一个方程的 n 个系数以及等号右侧的常数。
输出格式
如果给定线性方程组存在唯一解,则输出共 n 行,其中第 i 行输出第 i 个未知数的解,结果保留两位小数。
如果给定线性方程组存在无数解,则输出 Infinite group solutions
。
如果给定线性方程组无解,则输出 No solution
。
数据范围
1≤n≤100,
所有输入系数以及常数均保留两位小数,绝对值均不超过 100。
本题是高斯消元的一个模板,可以用上述方法解决,同时需要判断无解,无穷解,唯一解。
图片中矩阵状态对应解的数量。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e2+10;
const double eps = 1e-8;
int n;
double a[N][N]; //存储线性方程组
int gauss(){
int r=1,c=1;//定义变量行与列
for(r,c;c<=n;c++){ //枚举列。
int t=r;
//找这一列绝对值最大的一行
for(int i=r;i<=n;i++){
if(fabs(a[i][c])>fabs(a[t][c]))t=i;
}
//如果这一列都为0则考虑下一列
if(fabs(a[t][c])<eps)continue;
//交换两行
for(int i=c;i<=n+1;i++)swap(a[t][i],a[r][i]);
// 将当前行的首位变成1
for (int i = n+1; i >= c; i -- ) a[r][i] /= a[r][c];
// 用当前行将下面所有的列消成0,注意当前行第一个数为1
for (int i = r + 1; i <= n; i ++ )
if (fabs(a[i][c]) > eps) //不为0则消成0
for (int j = n+1; j >= c; j -- ) //一整行都需要消
a[i][j] -= a[r][j] * a[i][c];
//考虑下一行(消成阶梯型)
r++;
}
//转化为最简型
for (int i = n-1 ; i >= 1; i -- )
for (int j = i + 1; j <= n; j ++ )
a[i][n+1] -= a[i][j] * a[j][n+1];
if (r <= n)
{
//当前的r为全0的一行如果方程对应右边不为0则无解
for (int i = r; i <= n; i ++ )
if (fabs(a[i][n+1]) > eps)
return 2;
return 1; // 有无穷多组解
}
return 0; // 有唯一解
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n + 1; j ++ )
scanf("%lf", &a[i][j]);
int type = gauss();
if (type == 2) puts("No solution");
else if (type == 1) puts("Infinite group solutions");
else
{
for (int i = 1; i <= n; i ++ )
{
if (fabs(a[i][n+1]) < eps) a[i][n+1] = 0;//小于eps即为0;
printf("%.2lf\n", a[i][n+1]);
}
}
return 0;
}
例题acwing884. 高斯消元解异或线性方程组
异或又名不进位加法。
可以用高斯消元解决此问题。
与上题的区分是将+-换成了^(异或)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 1e2+10;
int n;
int a[N][N];
int gauss()
{
int c, r;
for (c = 1, r = 1; c <= n; c ++ )
{
int t = r;
for (int i = r; i <= n; i ++ )
if (a[i][c])
t = i;
if (!a[t][c]) continue;
for (int i = c; i <= n+1; i ++ ) swap(a[r][i], a[t][i]);
for (int i = r + 1; i <= n; i ++ )
if (a[i][c])
for (int j = n + 1 ; j >= c; j -- )
a[i][j] ^= a[r][j];
r ++ ;
}
if (r <= n)
{
for (int i = r; i <= n; i ++ )
if (a[i][n+1])
return 2;
return 1;
}
for (int i = n - 1; i ; i -- )
for (int j = i + 1; j <= n; j ++ )
if(a[i][j])a[i][n+1] ^= a[j][n+1];
return 0;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n + 1; j ++ )
cin >> a[i][j];
int t = gauss();
if (t == 0)
{
for (int i = 1; i <= n; i ++ ) cout << a[i][n+1] << endl;
}
else if (t == 1) puts("Multiple sets of solutions");
else puts("No solution");
return 0;
}