线性方程组求解

初来乍到,多多指教!

问题:

线性方程组求解

输入是N(N<256)元线性方程组Ax=B,输出是方程组的解,也可能无解

或有多组解。可以用高斯消去法求解,也可以采用其它方法。

1.高斯消元:通过增广矩阵将系数与常数集中放在一起,利用增广矩阵性质

将矩形变为\形,再倒着依次求出相关变量值

2.矩形变\形:通过依次以1~n行为操作对象,将此行以下的行首系数与此行首系数比较,化同,做差即为消去系数少一个变量的方程,如此,最后形成的方阵便是\形

3.从后回代求解:在矩阵变\形时,对每行行首系数化为一,若此行为第i+1行,则第i行第i+1列未知量就是此行行首未知量,因为行首系数为1,则第i行方程减去第i+1行方程的a[i][i+1]倍(第i行第i+1列未知量的系数)即得到第i行行首未知量,如此循环得到所有变量。

4.解的判定:高斯消元后,若是标准的\形,有唯一解;若消元后的行数小于n行(元的个数)且没有出现0=常数(矛盾)的情况,则有多解,若出现0=常数的情况,则无解。

附上源代码

#include<iostream>

#include <cmath>

#include<iomanip>

using namespace std;

const int N = 260;

const double eps = 1e-6;//double精度

double a[N][N];

int n,i,j;

int work(int x)

{

//矩形变\形

//每循环一次重新定义一次,这样可以同时记录有几行,可以与原行数进行比较,进而判断解的情况

int c=0, r=0;//c表示在枚举哪一列,r表示在枚举哪一行

//高斯消元

for (; c < x; c++)

{

int maxr = r;

for (i = r; i < x; i++)//列不变,行变,找到对应系数绝对值最大的交换,减小误差

{

if (fabs(a[i][c]) > fabs(a[maxr][c]))//定位到当前列主元的最大系数所在行

maxr = i;

}

if (fabs(a[maxr][c]) < eps) continue;//是造成r<n无解的一个原因

for (i = c; i <= x; i++)swap(a[maxr][i], a[r][i]);//交换这两行

for (i = x; i >= c; i--)a[r][i] /= a[r][c];//这一行同除,行首系数化一 便于下边a[i][n]得出的就是Xi的值

for ( i = r + 1; i < x; i++)

{

if (fabs(a[i][c]) > eps)

{

for (j = x; j >= c; j--)

a[i][j] -= a[r][j] * a[i][c];//对第r行同扩大a[i][c]倍,与第i行相加减,消去列下面的系数

}

}

r++;

}

if (r==x)

{

for (i = x- 1; i >=0; i--)//向后回代求解

{

for (j = i + 1; j < x; j++)

{//+n-x 是恢复最初的行列计数,避免对应错未知量

a[i][x] -= a[i][j] * a[j][x];//之前已经把每行行首系数变为1

a[i + n - x][n] = a[i][x];

}

}

a[n - 1][n] = a[x - 1][x];//注意点!!!!!!!!!!!

}

if (r < x)

{

for ( i = r; i < x; i++)

{

if (fabs(a[i][x]) > eps)//即存在0=常数(不为零)的情况

return 2;//无解

}

//有无穷多组解

for (i = 0; i <x; i++)

for (int j = 0; j <x-r; j++)

{

a[i][j] = 0;//把这一项系数设为零

}

int r0=0, c0=0,j0=0;

for (j = 0; j <= x; j++)

if (fabs(a[0][j]) >= eps)

{

j0 = j;

break;

}//这是个注意点!!!!!!!!!

//重置矩阵

for (i = 0; i <x; i++)

for (j=j0; j <= x; j++)

{

if (j != x)

a[r0][c0++] = a[i][j];

else

{//换行

a[r0][r] = a[i][j];

r0++; c0 =0;//使等号对齐

}

}

for ( i = 0; i < x - r; i++)//这也是注意点!!!!位置

for ( j = 0; j < x - r; j++)

{

a[i+n-x][x+n-x] = 0;//把这一项自由变量设为零

}

work(r);//继续消元

return 1;//打印出来的是特解

}

return 0;//有唯一解

}

int main()

{

cin >> n;

for (i = 0; i < n; i++)

{

for (j = 0; j < n + 1; j++)

{

cin >> a[i][j];

}

}

int t = work(n);

if (t ==0)//有唯一解

{

for ( i = 0; i < n; i++)

cout <<"x"<<i+1<<"="<< fixed << setprecision(2) << a[i][n] << endl;

}

else if (t == 1)//有多解,打印特解

{

for (i = 0; i < n; i++)

cout << "x" << i + 1 << "=" << fixed << setprecision(2) << a[i][n] << endl;

}

else cout<<"-1";//无解

return 0;

}

设计过程遇到的问题及解决方案:

  1. 在设置的精度为1e-6下,我发现在解决有些线性方程组案例时,所得到的解总是与正确答案有些偏差

解决法案

搜索csdn发现此类问题主要是因为计算过程除法的使用(计算机除法会保留成小数),因此在程序中添加寻找所操作列系数绝对值最大值,将最大值的一行换到操作行。这样做的目的是在消元时尽量扩大倍数,用乘法。

2.

在最初的设计中

对于这种原来元个数(4)大于方程个数(3)的可以求出特解;

但对于下列这种原来元个数等于方程个数,通过

变换得到的多解情况,求不出来特解。

解决法案

我认为这两种情况的本质差别是:前者是矩形变为\形的特例,将\形截取了一部分,程序将最后几个未知量当作0(特解),从而求出前几个未知量;

后者通过高斯消元操作后,打乱了\形,无法求出未知量。

于是尝试将打乱的\形规整化,采取将前几个自由变量手动置零,再进行高斯消元化为\形,

代码如下:

//求特解,把前n-r个未知数全设为零,与上面方法相同,r变为r0,n(是行的临界时)变为r

//有无穷多组解

for (i = 0; i <x; i++)

for (int j = 0; j <x-r; j++)

{

a[i][j] = 0;//把这一项系数设为零

}

int r0=0, c0=0,j0=0;

for (j = 0; j <= x; j++)

if (fabs(a[0][j]) >= eps)

{

j0 = j;

break;

}//这是个注意点!!!!!!!!!

//重置矩阵

for (i = 0; i <x; i++)

for (j=j0; j <= x; j++)

{

if (j != x)

a[r0][c0++] = a[i][j];

else

{//换行

a[r0][r] = a[i][j];

r0++; c0 =0;//使等号对齐

}

}

for ( i = 0; i < x - r; i++)//这也是注意点!!!!位置

for ( j = 0; j < x - r; j++)

{

a[i+n-x][x+n-x] = 0;//把这一项自由变量设为零

}

work(r);//继续消元

return 1;//打印出来的是特解

}

3.继第二个问题的解决方案得出后,在设计过程中我又遇到了如何在设置变量时,使再次进行高斯消元时既能再次比较行数判断解的情况,又能不改变最初的未知量序号(因为重新定义行和列时,未知量的求解实质上是求消元后的系数值,若改变行和列,就会改变未知量与原系数的关系)

解决法案

在每次重新定义行和列后,再次求解时,对系数还原;

同时注意不要漏掉最后一个的还原

个人见解,不足之处多多体谅!