高斯消元模板

梦想与她 提交于 2019-12-20 07:58:50

高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵。
高斯消元法的原理是:
若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组。
所以我们可以用初等行变换把增广矩阵转换为行阶梯阵,然后回代求出方程的解。

以上是线性代数课的回顾,下面来说说高斯消元法在编程中的应用。

首先,先介绍程序中高斯消元法的步骤:
(我们设方程组中方程的个数为equ,变元的个数为var,注意:一般情况下是n个方程,n个变元,但是有些题目就故意让方程数与变元数不同)

1. 把方程组转换成增广矩阵。

2. 利用初等行变换来把增广矩阵转换成行阶梯阵。
枚举k从0到equ – 1,当前处理的列为col(初始为0) ,每次找第k行以下(包括第k行),col列中元素绝对值最大的列与第k行交换。如果col列中的元素全为0,那么则处理col + 1列,k不变。

3. 转换为行阶梯阵,判断解的情况。

① 无解
当方程中出现(0, 0, …, 0, a)的形式,且a != 0时,说明是无解的。

② 唯一解
条件是k = equ,即行阶梯阵形成了严格的上三角阵。利用回代逐一求出解集。

③ 无穷解。
条件是k < equ,即不能形成严格的上三角形,自由变元的个数即为equ – k,但有些题目要求判断哪些变元是不缺定的。
    这里单独介绍下这种解法:
首先,自由变元有var - k个,即不确定的变元至少有var - k个。我们先把所有的变元视为不确定的。在每个方程中判断不确定变元的个数,如果大于1个,则该方程无法求解。如果只有1个变元,那么该变元即可求出,即为确定变元。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 507
using namespace std;
//freopen("din.txt","r",stdin);

int a[N][N],X[N];//分别记录增广矩阵和解集
int free_x[N];//记录自由变量
int equ,var;//分别表示方程组的个数和变量的个数

int GCD(int x,int y){
    if (y == 0) return x;
    return GCD(y,x%y);
}
int LCM(int x,int y){
    return x/GCD(x,y)*y;
}
void Debug(void)
{
    int i, j;
    for (i = 0; i < equ; i++)
    {
        for (j = 0; j < var + 1; j++)
        {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}
// 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)
int Guass(){
    int i,j,k,col;
    CL(X,0); CL(free_x,1);//把解集清空,所有变量都标为自由变量

    for (k = 0,col = 0; k < equ && col < var; ++k, ++col){//枚举行列
        int max_r = k;//找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)
        for (i = k + 1; i < equ; ++i){
            if (iabs(a[i][col]) > iabs(a[max_r][col])) max_r = i;
        }
        if (max_r != k){//交换
            for (i = k; i < var + 1; ++i) swap(a[k][i],a[max_r][i]);
        }
        if (a[k][col] == 0){//如果对应该列都为0,枚举该行的下一列
            k--; continue;
        }
        for (i = k + 1; i < equ; ++i){//将k后边的col进行初等变换成行阶梯矩阵
            if (a[i][col] != 0){
                int lcm = LCM(a[k][col],a[i][col]);
                int ta = lcm/iabs(a[i][col]); int tb = lcm/iabs(a[k][col]);
                if (a[i][col]*a[k][col] < 0) tb = -tb;
                for (j = col; j < var + 1; ++j){
                    a[i][j] = ta*a[i][j] - tb*a[k][j];
                }
            }
        }
    }
    Debug();
    // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). 即R(A) != R(A')无解
    for (i = k; i < equ; ++i){
        if (a[i][col] != 0) return -1;
    }
   // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.
   // 且出现的行数即为自由变元的个数.   即R(A) = R(A') < n
    if (k < var){
         // 首先,自由变元有var - k个,即不确定的变元至少有var - k个.
        int num = 0,freeidx;
        for (i = k - 1; i >= 0; --i){
            num = 0;// 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.
            int tmp = a[i][var];
            // 第i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ行.
            // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的.
            for (j = 0; j < var; ++j){
                if (a[i][j] != 0 && free_x[j]){
                    num++;
                    freeidx = j;
                }
            }
            if (num > 1) continue; // 无法求解出确定的变元.
          // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.
            tmp = a[i][var];
            for (j = 0; j < var; ++j){
                if (a[i][j] && j != freeidx) tmp -= a[i][j]*X[j];
            }
            X[freeidx] = tmp/a[i][freeidx];
            free_x[freeidx] = 0;
        }
        return var - k;
    }
    // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵.
    // 计算出Xn-1, Xn-2 ... X0.
    for (i = k - 1; i >= 0; --i){
        int tmp = a[i][var];
        for (j = i + 1; j < var; ++j){
            tmp -= a[i][j]*X[j];
        }
        X[i] = tmp/a[i][i];
    }
    return 0;
}
int main(){
    //freopen("din.txt","r",stdin);
    int i,j;
    while (~scanf("%d%d",&equ,&var)){
        for (i = 0; i < equ; ++i)
        for (j = 0; j < var + 1; ++j) scanf("%d",&a[i][j]);
        Debug();

        int free_num = Guass();

        if (free_num == -1) printf("无解!\n");
        else if (free_num == -2) printf("无整数解\n");
        else if (free_num > 0){
            printf("无穷多解! 自由变元个数为%d\n", free_num);
            for (i = 0; i < var; ++i){
                if (free_x[i]) printf("X%d 是不确定的\n",i + 1);
                else printf("X%d %d",i + 1,X[i]);
            }
        }
        else{
            for (i = 0 ; i < var; ++i){
                printf("X%d %d\n",i + 1,X[i]);
            }
        }
        printf("\n");
    }
    return 0;
}
/*
4 3
1 1 2 1
2 -1 2 4
1 -2 0 3
4 1 4 2
4 4
1 -2 3 -4 4
0 1 -1 1 -3
1 3 0 -3 1
0 -7 3 1 -3
4 5
1 -2 3 -1 -1 2
1 1 -1 1 -2 1
2 -1 1 0 -2 2
2 2 -5 2 -1 5
2 2 -5 2 -1 5
*/
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!