高斯消元法主要有消元和回代两个过程,和其他求解线性方程组的方法如克莱姆法则,约当消除法相比运算量小,速度快。更适合在计算机内使用。

假设有n个方程,var个变量,时间:

for i=1 --> n-1

    for j=i+1-->n

          for k=j+1-->n+1

所以时间是O(n^3)

把方程组写成增广矩阵,进行行初等变换,系数矩阵形成上三角矩阵,然后回代逐一求解每一个未知数。

结果又分为:无解;无穷多的解(找出自由变量);浮点数解;唯一整数解等情况

高斯消元的模板:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
const int MAXN=50;

int a[MAXN][MAXN];//增广矩阵
int x[MAXN];//解集
bool free_x[MAXN];//不确定的变元
int equ,var,free_num;
void Debug()
{
int i, j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
}

int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}

// 高斯消元法解方程组(free_num=-2表示有浮点数解,但无整数解,
//-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
//有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var.
int Gauss(int equ,int var)
{
int i,j,k;
int max_r;
int col;//当前处理的列
int ta,tb;
int LCM;
int temp;
int free_x_num;
int free_index;

col=0;
for(k = 0;k < equ && col < var;k++,col++)
{// 找到该col列元素绝对值最大的那行与第k行交换.(为了处理无解的判断)
max_r=k;
for(i=k+1;i<equ;i++)
{
if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
}
if(max_r!=k)
{
for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
}
//Debug();
if(a[k][col]==0)
{// 说明该col列第k行以下全是0了,则处理当前行的下一列.
k--;
continue;
}
for(i=k+1;i<equ;i++)
{
if(a[i][col]!=0)
{
LCM = lcm(abs(a[i][col]),abs(a[k][col]));
ta = LCM/abs(a[i][col]);
tb = LCM/abs(a[k][col]);
if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况
for(j=col;j<var+1;j++)
{
a[i][j] = a[i][j]*ta-a[k][j]*tb;
}
}
}
//Debug();
}

// 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).
for (i = k; i < equ; i++)
{
if (a[i][col] != 0) return -1;
}
if (k < var)
{
//自由变元,不确定的变元至少有var - k个.
for (i = k - 1; i >= 0; i--)
{
free_x_num = 0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它为不确定的变元.
for (j = 0; j < var; j++)
{
if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
}
if (free_x_num > 1) continue;
temp = a[i][var];
for (j = 0; j < var; j++)
{
if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
}
x[free_index] = temp / a[i][free_index];
free_x[free_index] = 0;
}
return var - k; // 自由变元有var - k个.
}
for (i = var - 1; i >= 0; i--)
{
temp = a[i][var];
for (j = i + 1; j < var; j++)
{
if (a[i][j] != 0) temp -= a[i][j] * x[j];
}
if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解.
x[i] = temp / a[i][i];
}
return 0;
}
void in(){
memset(a, 0, sizeof(a));
memset(x,0,sizeof(x));
memset(free_x,1,sizeof(free_x));
int i,j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)scanf("%d", &a[i][j]);
}
}
void out(){
int i,j;
if (free_num == -1) printf("no solutions!\n");
else if (free_num == -2) printf("here are double but not Integer solutions!\n");
else if (free_num > 0)
{
printf("here are many solutions and var number is %d\n", free_num);
for (i = 0; i < var; i++)
{
if (free_x[i]) printf("x%d is not certain\n", i + 1);
else printf("x%d: %d\n", i + 1, x[i]);
}
}
else
for (i = 0; i < var; i++)printf("x%d: %d\n", i + 1, x[i]);
printf("\n");
}
int main(void)
{
freopen("cin.txt", "r", stdin);
int i, j;
while (scanf("%d %d", &equ, &var) != EOF)
{
in();
free_num = Gauss(equ,var);
out();
}
return 0;
}

测试用例:

3 3

2 1 -1 8

-3 -1 2 -11

-2 1 2 -3

3 3

3 2 0 0

1 1 0 -1

0 0 0 0

3 3

3 2 0 0

1 1 1 -1

0 1 1 -2

3 3

3 2 0 0

