C[i][j]=C[i][j]+A[i][k]*B[k][j];
printf("%4d",C[i][j]);
}
printf("\n");
}
}
六.结果展示
程序1和2的运行结果分别如图1-1和1-2所示:
图1-1图1-2
七.实验讨论与总结
1.本实验考察了对数据文件的建立、读入和试用,是一个突破;
2.第一个矩阵相乘中出现小数,故应用float数据类型;第二个矩阵中全是整数,故用int型即可。
3.对于输出结果的排版需要格外注意,应灵活使用输出格式符使其美观完整。
4.文件操作的基本函数(打开、关闭、输入和输出)
①fopen(打开文件)
相关函数open,fclose
表头文件#include
定义函数FILE*fopen(constchar*path,constchar*mode);
函数说明参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
②fclose(关闭文件)
相关函数close,fflush,fopen,setbuf
表头文件#include
定义函数intfclose(FILE*stream);
函数说明fclose()用来关闭先前fopen()打开的文件。
此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。
返回值若关文件动作成功则返回0,有错误发生时则返回EOF并把错误代码存到errno。
错误代码EBADF表示参数stream非已打开的文件。
③fscanf(输入函数)
功能:
从一个流中执行格式化输入
表头文件:
#include
函数原型:
intfscanf(FILE*stream,char*format[,argument...]);
FILE*一个FILE型的指针
char*格式化输出函数,和scanf里的格式一样
返回值:
成功时返回转换的字节数,失败时返回一个负数
fp=fopen("/local/test.c","a+");
fscanf(fp,"%s",str);
④fprintf
功能:
传送格式化输出到一个文件中
表头文件:
#include
函数原型:
intfprintf(FILE*stream,char*format[,argument,...]);
FILE*一个FILE型的指针
char*格式化输入函数,和printf里的格式一样
返回值:
成功时返回转换的字节数,失败时返回一个负数
fp=fopen("/local/test.c","a+");
fprintf(fp,"%s\n",str)
实验二非线性方程求根
1.问题提出
二.要求
1.编制一个程序进行运算,最后打印出每种迭代格式的敛散情况;
2.用事后误差估计|xk+1-xk|<ε来控制迭代次数,并且打印出迭代的次数;
3.初始值的选取对迭代收敛有何影响;
4.分析迭代收敛和发散的原因。
三.实验目的和意义
1.通过实验进一步了解方程求根的算法;
2.认识选择计算格式的重要性;
3.掌握迭代算法和精度控制;
4.明确迭代收敛性与初值选取的关系。
四.计算公式
对于给定的线性方程组x=Bx+f(这里的xBf同为矩阵,任意线性方程组都可以变换成此形式),用公式x(k+1)=Bx(k)+f(括号中为上标,代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法)。
如果k趋向无穷大时limx(k)存在,记为x*,称此迭代法收敛。
显然x*就是此方程组的解,否则称为迭代法发散。
五.结构程序设计
各函数及其导数建立如图2-1和图2-2所示:
图2-1图2-2
主函数如下:
voidmain()
{
doublei,j;
doubleeps=1e-10;
intk;
if(fabs(f11
(1))>1||fabs(f11
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f1
(1);j=i;i=f1(j);
while(fabs(i-j)>eps)
{
j=i;
i=f1(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
if(fabs(f22
(1))>1||fabs(f22
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f2
(1);j=i;i=f2(j);
while(fabs(i-j)>eps)
{
j=i;
i=f2(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
if(fabs(f33
(1))>1||fabs(f33
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f3
(1);j=i;i=f3(j);
while(fabs(i-j)>eps)
{
j=i;
i=f3(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
if(fabs(f44
(1))>1||fabs(f44
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f4
(1);j=i;i=f4(j);
while(fabs(i-j)>eps)
{
j=i;
i=f4(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
if(fabs(f55
(1))>1||fabs(f55
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f5
(1);j=i;i=f5(j);
while(fabs(i-j)>eps)
{
j=i;
i=f5(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
if(fabs(f66
(1))>1||fabs(f66
(2))>1)
printf("不满足收敛条件\n");
else
{
k=0;
i=f6
(1);j=i;i=f6(j);
while(fabs(i-j)>eps)
{
j=i;
i=f6(j);k++;
}
printf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
}
五.结果展示
程序运行结果如图2-3所示:
图2-3
六.实验讨论与总结
1.完成本实验相当于实现了将迭代算法转化成程序语言这一突破;
2.本实验考察众多容易忽视的细节和学生的仔细程度,比如六个函数转化成C语言时,语法会出现众多不适,括号较多,很容易出错;另一方面,在用pow函数求x的y次方时,容易将x的1/3次方写成pow(x,1/3),这样写是错误的。
因为1/3为两int型整数相除,系统执行结果为0,故应写成pow(x,1.0/3)。
这是一个极容易忽视的地方;
☆3.方法改进
由于求导法判定迭代收敛时,必定限制在一个区间内,而这个区间内只能存在一个根。
因此用该方法判断出来的迭代结果如果是不收敛,并不能说明它对于其余的根也不收敛。
因此,求导法只能求出f(x)的一个根,不能满足题目要求,而且求导需人工执行、过程繁琐。
故对该算法进行改进:
#include"math.h"
#include"stdio.h"
doublef1(doublex)
{return((3*x+1)/(x*x));}
doublef2(doublex)
{return((pow(x,3)-1)/3);}
doublef3(doublex)
{return(pow(3*x+1,1.0/3));}
doublef4(doublex)
{return(1/(x*x-3));}
doublef5(doublex)
{return(pow((3+1/x),1.0/2));}
doublef6(doublex)
{return(x-(x*x*x-3*x-1)/(3*(x*x-1)));}//无需建立导函数
voidmain()
{
doublei,j;
doubleeps=1e-5;
intk;
k=0;
i=f1
(1);j=i;i=f1(j);
while(fabs(i-j)>eps)
{
j=i;
i=f1(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
k=0;
i=f2
(1);j=i;i=f2(j);
while(fabs(i-j)>eps)
{
j=i;
i=f2(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
k=0;
i=f3
(1);j=i;i=f3(j);
while(fabs(i-j)>eps)
{
j=i;
i=f3(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
k=0;
i=f4
(1);j=i;i=f4(j);
while(fabs(i-j)>eps)
{
j=i;
i=f4(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
k=0;
i=f5
(1);j=i;i=f5(j);
while(fabs(i-j)>eps)
{
j=i;
i=f5(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
k=0;
i=f6
(1);j=i;i=f6(j);
while(fabs(i-j)>eps)
{
j=i;
i=f6(j);k++;
}
if(i<-99999)printf("不满足收敛条件\n");
elseprintf("满足收敛条件,x≈%f,迭代次数为%d次\n",i,k);
}
改进算法的程序运行结果如图2-4所示:
图2-4
该方法的思想是:
无区间限制迭代,如果迭代结束得到的结果为无穷大或无穷小,则说明该迭代不收敛。
这种做法更加实用,而且可以看出,求得了两个根。
但就科学性而言,教材上定理2.1的内容显示了迭代法收敛的条件。
定义明确指出用求导判断收敛性,根据定义判断才是最科学的方法。
但考虑到计算机语言的便利性就在于全过程无人工计算,而手动求导的方法与之相违背,从这个角度考虑,改进法更加具有说服力。
但若只需求一个根而又要讲究科学性,我们仍然可以选择求导法。
因此,我们应做到根据情况的不同来选择不同的适当方法来解决问题。
4.初始值的选取对迭代收敛有何影响?
初始值的选取虽然不会改变迭代收敛的性质,但是会影响其收敛速度。
为了提高收敛速度,可设法提高初值的精度以减少迭代的次数。
5.分析迭代收敛和发散的原因。
迭代公式收敛与否,完全取决于迭代公式ψ(x)的性态。
这里考察到迭代法的几何意义:
方程x=ψ(x)的求根问题在几何上就是确定曲线y=ψ(x)与直线y=x的交点P的横坐标。
按教材图2.3演示的路径走下去,在曲线y=ψ(x)上得到点列P0,P1,…,其横坐标分别按迭代公式所确定的迭代值x1,x2,…,如果迭代收敛,则点P1,P2,…将越来越逼近所求的交点P。
否则迭代法发散。
这是迭代收敛和发散的本质原因。
实验三线性方程组的直接解法
1.问题提出
给出下列几个不同类型的线性方程组,请用适当算法计算其解。
二.要求
1.对上述三个方程组分别利用Gauss顺序消去法与Gauss列主元消去法;平方根法与改进平方根法;追赶法求解(选择其一);
2.应用结构程序设计编出通用程序;
3.比较计算结果,分析数值解误差的原因;
4.尽可能利用相应模块输出系数矩阵的三角分解式。
三.实验目的和意义
1.通过该课题的实验,体会模块化结构程序设计方法的优点;
2.运用所学的计算方法,解决各类线性方程组的直接算法;
3.提高分析和解决问题的能力,做到学以致用;
4.通过三对角形线性方程组的解法,体会稀疏线性方程组解法的特点。
四.计算公式
本次实验的三个方程组,在此分别使用Gauss顺序消去法、平方根法和追赶法求解。
下面给出这三种方法的计算公式。
1.Gauss顺序消去法计算公式
2.平方根法计算公式
3.追赶法计算公式
五.结构程序设计
1.用Gauss顺序消去法求解线性方程组的程序设计如下(问题1):
//Gauss顺序消去法--求解线性方程组
//设计人:
尹一君20134651
#include"stdio.h"
#include"math.h"
voidmain()
{
FILE*f;
doublea[20][20],b[20],x[20];
doublem,s;
inti,j,k;
if((f=fopen("Array3.txt","r"))==NULL)
return;
for(i=1;i<=10;i++)
for(j=1;j<=10;j++)
fscanf(f,"%lf",&a[i][j]);
for(i=1;i<=10;i++)
fscanf(f,"%lf",&b[i]);
fclose(f);
for(k=1;k<10;k++)
for(i=k+1;i<=10;i++)
{
for(j=k+1;j<=10;j++)
{
m=a[i][k]/a[k][k];
a[i][j]=a[i][j]-m*a[k][j];
}
b[i]=b[i]-m*b[k];
}
x[10]=b[10]/a[10][10];
for(i=9;i>0;i--)
{
s=0;
for(j=i+1;j<=10;j++)
s=s+a[i][j]*x[j];
x[i]=(b[i]-s)/a[i][i];
}
for(i=1;i<=10;i++)
{
if(fabs(x[i])<0.5)
x[i]=fabs(x[i]);
printf("x[%d]=%f\n",i,x[i]);
}
}
2.用平方根法求解对称正定阵系数阵线方程组的程序设计如下(问题2):
//平方根法--求解对称正定阵系数阵线方程组
//设计人:
尹一君20134651
#include"stdio.h"
#include"math.h"
voidmain()
{
FILE*f;
doublea[10][10],b[10],l[10][10],x[10],y[10];
doubles1,s2,s3,s4;
inti,j,k;
if((f=fopen("Array4.txt","r"))==NULL)
return;
for(i=1;i<=8;i++)
for(j=1;j<=8;j++)
fscanf(f,"%lf",&a[i][j]);
for(i=1;i<=8;i++)
fscanf(f,"%lf",&b[i]);
fclose(f);
l[1][1]=pow(a[1][1],1.0/2);
for(i=1,j=2;j<=8;j++)
l[j][i]=a[j][i]/l[1][1];
for(i=2;i<=8;i++)
for(j=i;j<=8;j++)
if(i==j)
{
s1=0;
for(k=1;k<=i-1;k++)
s1=s1+l[i][k]*l[i][k];
l[i][i]=pow((a[i][i]-s1),1.0/2);
}
else
{
s2=0;
for(k=1;k<=i-1;k++)
s2=s2+l[j][k]*l[i][k];
l[j][i]=(a[j][i]-s2)/l[i][i];
}
y[1]=b[1]/l[1][1];
for(i=2;i<=8;i++)
{
s3=0;
for(k=1;k<=i-1;k++)
s3=s3+l[i][k]*y[k];
y[i]=(b[i]-s3)/l[i][i];
}
x[8]=y[8]/l[8][8];
for(i=7;i>=1;i--)
{
s4=0;
for(k=i+1;k<=8;k++)
s4=s4+l[k][i]*x[k];
x[i]=(y[i]-s4)/l[i][i];
}
for(i=1;i<=8;i++)
{
if(fabs(x[i])<0.5)
x[i]=fabs(x[i]);
printf("x[%d]=%f\n",i,x[i]);
}
}
3.用追赶法求解三对角形线性方程组的程序设计如下(问题3):
//追赶法--求解三对角形线性方程组
//设计人:
尹一君20134651
#include"stdio.h"
#include"math.h"
voidmain()
{
FILE*p;
doublea[15],b[15],c[15],f[15],l[15],u[15],x[15],y[15];
inti;
if((p=fopen("Array5.txt","r"))==NULL)
return;
for(i=1;i<=10;i++)
fscanf(p,"%lf",&a[i]);
for(i=1;i<=10;i++)
fscanf(p,"%lf",&b[i]);
for(i=1;i<=10;i++)
fscanf(p,"%lf",&c[i]);
for(i=1;i<=10;i++)
fscanf(p,"%lf",&f[i]);
fclos