求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; }