1 1 0 -1

1 1 0 0

对应的答案是:

2

3

-1

2

-3

任意数

1

-3/2

-1/2

程序的结果:
x1: 2
x2: 3
x3: -1

here are many solutions and var number is 1
x1: 2
x2: -3
x3 is not certain

here are double but not Integer solutions!

no solutions!

中间的过程(以:

3 3
2 1 -1 8
-3 -1 2 -11
-2 1 2 -3
为例):
-3 -1 2 -11
2 1 -1 8
-2 1 2 -3

-3 -1 2 -11
0 1 1 2
0 5 2 13

-3 -1 2 -11
0 5 2 13
0 1 1 2

-3 -1 2 -11
0 5 2 13
0 0 3 -3

-3 -1 2 -11
0 5 2 13
0 0 3 -3

-3 -1 2 -11
0 5 2 13
0 0 3 -3

x1: 2
x2: 3
x3: -1

高斯消元过程仅仅涉及行变换,所以得到的X[]解集顺序是不变的。

压缩的写法:


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=105;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
// a[][]:增广矩阵 n: 方程数 m:变量数
void Gauss(int a[N][N],int n,int m,int &r,int &c){
for(r=0,c=0;r<n&&c<m;r++,c++){
int maxi=r;
for(int i=r+1;i<n;i++){
if(abs(a[i][c])>abs(a[maxi][c])) maxi=i;
}
if(maxi!=r){
for(int j=r;j<m+1;j++){ // 1
swap(a[r][j],a[maxi][j]);
}
}
if(a[r][c]==0){
r--;
continue;
}
for(int i=r+1;i<n;i++){
if(a[i][c]!=0){
int x=abs(a[i][c]);
int y=abs(a[r][c]);
int lcm=x/gcd(x,y)*y;
int tx=lcm/x;
int ty=lcm/y;
if(a[i][c]*a[r][c]<0) ty=-ty;
for(int j=c;j<m+1;j++){
a[i][j]=a[i][j]*tx-a[r][j]*ty;
}
}
}
}
}
// -2 没有整数解 -1:无解 0: 唯一解 m-r:(自由变元个数)无穷多个解
int reback(int a[][N],int n,int m,int r,int c,int x[],bool tag[]){
for(int i=r;i<n;i++) if(a[i][c]!=0) return -1;
if(r<m){
memset(tag,0,sizeof(tag));
for(int i=r-1;i>=0;i--){
int dex=0;
int cnt=0;
for(int j=0;j<m;j++){
if(a[i][j]!=0&&tag[j]==0){
cnt++;
dex=j;
}
}
if(cnt>1) continue;
int t=a[i][m];
for(int j=0;j<m;j++){
if(a[i][j]!=0&&j!=dex){
t-=a[i][j]*x[j];
}
}
x[dex]=t/a[i][dex];
tag[dex]=1;
}
return m-r;
}
for(int i=r-1;i>=0;i--){
int t=a[i][c];
for(int j=i+1;j<c;j++) t-=a[i][j]*x[j];
if(t%a[i][i]!=0) return -2;
x[i]=t/a[i][i];
}
return 0;
}

int a[N][N];
int x[N];
bool tag[N];

int main(){
//freopen("cin.txt","r",stdin);
int n,m;
while(cin>>n>>m){
for(int i=0;i<n;i++){
for(int j=0;j<m+1;j++){
scanf("%d",&a[i][j]);
}
}
int r,c;
Gauss(a,n,m,r,c);
reback(a,n,m,r,c,x,tag);
for(int i=0;i<n;i++){
for(int j=0;j<m+1;j++) cout<<a[i][j]<<" ";
cout<<endl;
}
for(int i=0;i<r;i++) cout<<x[i]<<" "; cout<<endl;
}
return 0;
}


浮点数型的高斯消元法:



