高斯消元法

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

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




用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.


你可能感兴趣的:(高斯消元)