[关闭]
@xiaoziyao 2020-04-25T16:02:09.000000Z 字数 3171 阅读 1599

高斯消元& 线性基学习笔记

数学 学习笔记


前置知识

矩阵(不看也行,不是很需要)

定义

我们要解有n个未知数的n个不成倍数的线性方程组,如下式:


可以写为如下的矩阵:

然后我们除去未知数,将矩阵改为一个增广矩阵:

我们发现,某些操作可以让矩阵变为另一个等价的矩阵,我们将这些操作称为初等行(列)变换:
1. 交换两行
2. 把某一行所有的数字乘k
3. 把某一行的元素对应加到另一行的元素上
我们的目标是让增广矩阵改写为阶梯矩阵,使第i行只有n-i+2个数字(如下图):

改写为阶梯矩阵之后,我们可以让阶梯矩阵改写为:

然后我们就可以求出第n个未知数,然后代入第n-1个方程,然后求出第n-1个未知数,自下而上把阶梯矩阵改写为单位矩阵:

这样,我们就求出了所有的未知数
总结一下,我们的操作为:方程组->增广矩阵->(初等行(列)变换)->阶梯矩阵->单位矩阵
我们使用的一般都为高斯-约旦消元法,在这个算法中,每次消元都选出了系数最大的一项,这样可以有效地减少误差(我也不是很清楚,不会证明)(文后有代码)
但是不一定所有的方程组有解,因此高斯消元之后有三种结果:
1. 如果矩阵的非0行数小于n,或者出现一行为[0,0,0,0,...,0|0]的情况有无穷多解
2. 如果矩阵的非0行数大于n,或者出现一行为[0,0,0,0,...,0|x]且x非零,无解
3. 否则方程组只有一组唯一解
当方程组有无穷解时,我们可以将某一些未知数看为常数,然后用这些未知数来表示其他的未知数,其中这些被看为常数的未知数称为自由元,可以被表示出来的未知数称为主元。

  1. #include<stdio.h>
  2. const int maxn=105;
  3. const double eps=1e-20;//极限小值,用来处理误差
  4. int i,j,k,m,n,flg;
  5. double a[maxn][maxn];
  6. inline double fabs(double x){//double的绝对值
  7. return x<0? -x:x;
  8. }
  9. inline void swp(double &a,double &b){//手写swap
  10. a+=b,b=a-b,a-=b;
  11. }
  12. void gauss_jordan(){
  13. flg=0;
  14. for(int i=1;i<=n;i++){//枚举每一个式子
  15. int k=i;
  16. double tmp=fabs(a[i][i]);
  17. for(int j=i+1;j<=n;j++)
  18. if(tmp<fabs(a[k][i]))
  19. tmp=fabs(a[k][i]),k=j;
  20. //有点像选择排序,每一次选择都求出了当前项最大系数 ,减少误差
  21. if(tmp<eps){//系数为0,无解或无穷多解
  22. flg=1;
  23. return ;
  24. }
  25. if(i!=k)//交换两行
  26. for(int j=1;j<=n+1;j++)
  27. swp(a[i][j],a[k][j]);
  28. for(int j=1;j<=n;j++)//将除第i个之外所有式子的系数处理一遍
  29. if(i!=j){//不能自己减自己
  30. tmp=a[j][i]/a[i][i];
  31. for(k=i+1;k<=n+1;k++)
  32. a[j][k]-=a[i][k]*tmp;
  33. }
  34. }
  35. }
  36. int main(){
  37. scanf("%d",&n);
  38. for(i=1;i<=n;i++)
  39. for(j=1;j<=n+1;j++)
  40. scanf("%lf",&a[i][j]);
  41. gauss_jordan();//高斯-约旦算法
  42. if(flg){//无解
  43. puts("No Solution");
  44. return 0;
  45. }
  46. for(i=1;i<=n;i++)
  47. printf("%.2lf\n",a[i][n+1]/a[i][i]);//当前项的解为右边的项除以系数
  48. return 0;
  49. }

题单

洛谷

POJ

HDU

BZOJ

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注