课程设计.docx
《课程设计.docx》由会员分享,可在线阅读,更多相关《课程设计.docx(37页珍藏版)》请在冰豆网上搜索。
课程设计
实验一用计算机自动求解关系闭包2
一.需求分析2
二.概要设计3
三.详细设计6
四.调试分析:
9
五.测试结果如下;10
六.用户手册10
七.附录11
实验二对保龄球记分12
一.需求分析:
12
二.概要设计:
13
三.详细设计:
16
四.调试分析18
五.测试结果18
六.用户手册19
七.附录21
实验三设计程序为客人提供相关服务21
一.需求分析:
21
二.概要设计21
三.详细设计22
四.设计和调试分析;26
五.用户手册26
六.测试结果:
27
七.附录:
28
实验一用计算机自动求解传递闭包
一.需求分析
(1)用关系矩阵的形式输入一关系R,由计算机自动求解关系R的传递闭包,
并用矩阵的形式予以输出。
(2)在集合X上的二元关系R的传递闭包是包含R的X上的最小的传递关系。
一般用B表示定义在具有n个元素的集合X上关系R的n×n阶矩阵,则传递闭包的矩阵B+可如下计算:
B+=B+B2+B3+……+(B)n
式中矩阵运算时所有乘法都用逻辑与代替,所有加法都用逻辑或代替。
上式中的操作次序为B,B(B),B(BB),B(BBB),……,所以在运算的每一步只需简单地把现有结果乘以B,完成矩阵的n次乘法即可。
(3)输入由n个元素组成的关系矩阵M,并置新矩阵A=M,置k=1,对所有正整数i如果A[i,k]=1,则对j=1…n执行:
A[i,j]←A[i,j]∨A[k,j],同时k增1;如果k≤n,则返回重新执行原过程,否则停止。
所得的矩阵A即为关系R的传递闭包t(R)的关系矩阵。
(4)测试数据为:
若输入原矩阵为:
1001
0011
1100
1010
则输出的传递闭包如下所示:
<1,1>,<1,2>,<1,3>,<1,4>,<2,1>,<2,2>,<2,3>,<2,4>,<3,1>,<3,2>,<3,3>
<3,4>,<4,1>,<4,2>,<4,3>,<4,4>
(5)程序执行的命令为:
(1)输入组成关系矩阵R的元素个数;
(2)创建关系R的初始矩阵M;
(3)求解关系R的传递闭包;
(4)用矩阵的形式输出所形成的传递闭包。
二.概要设计
1.数组的顺序存储表示
#include//标准头文件,提供宏va_start,va_arg和va_end,
//用于寸取变长参数表
#defineMAX_ARRAY_DIM8//假设数组维数的最大值为8
typedefstruct{
ElemType*base;//数组元素基址,由InitArray分配
intdim//数组维数
int*bounds;//数组维界基址,由InitArray分配
int*constants;//数组映像函数常量基址,由InitArray分配
}Array;
2.数组的抽象数据类型的定义:
ADTArray{
数组对象:
ji=0,…,bi-1,I=1,2,3,…,n.
D={aj1j2j3…jn|n(>0)称为数组的维数,bi是数组第维的长度,ji是数组元素的第i维下标,aj1j2j3…jn属于ElemSet}
数组元素:
R={R1,R2,…,Rn}
Ri={|
00aj1…ji…jn,aj1…ji+1…jn属于D,i=2,…,n}
基本操作:
InitArray(&A,n,bound1,….boundn)
操作结果;若维数和各维长度合法,则构造相应的数组A,并返回OK.
DestroyArray(&A)
操作结果;销毁数组A.
Value(A,&e,index1,…,indexn)
初始条件;A是n维数组,e为元素变量,随后是n个下标值。
操作结果:
若各下标不超界,则e赋值为所指定的A的元素值,并返回OK.
Assign(&A,e,index1,…,indexn)
初始条件:
A是n维数组,e为元素变量,随后是n个下标值。
操作结果;若下标不超界,则将e的值赋给所指定的A的元素,并返回OK.
}ADTArray
3.抽象数据类型稀疏矩阵的定义为:
ADTSparseMatrix{
数据对象:
D={aij|I=1,2,…,m;j=1,2,…,n;
ai,j属于ElemSet,m和n分别称为矩阵的行数和列数}
数据关系;R={Row,Col}
Row={|1Col={|1基本操作;
CreateSMatrix(&M);
操作结果;创建稀疏矩阵M。
DestroySMatrix(&M);
初始条件:
稀疏矩阵M存在。
操作结果;销毁稀疏矩阵M.
PrintSMatrix(&M);
初始条件:
稀疏矩阵M存在。
操作结果;输出稀疏矩阵M.。
CopySMatrix(M,&T);
初始条件:
稀疏矩阵M存在。
操作结果;由稀疏矩阵M复制得到T.
AddSMatrix(M,N,&Q);
初始条件:
稀疏矩阵M与N的行数和列数对应相等。
操作结果;求稀疏矩阵的和Q=M+N.
SubSMatrix(M,N,&Q);
初始条件:
稀疏矩阵M与N的行数和列数对应相等。
操作结果;求稀疏矩阵的差Q=M-N.
TrasposeSMatrix(M,&T);
初始条件:
稀疏矩阵M存在
操作结果;求稀疏矩阵M的转置矩阵T.
}ADTSparseMatrix
4.本程序包含for循环的多次嵌套
定义全局变量
主函数main()
{
利用for循环输入欲求传递闭包的关系矩阵;
利用for循环求解已输入关系矩阵的传递闭包;
利用for循环输出所求得到关系。
}
具体过程如下所示:
三.详细设计
1.元素类型;
#defineMAX_ARRAY_DIM8//假设数组维数的最大值为8
typedefstruct{
ElemType*base;//数组元素基址,由InitArray分配
intdim//数组维数
int*bounds;//数组维界基址,由InitArray分配
int*constants;//数组映像函数常量基址,由InitArray分配
2,数组中基本操作设置如下;
StatusInitArray(Array&A,intdim,…);
//若维数dim和各维长度合法,则构造相应的数组A,并返回OK.
StatusDestroyArray(Array&A)//销毁数组A.
StatusValue(ArrayA,ElemTypee,…)
//A是n维数组,e为元素变量,随后是n个下标值。
//若各下标不超界,则e赋值为所指定的A的元素值,并返回OK.
StatusAssign(Assign&A,ElemTypee,…)
//A是n维数组,e为元素变量,随后是n个下标值。
//若下标不超界,则将e的值赋给所指定的A的元素,并返回OK.其中部分操作的伪码算法如下:
StatusInitArray(Array&A,intdim,…);
//若维数dim和各维长度合法,则构造相应的数组A,并返回OK.
if(dim<1||dim>MAX_ARRAY_DIM)returnERROR;
A.dom=dom;
A.bounds=(int*)malloc(dim*sizeof(int));
if(!
A.bounds)exit(OVERFLOW);
//若长度合法,则存入A.bounds,并求出的元素总数elemtotal
elemtotal=1;
va_start(ap,dim);
for(i=0;iA.bounds[i]=va_arg(ap,int);
if(A.bounds[i]<0)returnUNDERFLOW;
elemtotal*=A.bounds[i];
}
va_end(ap);
A.base=(ElemType*)malloc(elemtotal*size(ElemType));
if(!
A.base)exit(OVERFLOW);
//求映像函数的常数ci,并存入A.constants[i-1],I=1,..,dim
A.constants=(int*)malloc(dim*sizeof(int));
if(!
A.constants)exit(OVERFLOW);
A.constants[dim-1]=1;//L=1,指针的增减以元素的大小为单位
for(i=dim-2;i>=0;--I)
A.constants[i]=A.bounds[i+1]*A.constants[i+1];
returnOK;
}
3.程序的实现过程:
#include"stdio.h"
#defineN10//定义全局变量N,指矩阵中所能允许输入的元素的最大个数
主函数部分
main()
{
inti,j,a[N][N],b[N][N],c[N][N],s=0,k,e[N][N],m,n;//定义局部变量,矩阵并对相关变量初始化
printf("Pleaseinputthenumbern(n<=100):
\n");//输入矩阵的元素个数
scanf("%d",&n);//逐个输入数据元素
printf("PleaseinputtheMaxtix:
\n");//输入一矩阵
利用for循环对矩阵进行输入
for(i=0;ifor(j=0;j{
scanf("%d",&a[i][j]);//输入元素的值
e[i][j]=a[i][j];//将矩阵a[i][j]的值赋值给矩阵e[i][j]
b[i][j]=a[i][j];及矩阵b[i][j]
}
继续利用for循环求矩阵的传递闭包
for(m=1;m计算到原矩阵的n次方时,经过矩阵的合取即可得到矩阵的传递闭包
{
for(i=0;ifor(j=0;j{
for(s=0,k=0;ks+=b[i][m]*a[m][j];
c[i][j]=s;//将求得的元素s赋值给新矩阵c[i][j]
if(e[i][j]==0&&c[i][j]!
=0)
e[i][j]=c[i][j];//将矩阵c[i][j]中值不为0的元素赋值给矩阵e[i][j]
}
利用for循环将得到的结果予以输出
for(i=0;ifor(j=0;jb[i][j]=c[i][j];
}
for(i=0;ifor(j=0;jif(e[i][j]!
=0)//若矩阵e[i][j]中元素不为0,则可将其输出
printf("<%d,%d>,",i+1,j+1);
printf("\n");
}
四.调试分析:
1.在求解矩阵的传递闭包时由于思路比较简单,因此相对比较容易解决。
2.在具体实现过程过程中要注意变量的正确定义,使用范围以及for循环的嵌套使用。
3.由于数组一般不作插入及删除操作,即一旦建立了数组,则结构中的数组元素个数和元素之间的关系就不再发生变动,因此在构建数组时通常采用顺序存储结构予以实现。
4.经过离散数学的学习已经知道在求解n阶矩阵的关系闭包时只需要求到矩阵的n次幂即可,而不需要求无数次幂,然后再进行合取求解,因此就减少了程序的复杂程度。
五.测试结果如下;
六.用户手册
1.本程序的运行环境为TC环境。
2.进入演示程序后即显示文本方式的界面。
3.当显示出用户界面时,根据相关提示首先输入要输入矩阵中元素的个数,之后输入0或1构造一矩阵。
4.执行完相关命令后,即可得出用户所需求的传递闭包。
5.之后点击键Q即可退出相关界面。
七.附录
源程序如下所示;
#include"stdio.h"
#defineN10
main()
{
inti,j,a[N][N],b[N][N],c[N][N],s=0,k,e[N][N],m,n;
printf("Pleaseinputthenumbern(n<=100):
\n");
scanf("%d",&n);
printf("PleaseinputtheMaxtix:
\n");
for(i=0;ifor(j=0;j{
scanf("%d",&a[i][j]);
e[i][j]=a[i][j];
b[i][j]=a[i][j];
}
for(m=1;m{
for(i=0;ifor(j=0;j{
for(s=0,k=0;ks+=b[i][k]*a[k][j];
c[i][j]=s;
if(e[i][j]==0&&c[i][j]!
=0)
e[i][j]=c[i][j];
}
for(i=0;ifor(j=0;jb[i][j]=c[i][j];
}
for(i=0;ifor(j=0;jif(e[i][j]!
=0)
printf("<%d,%d>,",i+1,j+1);
printf("\n");
}
实验二对保龄球记分
一.需求分析:
(1)用数组的形式对保龄球游戏进行记分;
(2)游戏规则为:
打保龄球是用一个滚球去撞击10个站立的瓶,将瓶击倒。
一局分10轮,每轮可滚球1次或2次,以击到的瓶数为依据计分。
一局得分为10轮得分之和,而每轮的得分不仅与本轮的滚球情况有关,还可能与后一轮或后两轮的滚球情况有关,即:
某轮某次滚球击倒的瓶数不仅要计入本轮得分,还可能会计入前一轮或两轮得分。
(3)计分规则如下:
1)若某一轮的第一次滚球就击倒全部10个瓶,则本轮不再滚球(若是第十轮还需加2次滚球),该轮得分为本次击倒瓶数10与以后2次滚球所击倒瓶数之和;
2)若某一轮的第一次滚球未击倒全部10个瓶,则对剩下未倒的瓶再滚球一次,如果这2次滚球击倒全部10个瓶,则本轮不再滚球(若是第十轮还需加1次滚球),该轮得分为这2次击倒瓶数10与以后1次滚球所击倒瓶数之和;
3)若某一轮2次滚球未击倒全部10个瓶,则本轮不在滚球,该轮得分为这2次滚球所击倒瓶数之和。
(4)具体做法为:
1)模拟1人打保龄球的过程,用一个二维数组:
intx[11][4];存储每轮每次击倒的瓶数和得分以及累计得分。
即:
一行中的4个元素x[i][0]、x[i][1]、x[i][2]、x[i][3]分别记录第i轮的第1次滚球击倒的瓶数、第2次滚球击倒的瓶数、本轮得分和累计得分;
2)输入每轮每次滚球击倒的瓶数,若第1次滚球击倒的瓶数为10,则该轮只输入1次数据;
3)输出每轮每次击倒的瓶数和得分以及累计得分,若仿照例题输入所要测定的值,则输出结果如下所示;
一
二
三
四
五
六
七
八
九
十
8
10
7
9
9
10
10
8
9
10
8
2
0
2
1
1
0
0
1
1
0
2
20
19
9
19
20
28
19
9
20
20
20
39
48
67
87
115
134
143
163
183
4)程序执行的命令为:
●根据输入的得分数据构造一个二维数组;
●输入的数据计算在每轮中的得分及累积得分;
●出每轮每次击倒的瓶数和得分以及累计得分;
二.概要设计:
1.数组的相关知识链接:
(1)对数组进行定义:
intcompact(type*Array,intCount)
数组类型说明在C语言中使用数组必须先进行类型说明。
数组说明的一般形式为:
类型说明符数组名[常量表达式],……;
其中,类型说明符是任一种基本数据类型或构造数据类型。
数组名是用户定义的数组标识符,方括号中的常量表达式表示数据元素的个数,也称为数组的长度。
例如:
inta[10];说明整型数组a,有10个元素。
floatb[10],c[20];说明实型数组b,有10个元素,实型数组c,有20个元素。
charch[20];说明字符数组ch,有20个元素。
(2)对数组进行声明:
有三种方法声明固定大小的数组,用哪一种方法取决于数组应有的有效范围:
1)建立公用数组,在模块的声明段用Public语句声明数组。
2)建立模块级数组,在模块的声明段用Private语句声明数组。
3)建立局部数组,在过程中用Private语句声明数组。
(3)数组的分类:
一维数组:
数据类型数组名[长度];
二维数组:
类型说明符数组名[常量表达式1][常量表达式2]…;
其中常量表达式1表示第一维下标的长度,常量表达式2表示第二维下标的长度。
二维数组的元素也称为双下标变量,其表示的形式为:
数组名[下标][下标]。
其中下标应为整型常量或整型表达式。
例如:
a[3][4]表示a数组三行四列的元素,下标变量和数组说明在形式中有些相似,但这两者具有完全不同的含义。
数组说明的方括号中给出的是某一维的长度,即可取下标的最大值;而数组元素中的下标是该元素在数组中的位置标识。
前者只能是常量,后者可以是常量,变量或表达式。
字符数组:
用来存放字符量的数组称为字符数组。
字符数组类型说明的形式与前面介绍的数值数组相同。
例如:
charc[10];由于字符型和整型通用,也可以定义为intc[10]但这时每个数组元素占2个字节的内存单元。
字符数组也可以是二维或多维数组,例如:
charc[5][10];即为二维字符数组。
字符数组也允许在类型说明时作初始化赋值。
例如:
staticcharc[10]={`c`,``,`p`,`r`,o`,g`,r`,`a`,`m`};赋值后各元素的值为:
数组Cc[0]c[1]c[2]c[3]c[4]c[5]c[6]c[7]c[8]c[9]其中c[9]未赋值,由系统自动赋予0值。
(4)注意事项:
1)可以只给部分元素赋初值。
当{}中值的个数少于元素个数时,只给前面部分元素赋值。
例如:
staticinta[10]={0,1,2,3,4};表示只给a[0]~a[4]5个元素赋值,而后5个元素自动赋0值。
2)只能给元素逐个赋值,不能给数组整体赋值。
例如给十个元素全部赋1值,只能写为:
staticinta[10]={1,1,1,1,1,1,1,1,1,1};而不能写为:
staticinta[10]=1;(请注意:
在C语言中是这样,但并非在所有涉及数组的地方都这样)
3)如不给可初始化的数组赋初值,则全部元素均为0值。
4)如给全部元素赋值,则在数组说明中,可以不给出数组元素的个数。
例如:
staticinta[5]={1,2,3,4,5};可写为:
staticinta[]={1,2,3,4,5};动态赋值可以在程序执行过程中,对数组作动态赋值。
这时可用循环语句配合scanf函数逐个对数组元素赋值。
`r`,`o`,`g`,`r`,`a`,`m`};这时C数组的长度自动定为9。
2.保龄球计分游戏中所用数组的定义:
二维数组:
类型说明符数组名[常量表达式1][常量表达式2];
其中常量表达式1表示第一维下标的长度,常量表达式2表示第二维下标的长度。
二维数组的元素也称为双下标变量,其表示的形式为:
数组名[下标][下标]。
其中下标应为整型常量或整型表达式。
例如:
a[3][4]表示a数组三行四列的元素,下标变量和数组说明在形式中有些相似,但这两者具有完全不同的含义。
数组说明的方括号中给出的是某一维的长度,即可取下标的最大值;而数组元素中的下标是该元素在数组中的位置标识。
前者只能是常量,后者可以是常量,变量或表达式。
3.本程序包含四个模块
1)数据输入模块:
在主函数中首先定义一二维数组,然后通过对变量i.j的控制逐个输入在每轮每次的得分数。
2)记录每轮得分模块:
对于每轮的得分要分情况考虑,因为根据计分规则可知若某一轮的第一次滚球就击倒全部10个瓶,则本轮不再滚球(若是第十轮还需加2次滚球),该轮得分为本次击倒瓶数10与以后2次滚球所击倒瓶数之和;若某一轮的第一次滚球未击倒全部10个瓶,则对剩下未倒的瓶再滚球一次,如果这2次滚球击倒全部10个瓶,则本轮不再滚球(若是第十轮还需加1次滚球),该轮得分为这2次击倒瓶数10与以后1次滚球所击倒瓶数之和;若某一轮2次滚球未击倒全部10个瓶,则本轮不在滚球,该轮得分为这2次滚球所击倒瓶数之和。
3)记录累计得分模块:
及每一次的得分都等于该轮得分与前几轮得分的和,因此也同样要用到循环语句予以解决。
4)数据输出模块:
在计算出每轮得分及累计得分后,要进行矩阵转置予以输出新的结果。
各模块间的关系如下所示:
1.主程序模块;
#include"stdio.h"
main()
{
定义变量,并对相关数据进行初始化
for{
对变量进行相关操作
}
}
2.设计程序输入每轮每次的得分
for(i=0;i<11;i++)
{
printf("firstnumber:
");
scanf("%d",&x[i][0]);//输入第i+1轮的第一次得分数
if(x[i][0]!
=10)//如果第i+1轮的第一次得分为10,则不需要进
行第二次击球,否则就要进行第二次击球,计算得分数。
{printf("secondnumber:
");
scanf("%d",&x[i][1]);
}
printf("\n");
}
3.设计程序计算每轮得分数,在此由于第10次比较特殊,故需要单独讨论。
for(i=0;i<9;i++)//在前9次中,通过对i进行处理计算每次的得分数
{
if(x[i][0]==10)//若第i轮中第一次得10分,则不再进行第二次击球
{
x[i][1]=0;
if(x[i+1][0]==10)
x[i][2]=20+x[i+2][0];//在第i轮第一次得10分的基础上,若第i+1
轮第