#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N=105;
const double eps=1e-7;
int myCmp(double a,double b){
if(fabs(a-b)<eps) return 0;
return (a>b)-(a<b);
}
double myMod(double a,double m){
while(myCmp(a,m)>=0) a-=m;
while(myCmp(a,0)<0) a+=m;
return a;
}
double gcd(double a,double b){
return myCmp(b,0)==0?a:gcd(b,myMod(a,b));
}
// a[][]:增广矩阵 n: 方程数 m:变量数
void Gauss(double a[N][N],int n,int m,int &r,int &c){
for(r=0,c=0;r<n&&c<m;r++,c++){
int maxi=r;
for(int i=r+1;i<n;i++){
if(myCmp(abs(a[i][c]),abs(a[maxi][c]))>0) maxi=i;
}
if(maxi!=r){
for(int j=r;j<m+1;j++){ // 1
swap(a[r][j],a[maxi][j]);
}
}
if(myCmp(a[r][c],0)==0){
r--;
continue;
}
for(int i=r+1;i<n;i++){
if(myCmp(a[i][c],0)!=0){
double x=fabs(a[i][c]);
double y=fabs(a[r][c]);
double lcm=x*y/gcd(x,y);
double tx=lcm/x;
double ty=lcm/y;
if(a[i][c]*a[r][c]<-eps) ty=-ty;
for(int j=c;j<m+1;j++){
a[i][j]=a[i][j]*tx-a[r][j]*ty;
}
}
}
}
}
// -1:无解 0: 唯一解 m-r:(自由变元个数)无穷多个解
int reback(double a[][N],int n,int m,int r,int c,double x[],bool tag[]){
for(int i=r;i<n;i++) if(myCmp(a[i][c],0)!=0) return -1;
if(r<m){
memset(tag,0,sizeof(tag));
for(int i=r-1;i>=0;i--){
int dex=0;
int cnt=0;
for(int j=0;j<m;j++){
if(myCmp(a[i][j],0)!=0&&tag[j]==0){
cnt++;
dex=j;
}
}
if(cnt>1) continue;
double t=a[i][m];
for(int j=0;j<m;j++){
if(myCmp(a[i][j],0)!=0&&j!=dex){
t-=a[i][j]*x[j];
}
}
x[dex]=t/a[i][dex];
tag[dex]=1;
}
return m-r;
}
for(int i=r-1;i>=0;i--){
double t=a[i][c];
for(int j=i+1;j<c;j++) t-=a[i][j]*x[j];
//if(myCmp(myMod(t,a[i][i]),0)!=0) return -2;
x[i]=t/a[i][i];
}
return 0;
}

double a[N][N];
double x[N];
bool tag[N];

int main(){
freopen("cin.txt","r",stdin);
int n,m;
while(cin>>n>>m){
for(int i=0;i<n;i++){
for(int j=0;j<m+1;j++){
scanf("%lf",&a[i][j]);
}
}
int r,c;
Gauss(a,n,m,r,c);
reback(a,n,m,r,c,x,tag);
/*for(int i=0;i<n;i++){
for(int j=0;j<m+1;j++) cout<<a[i][j]<<" ";
cout<<endl;
}*/
for(int i=0;i<r;i++) {
printf("%.15lf ",x[i]);
}
cout<<endl;
}
return 0;
}


用几个式子验证,结果让人满意。


高斯消元法_#include

高斯消元法_i++_02

高斯消元法_i++_03


用mathematica计算:

In[1]:= Solve[{0.001*x1 + 2 x2 == 1, 3*x1 + 5*x2 == 2}, {x1, x2}]

Out[1]= {{x1 -> -0.166806, x2 -> 0.500083}}

In[2]:= Solve[{0.0000000000000001*x1 + 20000*x2 == 10000, 300*x1 + 500*x2 == 2000}, {x1, x2}]

Out[2]= {{x1 -> 5.83333, x2 -> 0.5}}

In[3]:= Solve[{0.0000000000000000001*x1 + 20000*x2 == 10000, 30000000000000*x1 + 500*x2 == 2000}, {x1, x2}]

Out[3]= {{x1 -> 5.83333*10^-11, x2 -> 0.5}}

高斯运算结果:
-0.166805671392827 0.500083402835696
5.833333333333333 0.500000000000000
0.000000000058333 0.500000000000000

Process returned 0 (0x0) execution time : 0.375 s
Press any key to continue.