高斯消元(写(shui)题必备)

前言:
在一次学校hu测中,
遇到一道正解不用高斯消元,但是部分分需要的中档题
用舒老师的话说,只要是会高斯消元和树形dp
乱搞一下那道题就可以水到70
所以还是学习一下这个很有用算法:高斯消元

简介


数学上,高斯消元法(或译:高斯消去法),是线性代数中的一个算法,
可用来为线性方程组求解,求出矩阵的秩,以及求出可逆方阵的逆矩阵。
当用于一个矩阵时,高斯消元法会产生出一个“行梯阵式”
高斯消元法可以用在电脑中来解决数千条等式及未知数。
不过,如果有过百万条等式时,这个算法会十分费时。一些极大的方程组通常会用迭代法来解决。
亦有一些方法特地用来解决一些有特别排列的系数的方程组。

手玩

高斯消元简单来说就是我们平时的解方程
然而我在简介中提到了一个“行梯阵式”

行梯矩阵
指一个矩阵每个非零行的非零首元都出现在上一行非零首元的右边,
同时没有一个非零行出现在零行之下。
如:
1 3 0 1
0 2 1 0
0 0 0 1

我们还是稍微来看一下手玩版的高斯消元

下面的是咱们要求解的线性方程组,先把四个方程编上序号。
这里写图片描述

先把第一行乘以1/2,然后把第一行的相应倍数加到第二、三、四行上。
这里写图片描述

再把第二行乘以-2,接着将其相应的倍数加到第三、四行上,然后把第三行乘以-1/6。
这里写图片描述

将第三行的二倍加到第四行上,再把第四行乘以3/7。
这里写图片描述

然后往回代,就是把第四行的相应倍数加到第一、第二、第三行上;
把第三行的相应倍数加到第一、第二行上。
这里写图片描述

再把第二行的相应倍数加到第一行上,最后根据得出的矩阵列出方程组,解得最后的解。
这里写图片描述

总结上面过程,高斯消元法其实就是下面非常简单的过程

原线性方程组 ——> 高斯消元法 ——> 下三角或上三角形式的线性方程组 ——> 前向替换算法求解(对于上三角形式,采用后向替换算法)

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

① 无解
当方程中出现(0, 0, …, 0, a)的形式,且a != 0时,说明是无解的。
这里写图片描述

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

③ 无穷解。
条件是k < equ,即不能形成严格的上三角形,自由变元的个数即为equ – k,但有些题目要求判断哪些变元是不确定的。
这里写图片描述

这里单独介绍下这种解法:
首先,自由变元有var - k个,即不确定的变元至少有var - k个。
我们先把所有的变元视为不确定的。
在每个方程中判断不确定变元的个数,如果大于1个,则该方程无法求解。
如果只有1个变元,那么该变元即可求出,即为确定变元。

程序实现

bool guass()
{
    int now=1,to;
    double t;
    for (int i=1;i<=n;i++)   //枚举未知量 
    {
        for (to=now;to<=n;to++)
            if (fabs(a[to][i])>eps) break;
        if (to>n) continue;
        if (to!=now)
           for (int j=1;j<=n+1;j++)
               swap(a[to][j],a[now][j]);
        t=a[now][i];
        for (int j=1;j<=n+1;j++) a[now][j]/=t;  //系数化为一 
        for (int j=1;j<=n;j++)
            if (j!=now)
            {
                t=a[j][i];
                for (int k=1;k<=n+1;k++)
                    a[j][k]-=t*a[now][k];
            }
        now++;
    }
    for (int i=now;i<=n;i++)
        if (fabs(a[i][n+1])>eps) return 0;
    return 1;
}

我尝试这解释一下这个函数是怎么发挥作用的:
我们要调用这个函数的前提是我们已经有了一个阵式

for (int i=1;i<=n;i++)   //枚举未知量 

实际上就是模拟手玩过程中一行一行进行消元
(第i行消掉的一定是第i个未知量)

变量now和to:
因为我们最后计算出来的一定是行阶阵式
now记录的就是当前行从左起第一个非零元素的位置

for (to=now;to<=n;to++)
    if (fabs(a[to][i])>eps) break;

找到未处理的方程中,第now个未知量的系数不为零的方程
(这些方程是需要消元的)

t=a[now][i];
for (int j=1;j<=n+1;j++) a[now][j]/=t;  //系数化为一 

方程的所有系数都除以t
以此为基准进行之后的消元

for (int j=1;j<=n;j++)
    if (j!=now)
    {
        t=a[j][i];
        for (int k=1;k<=n+1;k++)
        a[j][k]-=t*a[now][k];
    }
now++;

其余的每一行进行消元
完成之后now++
相当于整个方程组减少了一个未知量

for (int i=now;i<=n;i++)
    if (fabs(a[i][n+1])>eps) return 0;

最后有一个很简单的判断
看一下是不是唯一解

原文地址:https://www.cnblogs.com/wutongtong3117/p/7673087.html