线性方程组求解
初来乍到,多多指教!
问题:
线性方程组求解
输入是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;
}
设计过程遇到的问题及解决方案:
在设置的精度为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.继第二个问题的解决方案得出后,在设计过程中我又遇到了如何在设置变量时,使再次进行高斯消元时既能再次比较行数判断解的情况,又能不改变最初的未知量序号(因为重新定义行和列时,未知量的求解实质上是求消元后的系数值,若改变行和列,就会改变未知量与原系数的关系)
解决法案
在每次重新定义行和列后,再次求解时,对系数还原;
同时注意不要漏掉最后一个的还原
个人见解,不足之处多多体谅!