高斯消元法
数学上,高斯消元法(Gaussian Elimination或译:高斯消去法),是线性代数规划中的一个算法,可用来为线性方程组求解。在许多应用中,我们需要解一个包含n个方程的n元联立方程组:
其中,n是一个大数。高斯消去法的思路是把n个线性方程构成的n元联立方程组变成一个等价方程组(也就是说,它的解和原来的方程组相同),该方程组有着一个上三角形的系数矩阵,这种矩阵的主对角线下方元素全部为0。
用矩阵的符号,我们可以把它写成 A x = b = > A ’ x = b ′ {\boldsymbol{A}x=\boldsymbol{b} => \boldsymbol{A’}x=\boldsymbol{b'}} Ax=b=>A’x=b′,其中:
高斯消元法的核心
将一个具有任意系数矩阵的方程组 A \boldsymbol{A} A推导出一个具有上三角系数矩阵的等价方程组 A ′ \boldsymbol{A'} A′是高斯消元法需要解决的一个关键问题,这一部分可以通过一系列初等变换(elementary operation)来做到,而所谓的初等变换就是:
- 交换方程组中两个方程的位置
- 把一个方程替换为它的非零倍
- 把一个方程替换为它和另一个方程倍数之间的和或差
高斯消元法的过程
高斯消元法需要经过前向消去和反向替换两个步骤。其中,前向消去是利用初等变换来实现从任意系数矩阵方程组变换成具有上三角系数矩阵的等价方程组的过程,反向替换是在前向消去的基础上,从求解 x n {x_{n}} xn(在上三角系数矩阵的等价方程组中,直接利用 b n ′ / a n n ′ {b'_{n}/a'_{nn}} bn′/ann′得到)开始,将其带回至上一个方程组来求解 x n − 1 x_{n-1} xn−1,以此类推,逆向求得所有解的过程。
前向消去的实现
// 输入任意矩阵的引用,返回的布尔变量表示矩阵是否有唯一解
// 经过前向消去后,矩阵将转变为具有上三角系数的矩阵
bool ForwardElimination(vector<vector<double>>& matrix){
for(int i=0; i<matrix.size()-1; i++){
// 部分选主元法,从i至n行中,找到n-i+1列中的最大值所在的行
int pivotrow = i;
for(int j=i+1; j<matrix.size(); j++){
if(abs(matrix[j][i]) > abs(matrix[pivotrow][i])){
pivotrow = j;
}
}
// 由于上三角形的对角线一定不为零,所以如果出现零,则表示解不唯一或无解
// 同时,这一步可以排除部分主元为零,而出现除零的现象
if(matrix[pivotrow][i] == 0){
return false;
}
// 将找到的最大值所在的行与第i行进行交换
for(int k=i; k<matrix.size()+1 && i != pivotrow; k++){
swap(matrix[i][k], matrix[pivotrow][k]);
}
// 将第i+1行到第n行的方程组都减去第i行乘一个数得到的方程组
for(int j=i+1; j<matrix.size(); j++){
double temp = matrix[j][i] / matrix[i][i];
for(int k=i; k<matrix.size()+1; k++){
matrix[j][k] = matrix[j][k] - matrix[i][k]*temp;
}
}
}
// 需要单独检测上三角形的对角线上最后一个元素是否为零,如果为零,则表示解不唯一或无解
if(matrix[matrix.size()-1][matrix.size()-1] == 0){
return false;
}
return true;
}
反向替换的实现
// 函数返回解向量
vector<double> BackSubstitution(vector<vector<double>>& matrix){
vector<double> solution(matrix.size(), 0);
for(int i=matrix.size()-1; i>=0; i--){
for(int j=matrix.size()-1; j>i;j--){
matrix[i][matrix.size()] -= matrix[i][j]*solution[j];
}
solution[i] = matrix[i][matrix.size()] / matrix[i][i];
}
return solution;
}
Gauss消元求解方程组
#include<iostream>
#include<vector>
#include<iomanip>
#include<cmath>
using namespace std;
void swap(double& a, double& b){
double temp = a;
a = b;
b = temp;
}
bool ForwardElimination(vector<vector<double>>& matrix){
for(int i=0; i<matrix.size()-1; i++){
int pivotrow = i;
for(int j=i+1; j<matrix.size(); j++){
if(abs(matrix[j][i]) > abs(matrix[pivotrow][i])){
pivotrow = j;
}
}
if(matrix[pivotrow][i] == 0){
return false;
}
for(int k=i; k<matrix.size()+1 && i != pivotrow; k++){
swap(matrix[i][k], matrix[pivotrow][k]);
}
for(int j=i+1; j<matrix.size(); j++){
double temp = matrix[j][i] / matrix[i][i];
for(int k=i; k<matrix.size()+1; k++){
matrix[j][k] = matrix[j][k] - matrix[i][k]*temp;
}
}
}
if(matrix[matrix.size()-1][matrix.size()-1] == 0){
return false;
}
return true;
}
vector<double> BackSubstitution(vector<vector<double>>& matrix){
vector<double> solution(matrix.size(), 0);
for(int i=matrix.size()-1; i>=0; i--){
for(int j=matrix.size()-1; j>i;j--){
matrix[i][matrix.size()] -= matrix[i][j]*solution[j];
}
solution[i] = matrix[i][matrix.size()] / matrix[i][i];
}
return solution;
}
int main()
{
int n;
cin >> n;
vector<vector<double>> matrix(n, vector<double>(n+1, 0));
for(int i=0; i<n; i++){
for(int j=0; j<n+1; j++){
cin >> matrix[i][j];
}
}
bool onlyS = ForwardElimination(matrix);
if(onlyS){
vector<double> solution = BackSubstitution(matrix);
for(int i=0; i<solution.size(); i++){
cout << fixed <<setprecision(2) << solution[i] << endl;
}
}else{
cout << "No Solution";
}
return 0;
}