东南大学C程序课程设计报告概述.docx
《东南大学C程序课程设计报告概述.docx》由会员分享,可在线阅读,更多相关《东南大学C程序课程设计报告概述.docx(22页珍藏版)》请在冰豆网上搜索。
![东南大学C程序课程设计报告概述.docx](https://file1.bdocx.com/fileroot1/2022-11/23/1840509b-c7bf-4cf3-9129-c0302cc7c3d8/1840509b-c7bf-4cf3-9129-c0302cc7c3d81.gif)
东南大学C程序课程设计报告概述
姓名:
学号:
指导老师:
提交日期:
C++程序课程设计
迭代法求解线性方程(A级)
程序设计原理与功能简介…………………2
课程设计要求………………………………5
源程序分析…………………………………7
新程序设计思路……………………………10
新增模块源代码与功能介绍………………11
程序设计中遇到的问题与解决方法………14
程序运行测试结果与分析…………………16
程序缺陷与大致优化思路…………………20
设计心得与个人体会………………………20
1.程序设计原理与功能简介
迭代法是线性方程的一种常用解法,首先选取适当的初值,然后用同样的计算步骤重复计算,在满足精度的情况下,停止迭代,求得近似解。
★迭代法简介:
也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。
迭代法又分为精确迭代和近似迭代。
“二分法”和“牛顿迭代法”属于近似迭代法。
迭代算法是用计算机解决问题的一种基本方法。
它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
最常见的迭代法是牛顿法。
其他还包括最速下降法、共轭迭代法、变尺度迭代法、最小二乘法、线性规划、非线性规划、单纯型法、惩罚函数法、斜率投影法、遗传算法、模拟退火等等。
本程序所用的主要为赛德尔迭代法和简单迭代法,分别进行求解,然后将求解的结果参数列出进行比较。
还可以选择不同的松弛因子进行算法上的比较。
其中新增的部分主要是文本的输入与输出,仿照源程序编写简单迭代法的部分。
★赛德尔迭代法:
设有线性方程:
首先将系数矩阵变换成:
形式。
设上述线性方程组写成:
(1)
系数矩阵A可逆且主对角元素
均不为零,令
并将A分解成
从而
(1)可写成
令:
其中M=
,
即:
其中:
M=
这就是原程序所变换的矩阵。
赛德尔迭代法的不同之处在于:
每算出一个分量的近似值,立即用到下一个分量的计算中去,即用迭代公式:
即:
选用松弛因子
代入上述线性方程得到松弛的赛德尔公式:
通常在(0,2)内选取,当
>1,成为超松弛,当
<1时,成为亚松弛。
松弛因子的恰当选择,可以大大提高收敛速度,(经查资料,可以证明利用赛德尔法计算方程,收敛则方程组有解,且收敛充分条件有多个,在此不一一例举),在实际计算中,往往要靠经验找到最优松弛因子。
★一般迭代法:
由赛德尔迭代法与一般迭代法比较可知,在迭代的每一步计算过程中一般迭代法是用
的全部分量来计算
的所有分量,显然在计算第i个分量
时,已经计算出的最新分量
没有被利用,从直观上看,最新计算出的分量可能比旧的分量要好些.因此,对这些最新计算出来的第
次近似
的分量
加以利用,就得到所谓解方程组的高斯—塞德(Gauss-Seidel)迭代法.
下面就用程序来验证这一点。
二.课程设计要求
1.重载类的构造函数,可以在定义类的对象时利用函数参数输入方程的各项参数,便于程序调试。
2.增加类的成员函数,可以将一个线性方程用文本文件的编辑器(如记事本)写入一个文件中,然后由新增的成员函数中读取,运行结束后写回文件中。
3.原程序是高斯-赛德尔算法解线性方程,仿照源程序编写简单迭代法求解线性方程的程序,并利用相同方程组与塞德尔算法进行验证比较,分别求出他们的收敛次数。
4.设计并对主程序进行测试:
本次测试所选线性方程组为:
★一般迭代法的大致运算:
0
1
2
3
4
5
6
0
0.7
1.42
1.783
2.0338
2.1823
……
0
0.8
1.49
1.862
2.1101
2.2594
……
0
2.4
3.18
3.876
4.2462
4.4948
……
★赛德尔迭代法大致运算:
0
1
2
3
4
5
6
0
0.7
1.6726
1.9337
2.1416
2.3271
……
0
1.01
1.9714
1.9224
2.3074
2.4330
……
0
3.348
2.7116
4.3247
4.6743
4.8118
……
3.源程序分析
源程序首先定义了一个类。
在这个类中,先定义了两个友元函数,分别重载输出与输入运算符。
后又定义了一些对象,包括矩阵行列数,方程解的个数,存放方程的解,迭代次数,松弛因子。
也定义了几个成员函数,包括误差函数,构造函数,输入行列数,变换矩阵函数,主要算法部分,输出最后结果的函数。
构造函数的作用是为行列数分配内存空间,是动态生成的。
输入行列数要求用户自己输入,来初始化行列数。
变换矩阵要求将原矩阵作出的变换是:
实对角线上的系数为0,每一行的其他数都是原数除以原每一行第一个数的相反数。
然后是进行一轮迭代,参数是松弛因子,求出最新的迭代解,每求出的一个新解都立即用到下一个分量的计算中去,继续迭代,直到满足解的精度才停止迭代,最后输出函数结果。
★源程序代码:
源程序头文件部分
#include
#include
#include
#include
(源程序主要由类和类的声明构成)
classMatrix//高斯-赛德尔矩阵乘法类
{
friendvoidoperator<<(ostream&,Matrix&);//重载输出运算符
friendvoidoperator>>(istream&,Matrix&);//重载输入运算符
protected:
introw,column;//矩阵行列数
double**mat;//矩阵
intvarnum;//方程解的个数
double*variable;//存放方程解
intitercount;//迭代次数
voiditeration(double);//迭代算法,参数是松弛因子
boolepsilon(double*,double*,int,double);//误差函数
public:
Matrix(int,int);//构造函数
staticvoidinitialize(int&,int&);//要求用户输入行列数
voidrearrange();//变换矩阵
voidsolve(double);//主要算法部分,参数是松弛因子
voidshow_answer();//输出最后结果
};
(动态生成方程系数的数组)
Matrix:
:
Matrix(intr=1,intc=1):
row(r),column(c)
{mat=newdouble*[row];//动态生成
for(inti=0;i{mat[i]=newdouble[column];}
}
(输入行列数,确定线性方程的大小)
voidMatrix:
:
initialize(int&i,int&j)/*初始化,输入行列(ROWS:
线性方程数,COLUNMNS:
系数)
及常数的个数*/
{cout<<"HowmanyROWS?
>";
cin>>i;
cout<<"HowmanyCOLUMNS?
>";
cin>>j;
}
(将矩阵转化为求解形式)
voidMatrix:
:
rearrange()//高斯-赛德尔矩阵变换函数,在进行迭代之前需将原矩阵做变换
{varnum=column-1;//方程解的个数
variable=newdouble[varnum];//存放方程解
for(inti=0;i{doublecoefficient=mat[i][i];//对角线上的系数
for(intj=0;jmat[i][j]/=coefficient;//实对角线上的系数为1
}
for(i=0;i{for(intj=0;jmat[i][j]*=-1;//变换
mat[i][i]=0;//对角线上的系数为0
}
}
(利用松弛因子进行迭代求解)
voidMatrix:
:
iteration(doublelambda)//进行一轮迭代,参数为松弛因子
{doublelast;//为最新求出的迭代解
for(inti=0;i{last=variable[i];
variable[i]=0;//最新的方程解,i为行次,求xi
for(intj=0;jvariable[i]+=mat[i][j]*variable[j];/*根据已解出的x0,x1,x2,……x(i-1)的最新值
求xi*/
variable[i]+=mat[i][column-1];//加上常数项系数
variable[i]=last+lambda*(variable[i]-last);//利用松弛因子调整收敛速度
}
}
(明示精度要求,进行新解和旧解的转换和保存,如不满足要求返回上一级继续迭代)
voidMatrix:
:
solve(doublelambda)//求解方程
{for(inti=0;i{variable[i]=0;}
itercount=0;
doublecriterion=0.0001;//迭代误差
double*newest=newdouble[varnum];
double*last=newdouble[varnum];
for(i=0;i{newest[i]=variable[i];}
do
{for(inti=0;ilast[i]=newest[i];//保存前一轮的解,计算误差用
iteration(lambda);//一轮迭代
for(i=0;inewest[i]=variable[i];//保存新一轮的解
itercount++;//整个迭代次数加1
}
while(epsilon(newest,last,varnum,criterion));//计算误差
}//计算迭代误差,看是否满足解得精度,返回0停止迭代,给出最后结果
(精度验证,不满足则继续迭代)
boolMatrix:
:
epsilon(double*newest,double*last,intsize,doublecriterion)
{for(inti=0;i{if((fabs(newest[i]-last[i])/newest[i])>criterion)//如果不满足精度
return1;//继续迭代
}
return0;
}
(输出求解结果)
voidMatrix:
:
show_answer()//输出求解结果函数
{for(inti=0;i{cout<<"X"<<(i+1)<<"="<cout<}
voidoperator<<(ostream&out,Matrix&m)//矩阵输入,输出流重载函数
{for(inti=0;i{for(intj=0;j{out<out<}
}
voidoperator>>(istream&in,Matrix&m)
{for(inti=0;i{for(intj=0;j{cout<<"valueofindice["<<(i+1)<<"]["<<(j+1)<<"]";
in>>m.mat[i][j];
}
}
}
主函数的部分
voidmain()
{cout<<"ThisProgramSolvelinearbytheGauss-SeigelMethoa"<inti,j;
Matrix:
:
initialize(i,j);
Matrixone(i,j);
cin>>one;
charX;
cout<<"DoyouwanttoseetheMatrix?
:
(y/n)>";
cin>>X;
if(X=='y')
{cout<one.rearrange();
doublerelax_coaf;
for(charx='y';x=='y'&&x!
='n';)
{cout<>";
cin>>relax_coaf;
one.solve(relax_coaf);
one.show_answer();
cout<:
(y/n)>";
cin>>x;
}
cin.ignore(128,'\n');
cin.ignore(128,'\n');
}
4.新程序设计思路
由于源程序的内容比较详细全面,所以新程序的内容不多,只需在原类中添加两个成员函数,还有将主函数适当修改一下即可。
添加的两个类的成员函数的功能分别是读取文本文件中内容和将结果保存在文本文件中。
由于文本文件中是线性方程的初始形式,即a11*x+a12*x+a13*x=b的形式。
所以要将它们读取出来是比较困难的事情。
这也是这个程序的难点之一。
首先应将它们以字符的形式读取出来,然后将前面的系数识别出来,送入到原类的矩阵中去,进行矩阵变换。
然后将运行结果存入到另一个文本文件中去。
这将采用文件的输入与输出功能来解决这个问题。
至于主函数的修改,也是一个重点。
主函数将采取菜单的模式,让用户选择输入方程组的方式,同一个方程组用两种方法进行计算求解,即定义两个对象(赛德尔迭代法和简单迭代法),分别进行求解,然后将求解的结果参数列出进行比较。
如果希望作进一步研究,还可以选择不同的松弛因子进行算法上的比较,得出一个比较完善的结论。
5.新增模块源代码与功能介绍
新增的模块主要有两个:
一个模块的功能是读取一个文本文件。
除了读取外,还要将线性方程未知数前面的系数识别出来,然后送入原类的矩阵中去,进行矩阵变换。
另一个模块的功能是将运行的结果存入这个文本文件里去。
这两个模块如下:
★算法类中添加两个函数:
voidRead(int,int);//新增函数:
读取文件中方程组的系数
voidWrite();//新增函数:
将计算结果写入文件中
★两个函数具体函数体内容:
●读取文件内容函数
voidmatrix:
:
Read(intr,intc)
{
ifstreaminfile;
infile.open("d:
\\线性方程组.txt");//读取文件
chara;
for(inth=0;h{
for(intg=0;g{
mat[h][g]=0;
}
}
chartemple[1000];//利用中间变量剔除某些字符
intt=0;
inti=0,j,k=0;
while(infile.get(a))//读取方程组系数矩阵
{
temple[t++]=a;
if(a>=48&&a<=57&&temple[t-2]!
=120)//当字符是数字且前一个不是“x”时
{
mat[k][i]*=10;
j=a-48;
mat[k][i]+=j;
if(temple[t-2]==45)mat[k][i]=-mat[k][i];//读取负数
}
else
{
if(a>=64)i++;
elseif(a=='\n')
{
k++;
i=0;
}
}
}
infile.close();
}
●写入文件内容函数
voidmatrix:
:
Write()
{
ofstreamoutfile;
outfile.open("d:
\\赛德尔迭代法的解.txt");
for(inti=0;i{
outfile<<"x"<<(i+1)<<"="<}
outfile.close();
}
★多项选择性的设计
虽然程序中采用的是从文件中提取线性方程组,即算法中新增的read读入函数,但本程序还设计了另外的方程组系数输入方法,即人工按方程组系数矩阵的排列逐个输入。
源代码即在重载输入运算符时添加了选择语句,由用户选择输入方式,源代码如下:
voidoperator>>(istream&in,matrix&m)
{
cout<<"请选择输入线性方程组系数的方式:
"<cout<<"1、从D盘文本文档中提取系数(注意:
文档中的系数为1或-1时要添上)"<cout<<"2、手动输入系数(注意:
按照系数矩阵的顺序输入)"<intchoice;
cin>>choice;
switch(choice)
{
case1:
{
for(inti=0;i{
for(intj=0;j{
cout<<"valueofindice["<<(i+1)<<"]["<<(j+1)<<"]"<}
}
break;
}
case2:
{
for(inti=0;i{
for(intj=0;j{
cout<<"valueofindice["<<(i+1)<<"]["<<(j+1)<<"]";
in>>m.mat[i][j];
}
}
break;
}
}
}
6.程序设计中遇到的问题与解决方法
1).最大的困难当属对于如何从文件中将线性方程组的系数与常数读入程序中,这个方面主要是从文件中如何读取字符串,并从字符串中将系数和常数读取出来,然后将字符型转化成数字。
从上面的读取文件函数设计中已经知道所用的方法,这主要是查找资料所得:
★参考程序[1]:
#include
#include
usingnamespacestd;
intmain(){
ofstreamofile;
ofile.open("d:
\\my.doc");
ofile<<"3x+8y=11"<<'\n'<<"2x+3y=5"<ofile.close();
ifstreamifile;
ifile.open("d:
\\my.doc");
chara;
ints[100][100];
for(inth=0;h<100;h++)for(intg=0;g<100;g++)s[h][g]=0;
inti=0,j,k=0;
while(ifile.get(a)){
if(a>=48&&a<=57){
s[k][i]*=10;
j=a-48;
s[k][i]+=j;
}
elseif(a>=64)i++;
elseif(a=='\n')k++;
}
cout<
ifile.close();
return0;
}
★参考程序[2]:
#include
#include
voidmain(void)
{
FILE*fp=fopen("D:
\\1.txt","r");
intnChar;
inti=0;
charbuf[64];
floata,b;
intn;
while((nChar=fgetc(fp))!
=EOF)//EOF=endoffile
{
buf[i++]=(char)nChar;
if(nChar=='\n')
{
buf[i]=0;
i=0;
sscanf(buf,"第%d点坐标:
x=%fy=%f",&n,&a,&b);
printf("%f\t%f\n",a,b);
}
}
fclose(fp);
}
但第一段程序只针对于正整数,对于负数和系数为1时没有具体的设计,因此有较大的局限性,本想通过第二段程序中的方式将数字读入源程序的,但运行效果不佳,且有许多陌生的程序代码,如FIFE类型的数据处理等,但经过参考此段代码,结合学过的C++内容,经过个人修改,在源文件中的添加了一些条件,使得程序运行环境更完善,运行效果更好。
2).在不断完善程序的过程中,还遇到许多小问题,比如还是线性方程组系数的写入问题,小数问题还没有解决,即如何从文本中将小数的系数或常数读入程序中,于是,在此,提供了两种写入系数的方式,一种即上述的文件写入,另一种即按照系数矩阵的顺序输入系数与常数,这样,只要采用后一种方法,小数的问题也可以解决了。
3).在程序不断运行和调试中,还遇到一些小问题,但都在调试中解决了,主要是语法错误。
7.程序运行测试结果和分析
程序中主要的示例线性方程为:
运行结果:
1.D盘文件中输入线性方程组:
2.赛德尔迭代法:
选择赛德尔迭代法,并输入系数矩阵的行数与列数,而后从文本文档中提取系数:
将所有系数以矩阵形式展示出来,这样便于使用者与已知的线性方程组的系数和常数进行比较,并第一次输入松弛因子
大小为1.1,进行运算,由下示运行结果可知迭代共进行了8次:
由于松弛因子的选择具有不确定性,因此在此选用其他松弛
|
|
|