高斯消元讲解 && 洛谷P3389 【模板】高斯消元法 题解

前言:回去刷数论的题,发现自己的高斯消元是0分。。。。。。很可能是当年交上去了没看结果就直接过了。。。。。。回来博客园搜自己之前对高斯消元的讲解想看看,没看到。只能从头过了一遍,顺便补上之前的锅。


高斯消元:

什么是高斯消元线性代数规划中的一个算法,可用来为线性方程组求解——度娘

在现阶段的学习中,高斯消元常被应用于求多项式的解:

形如

x+3y+2z=1,

2x+7y-z=2,

3x-2y+5z=9。

的多项式。

根据平常文化课的学习,我们不难知道对于n个未知数,只要给你n个不同(化简后依然不同)的多项式即可求出每一个未知数的解。在2元方程以下我们一般直接秒掉。3元也不是很难,再往上的就有点费精力了。高斯消元就是将多项式系数和结果抽象成一个矩阵,经过一些颠三倒四不知所以的操作后,成功求出多项式的解的过程。


举例:(借用了pksAFO巨佬的例子)

 以上是三个三元方程,我们将其抽象成矩阵后,变为:

|3     2     1     10|

|5     1     6     25|

|2     3     4     20|(这个蒟蒻不会markdown?踩他

假设有n个未知数,那么矩阵就有n行n+1列。


此矩阵的奇怪性质:

1.对于多个多项式,你把其中一个写在最上方,或者写在最下方,很明显就是换了个顺序,所以对答案并没有什么影响。也就是,矩阵的行可以任意互换

2.小学学过的方法:对于一整行多项式,你把它整体上扩大/缩小多少倍,对答案没有影响。矩阵同一行上的数,可以任意扩大/缩小k倍(k为实数)

3.二元方程常用方法:加减相消。具体例子:

x+2y=5;  1式

3x-2y=-1;  2式

2式+1式得:4x=4,x=1,y=2。

换句话说,我们可以这样表达:

(1+3)x+(2-2)y=5-1;

0x+0y=0;(加到1式上了,没用了)

变成这样:

4x+0y=4;

0x+0y=1;

回过来,我们可以把这两个二元多项式方程看做矩阵:

|1     2     5|

|3     -2   -1|

我们可以将第二行整体加到第一行上,使得矩阵变成了:

|4     0     4|

|0     0     0|

求得x=1,回带得y=2。

对于矩阵上任意一行,我们都可以将其整体地对于另一行进行加/减


如何利用这三个性质进行求方程解?

灵活运用性质2和3,将矩阵除对角线以外消为0:

1     0     0     k

0     1     0     a

0     0     1     b

解就是k,a,b。

步骤:

1.把当前对角线上的元素运用性质2变成1

2.用这个1去消掉除了当前对角线上的元素

重复这两个步骤n次(对角线长度)

注意:在对角线元素变为1的过程中,同一行的其他元素也要进行扩大/缩小相同的倍数。

如果1列上没有任何元素(全为0)说明这个未知数不存在,输出无解(No Solution)。


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,mo=100000007;
double a[1001][1001];
int main()
{    bool sc=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
            scanf("%lf",&a[i][j]);//这次没用祖传快读doge 
    int bj,flag=0;
    for(int i=1;i<=n;i++)
    {
        bj=i;
        while(a[bj][i]==0&&bj<=n)
            bj+=1; 
        if(bj==n+1){printf("No Solution");return 0;}
        //以上部分,我们做的是获取第一个此列中非0的行号。如果此列全为0,证明无解,直接退出、 
        for(int j=1;j<=n+1;j++)swap(a[i][j],a[bj][j]);
        //对角线上的元素不能为0,于是我们找到当前列号不为0的一行与对角线进行交换。可能当前对角线上元素也不为0.但是这样做囊括了更多情况。 
        double kk=a[i][i];//确保对角线上的数是1
        for(int j=1;j<=n+1;j++)
            a[i][j]/=kk;//其余数跟着处理,包括对角线上的数。运用性质2 
        for(int j=1;j<=n;j++)
        {
            if(i!=j)
            {
                double k=a[j][i];
    //k=这行第一个数,因为对角线上是1,那么这个“第一个数”可以表示为1*k。所以k也就是扩大/缩小的倍数 
                for(int m=1;m<=n+1;m++)
                    a[j][m]-=k*a[i][m];
    //此行所有的数减去第一个数*k,运用性质2 ,3 
            }      
        }
        if(i==n)//消完输出
        {
            for(int j=1;j<=n;j++)
                printf("%.2lf\n",a[j][n+1]);
    //只有对角线上有元素,且值为1,代表这个位置对应的列号未知数。第n+1列就代表着未知数的值。输出即可 
        }
    }
}

完结撒花。希望对各位的高斯消元学习有所帮助。

猜你喜欢

转载自www.cnblogs.com/lbssxz/p/13204930.html