C++实验报告高斯消元法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
高斯肖元法C++上机实验报告
学生姓名: 学 号: 专业班级: 实验类型: 综合
一 实验项目名称
全选主元高斯消去法解线性方程组 二 实验原理
设有n 元线性方程组(考虑便于C++程序数组表示,方程的下标从0开始),
0000110,1100000110,11110
1,111,111
n n n n n n n n n n a x a x a x b a x a x a x b a x a x a x b ---------+++=⎧⎪
+++=⎪⎨⎪⎪+++=⎩
写为矩阵形式为Ax=b,其中A 为线性方程组的系数矩阵,x 为列向量,是方程组的解,b 也
是列向量.
一般来讲,可以假定矩阵A 是非奇异阵。
(n 阶矩阵A 的行列式不为零,即 |A|≠0,则称A 为非奇异矩阵)
00010,10111,1,0
1,1
1,1n n n n n n a a a a a a A a a a ----⎡⎤⎢⎥⎢⎥=
⎢⎥⎢
⎥⎣⎦
,
011n x x x x -⎡⎤⎢⎥⎢⎥=⎢⎥⎢⎥⎣⎦ ,01
1n b b b b -⎡⎤
⎢⎥⎢
⎥=⎢⎥⎢⎥
⎣⎦
将系数矩阵A 和向量b 放在一起,形成增广矩阵B :
00
010,010
111,1
1,0
1,1
1,1
1(,)n n n n n n n a a a b a a a b b A b a a a b -----⎡⎤⎢⎥⎢
⎥==⎢⎥⎢
⎥⎣⎦
全选主元消去就在矩阵B 上进行,整个过程分为如下两个步骤: 第一步:消去过程。
对于k 从0开始到n-2结束,进行以下三步。
1. 首先,从系数矩阵A 的k 行k 列开始的子矩阵中选取绝对值最大的元素作为主元素。
例如:
11,max 0
i j ij k i n k j n
a a ≤<≤<=≠
然后交换B的第k行与第
1
i行,第k列与第1k列,这样,这个子矩阵中具有最大绝对值的元素被交换到k行k列的位置上.
2.其次,进行归一化计算。
计算方法为:
/,1,,1
/
kj kj kk
k k kk
a a a j k n
b b a
==+-⎧⎪
⎨
=
⎪⎩
3.最后进行消去计算:
,,1,,1
,1,,1 ij ij ik kj
i i ik k
a a a a j i k n
b b a b i k n
=-=+-⎧⎪
⎨
=-=+-
⎪⎩
第二步,回带过程:
111,1
1
1
/
,2,,1,0 n n n n
n
i i ij j
j i
x b a
x b a x i n
----
-
=+
=
⎧
⎪
⎨
=-=-
⎪
⎩
∑
三代码的实现
整个程序分为5个独立文件,Matrix.h文件中包括矩阵类Matrix的定义,Matrix.cpp文件中包括该类成员函数的实现,LinearEqu.h文件中包括线性方程组类LinearEqu的定义,LinearEqu.cpp文件中包括该类的成员函数实现文件;7-9.cpp文件包括程序的主函数,主函数中定义了一个类LinearEqu的对象,通过这个对象求解一个四元线性方程组。
//Matrix.h文件一,定义一个CMatrix类
#ifndef _MATRIX_H
#define _MATRIX_H
class CMatrix //定义CMatrix基类
{
public: //外部接口
CMatrix(int size=2); //构造函数
~CMatrix(); //析构函数
void setMatrix(const double*values); //矩阵赋初值
void printMatrix() const; //显示矩阵
int getsize() const{return size;} //得到矩阵大小
double &element(int i,int j){return elements[i*size+j];}
double element(int i,int j)const{return elements[i*size+j];}
private: //保护数据成员
int size; //定义矩阵的大小
double*elements; //矩阵存放数组首地址
};
#endif //_MATRIX_H
//LinearEqu.h 文件二,CLinearEqu类定义
#ifndef _LINEAR_EQU_H
#define _LINEAR_EQU_H
#include"Matrix.h"
class CLinearEqu:public CMatrix //公有派生类CLinearEqu定义
{
public: //外部接口
CLinearEqu (int size=2); //构造函数
~CLinearEqu(); //析构函数
void setLinearEqu(const double*a,const double*b); //方程赋值
bool solve(); //全选住院高斯消去法求解方程
void printLinearEqu()const; //显示方程
void printSolution()const; //显示方程的解
private: //私有数据
double*sums; //方程右端项
double*solution; //方程的解
};
#endif //_LINEAR_EQU_H
经过公有派生,LinearEqu类获得了除构造函数、析构函数之外的Matrix类的全部成员。
由于基类的成员是公有和保护类型,因此在派生类中的成员函数中,基类继承来的成员全部可以访问,而对于建立LinearEqu类对象的外部模块来讲,基类的保护成员是无法访问的。
通过保护访问类型和公有的基方式,就实现了基类Matrix的数据的有效共享和可靠保护。
在程序中,方程的系数矩阵、解以及右端项全部采用动态内存分配技术,这些工作都是在基类、派生类的构造函数中完成,它们的析构函数中完成。
//Matrix.cpp 文件三,CMatrix类实现
#include"Matrix.h" //包含类的定义头文件
#include<iostream>
using namespace std;
void CMatrix::setMatrix(const double*values) //设置矩阵
{
for(int i=0;i<size*size;i++)
elements[i]=values[i]; //矩阵成员赋初值
}
CMatrix::CMatrix(int size/*=2*/):size(size) //矩阵CMatrix类的构造函数
{
elements=new double[size*size];
}
CMatrix::~CMatrix() //矩阵Matrix类的析构函数
{
delete[]elements; //释放空间
}
void CMatrix::printMatrix()const //显示矩阵的元素
{
cout<<"The Matrix is:"<<endl;
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
cout<<element(i,j)<<" ";
cout<<endl;
}
}
//LinearEqu.cpp 文件四,CLinearEqu类实现
#include"LinearEqu.h" //包含类的定义头文件
#include<iostream>
#include<cmath>
using namespace std;
CLinearEqu::CLinearEqu(int size/*=2*/):CMatrix(size) //用size调用基类构造函数{
sums=new double[size]; //动态内存分配
solution=new double[size];
}
CLinearEqu::~CLinearEqu() //派生类CLinearEqu的析构函数{
delete[] sums; //释放内存
delete[] solution; //会自动调用基类析构函数
}
void CLinearEqu::setLinearEqu(const double*a,const double*b) //设置线性方程组{
setMatrix(a); //调用基类函数
for(int i=0;i<getsize();i++)
sums[i]=b[i];
}
void CLinearEqu::printLinearEqu()const //显示线性方程组
{
cout<<"The Line eqution is:"<<endl;
for(int i=0;i<getsize();i++)
{
for(int j=0;j<getsize();j++)
cout<<element(i,j)<<" ";
cout<<" "<<sums[i]<<endl;
}
}
void CLinearEqu::printSolution()const //输出方程组的解
{
cout<<"The Result is:"<<endl;
for(int i=0;i<getsize();i++)
cout<<"x["<<i<<"]="<<solution[i]<<endl;
}
inline void swap(double &v1,double &v2) //交换两个实数
{
double temp=v1;
v1=v2;
v2=temp;
}
bool CLinearEqu::solve() //全选主元高斯消去法求解方程
{
int *js=new int[getsize()]; //存储主元素所在列号的数组
for(int k=0;k<getsize()-1;k++) //选主元素
{
int is; //主元素所在行号
double max=0; //所有元素的最大值
for(int i=k;i<getsize();i++)
for(int j=k;j<getsize();j++)
{
double t=fabs(element(i,j));
if(t>max)
{
max=t;
js[k]=j;
is=i;
}
}
if(max==0)
{
delete []js;
return false;
}
else //通过行列交换,把主元素交换到第K行第K列
{
if(js[k]!=k)
for(int i=0;i<getsize();i++)
swap(element(i,k),element(i,js[k]));
if(is!=k)
{
for(int j=k;j<getsize();j++)
swap(element(k,j),element(is,j));
swap(sums[k],sums[is]);
}
} //消去过程
double major=element(k,k);
for(int j=k+1;j<getsize();j++)
element(k,j)/=major;
sums[k]/=major;
for(int i=k+1;i<getsize();i++)
{
for(int j=k+1;j<getsize();j++)
element(i,j)-=element(i,k)*element(k,j);
sums[i]-=element(i,k)*sums[k];
}
} //判断剩下的一个元素是否等于零
double d=element(getsize()-1,getsize()-1);
if(fabs(d)<1e-15)
{
delete[] js;
return false;
} //回带过程
solution[getsize()-1]=sums[getsize()-1]/d;
for(int i=getsize()-2;i>=0;i--)
{
double t=0.0;
for(int j=i+1;j<=getsize()-1;j++)
t+=element(i,j)*solution[j];
solution[i]=sums[i]-t;
}
js[getsize()-1]=getsize()-1;
for(int k=getsize()-1;k>=0;k--)
if(js[k]!=k) swap(solution[k],solution[js[k]]);
delete[]js;
return true;
}
在类的成员函数实现过程中,派生类的构造函数使用参数调用了基类的构造函数,为矩阵动态分配了内存空间。
而派生类的析构函数同样也调用了基类的析构函数,只是整个调用过程完全由系统内部完成。
基类的保护数据成员的身份出现,派生类的成员函数可以自由的进行访问。
全选主元高斯消去法求解函数返回值为整型,正常完成之后,返回值为1,非正常结束后,返回值为0,根据函数的返回值,就可以判断求解过程的完成情况
//7-9.cpp 文件五,主函数
#include"LinearEqu.h" //类定义头文件
#include<iostream>
using namespace std;
int main() //主函数
{
double a[]= //方程系数矩阵
{
0.2368,0.2471,0.2568,1.2671,
0.1968,0.2071,1.2168,0.2271,
0.1581,1.1675,0.1768,0.1871,
1.1161,0.1254,0.1397,0.1490
};
double b[]={1.8471,1.7471,1.6471,1.5471}; //方程右端项
CLinearEqu equ(4); //定义一个四元方程组对象
equ.setLinearEqu(a,b); //设置方程组
equ.printLinearEqu(); //通过调用输出方程组
if(equ.solve()) //求解方程组
equ.printSolution(); //输出方程组的解
else
cout<<"fail"<<endl;
return 0;
}
在程序的主函数部分,选择了一个四元方程组来作为一个实际例子验证算法。
方程组的系数及右端项数据都使用一维数组来存储。
首先定义一个
运行结果如下:
四实际应用
许多实际问题经常最后归结为求解线性方程组。
例如:用最小二乘法处理测量结果时,和用网格法逼近微分方程的边值问题时,都会得到线性方程组。
这些方程组中的未知量往往是几十个,甚至上百个,如果要解含n个未知量的线性方程时,就得计算出n+1个n阶行列式,而计算每一个n阶行列式就需做n!次乘法,一般来说,即使借助电子计算机,也需要数月乃至数年的时间,所以在解这些含有未知量的线性方程组时,不仅要借助于电子计算机,同时还必须寻求恰当的计算方法。
而高斯全选主元消去法是一
种效率很高、较为常用的线性方程组解法。
以下以电阻网络
电阻网络实例
简单电网中的电流可以利用线性方程组来描述。
当电流经过
电阻(如灯泡或发电机等)时,会产生“电压降”。
根据欧姆定律,U I R = 其中U 为电阻两端的“电压降”,I 为流经电阻的电流强度,R 为电阻值,单位分别为伏特、安培和欧姆。
对于电路网络,任何一个闭合回路的电流服从希尔霍夫电压定律:沿某个方向环绕回路一周的所有电压降U 的代数和等于沿同一方向环绕该回路一周的电源电压的代数和。
【解】在回路1中,电流1I 流经三个电阻,其电压降为:
1111I 7I 4I 12I ++=
回路2中的电流2I 也流经回路1的一部分,即从A 到B 的分支,对应的电压降为24I ;同样,回路3中的电流3I 也流经回路1的一部分,即从B 到C 的分支,对应的电压降为37I 。
然而,回路1中的电流在A B 段的方向与回路2中选定的方向相反,回路1中的电流在B C 段的方向与回路3中选定的方向相反,因此回路1 所有电压降的代数和为
12312I 4I 7I --。
由于回路
1中电源电压为40V ,由希尔霍夫定律可得
回路1的方程为12312I 4I 7I 40--=。
回路2的电路方程为 1244I 13I 5I 10-+-=-; 回路3的电路方程为 1347I 15I 6I 30-+-=; 回路4的电路方程为 2345I 6I 14I 20--+=; 于是,回路电流所满足的线性方程组为
---------=⎧⎪+=⎪⎨
+=⎪+=⎪⎩12312413423412I 4I 7I 404I 13I 5I 107I 15I 6I 305I 6I 14I 20
故
12
47041305701560
5
6
14A --⎡⎤⎢⎥--⎢⎥=
⎢⎥--⎢⎥--⎣⎦ 40103020b ⎡⎤⎢⎥-⎢⎥=⎢⎥⎢⎥⎣⎦
结果如下:
五心得体会
这是一个使用类的派生层次结构来解决实际问题的例子。
基类是专门处理矩阵的类,公有派生类LinearEqu是针对线性方程组而设计的,除了继承基类的基本特征之外,结合问题的实际需要,增加了很多线性方程组所特有的成员,使基类Matrix进一步具体化、特殊化,达到对问题的有效描述和处理。
程序的访问控制也是根据问题的需要设计的。
基类的数据成员存储、维护着矩阵数据,这正是派生类方程组的系数矩阵,是派生类解方程成员函数必须要访问的。
在派生过程中,基类的构造函数和析构函数无法继承下来,因此在派生类中要添加构造函数、析构函数来完成派生类的初始化和最后的清理工作。
派生类析构函数也会首先调用执行基类的析构函数,共同完成清理任务。