高斯消元学习笔记

参考文章:高斯消元(Gauss消元)https://www.cnblogs.com/xcg123/p/10679600.html
矩阵相关定义性质全总结 https://blog.csdn.net/I_canjnu/article/details/105778485
用来解n元一次方程组。高斯消元问题实际上就是n元一次方程组的问题(类似CRT的思路)。
操作很简单:先从定位点(i,i)往下找第一个当前列非零的一行,换到当前列。
再让这行除以(i,i)上的数,(i,i)即变成了1。
利用这个1,让所有行减去第i列数倍的第i行,第i列就消好了。
时间复杂度n^ 3,准确来说n^2 * m

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,pl;
double a[1001][1001];//注意数据类型
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
       for(int j=1;j<=n+1;j++)//高斯消元的矩阵是n*(n+1)的,别忘了最后一列
       cin>>a[i][j];
    for(int i=1;i<=n;i++)
    {
        pl=i;
        while(a[pl][i]==0&&pl<=n) 
        pl++;                                    
         // 判断第i列首元素非0的最上行,因为第i行第i列元素不能为0 
        if(pl==n+1) {cout<<"No Solution";return 0;}    
        //一直判到了n+1行,可是一共才只有n行,说明有一列全为0,无解 
        for(int j=i;j<=n+1;j++)             //将第i行第i列元素不为0的那一行与当前行交换 
        swap(a[i][j],a[pl][j]);
        double k=a[i][i];                          //让第i行每个元素都除以a[i][i]使得a[i][i]为1 
        for(int j=i;j<=n+1;j++)
        a[i][j]=a[i][j]/k;                         //将第i行第i列的元素消成1,注意同行进行同样的操作 
        for(int j=1;j<=n;j++)
        {
            if(i!=j)                        //将第i列除了第i行的元素全消成0 
            {                               //方法是第j行每个元素a[j][m]都减去a[j][1]*a[i][m] 
                double ki=a[j][i];
                for(int m=i;m<=n+1;m++)
                a[j][m]=a[j][m]-ki*a[i][m];
            }
        }
    }
    for(int i=1;i<=n;i++)
    printf("%.2lf\n",a[i][n+1]);
    return 0;
}

因为方程组的常数项,有解的n元一次方程组对应的矩阵大小为n*(n+1)。

要注意:矩阵并非完全等价于方程组,只是矩阵初等变换的倍乘、互换、倍加行在方程组方面的操作也是可行的,而倍加列则不可行。在消元过程中除了某一列都变为0的方程组无解的情况,当不同的方程个数多于未知数时也能消元成斜线为1的情况,不过这时在方程组方面,则是得出类似“0=一个非零常数”这样矛盾的式子,说明方程组无解。
换句话说,消好后的矩阵要根据实际情况分析合理性。根据实际操作,把矩阵的一行看做 和等于0 还是 前面的数之和等于最后一个数。熟练后,甚至不用让1排成一斜线,也不用把每项消元为1,只需范围内其他项为0即可(减少实数运算次数,减少误差)(范围内每行每列只有一个非零数)

进阶应用:一行进行数乘只能确保一个“1”用来消“1”所在的那一列。当方程数n多于未知数个数时,可以选择把矩阵消成有n* n 范围为斜线为1其他全0的样子,将1行看做总和为0的情况,即得到了关于其他无法消元部分的线性关系式。
例题:【CF113D】Museum【概率期望】【高斯消元】 有学习笔记的代码注释。

实际应用时,不同于模板题,要编号;把每行看做和为0的表达式/方程,对每行写每列对应项的系数,表达式“x=……”的话,等号右边记完了,别忘了等号左边。

注意点:
实数运算下,因为误差,判断x==0时要写成 if(abs(x)< eps ),eps为来应对误差的很小的值。
在进行一行的消元时,要倒着消,否则用来当乘数的该行最前面的数会变化,这时就错了。

posted @ 2022-04-23 18:39  千叶繁华  阅读(34)  评论(0编辑  收藏  举报