高斯消元

求n元一次方程组的解,我们知道n元一次方程组需要n个式子我们可以把n个式子写成n*(n+1)的矩阵,最后一列是等号右边的常数。

回忆一下我们小学学的二元一次方程组,其中一个式子所有系数乘以k,等式仍然成立,到我们的矩阵里就是某一行所有数同时乘或除一个数式子仍然成立;还有加减消元,就是令第一行-第二行,使得某一个未知数的系数变为0,变成kx=b的形式从而求的x的值,在矩阵里就是我们可以让某一行的式子减去另一行的k倍,从而消去一个变量的系数。来看一下我们的高斯消元是怎么做的:

  step1:把第i行的第i个数变成1,也就是我们打算用第i行来求第i个元素的值,想让第i个系数变成1只需要让第i行左右两边同时除以第i个系数就可以了。

  step2:把除了第i行之外的其他行的第i个系数都变成0,这个稍微复杂一点点,我们慢慢来讲

首先最后达到的效果是

1 0 0   x

0 1 0  y

0 0 1  z

其中x,y,z都是常数,那么显然a1=x,a2=y,a3=z,接下来我们只需要考虑如何来实现step2,假设我们现在正在处理第i个元素,并且已经完成了第一步,这时除了对角线,每一行的前i-1个系数都是0了,并且a[i][i]=1,a[j][i](即第j行的第i个元素)就是a[i][i]的a[j][i]倍,那么第j行减a[j][i]倍的第i行就可以使第j行的第i个元素系数变为0了,并且由于第i行的前i-1个数都是0,不会对已经求出来的0造成任何影响。

最后还有一点值得注意的就是,如果有一个未知数他的所有系数都是0,那么这个未知数是无法确定的,所以方程无解

代码:

#include<iostream>
#include<cstdio>
#define eps 1e-8
using namespace std;
int n;
double a[110][110];
template<typename T>T ab(T &a)
{
    return a>0?a:-a;
}
//把第i行第i列的元素系数变成1,第i行其他元素系数变成0,那么第i行就存储着第i个元素的值 
bool gauss()
{
    for(int i=1;i<=n;++i)//循环每一个元素
    {
        int k=i;
        for(int j=i+1;j<=n;++j)
            if(ab(a[j][i])>ab(a[k][i]))k=j;//找这个元素的最大的系数 
        if(ab(a[k][i])<eps)
        {
            return 1;//相当于是0,系数都是0代表无解 
        }
        swap(a[i],a[k]);//运算系数最大的那一行,减小误差 
        double now=a[i][i];
        for(int j=1;j<=n+1;++j)a[i][j]/=now;//首先这一行的每个数都除以a[i][i]就可以使a[i][i]变成1 
        a[i][i]=1;
        for(int j=1;j<=n;++j)//把第i列的其他元素都变成0 ,所以得循环每一行 
        {
            if(j!=i) 
            {
                double now=a[j][i];
                for(int k=1;k<=n+1;++k)
                    a[j][k]-=now*a[i][k];//加减消元把其他的都消掉 
                //第j行减去now倍的第j行 ,因为第i行系数是1,第j行系数是a[j][i],所以得都减去now倍的才能变成0 
                //这一行都减去其他行的now倍,矩阵值不变 
            }
        }
    }
    return 0;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n+1;++j)
            scanf("%lf",&a[i][j]);
    if(gauss())printf("No Solution");
    else{
        for(int i=1;i<=n;++i)printf("%.2lf
",a[i][n+1]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yuelian/p/12493138.html