数据结构课程设计 银行营业模拟演示系统.docx
《数据结构课程设计 银行营业模拟演示系统.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计 银行营业模拟演示系统.docx(37页珍藏版)》请在冰豆网上搜索。
数据结构课程设计银行营业模拟演示系统
目录
第一章项目简介
1.1项目名称………………………………………………………………………………….....2.
1.2开发人员……………………………………………………………………………………..2.
1.3问题描述……………………………………………………………………………………..2.
1.4问题分析……………………………………………………………………………………..2.
1.5需求分析……………………………………………………………………………………..2
1.6指导教师……………………………………………………………………………………...3
第二章
系统概要信息……………………………………………………………………………………...4.
第三章
系统详细信息……………………………………………………………………………………….10.
第四章
程序源代码………………………………………………………………………………………...15.
第五章
设计心得体会………………………………………………………………………………………26.
参考文献………………………………………………………………………………………………..26.
第一章项目简介
1.1项目名称
银行营业模拟演示系统
1.2开发人员
徐良辉卢贵忠蓝荣生康伟栋陈厉岳
1.3问题描述:
客户业务分2种,第一种是申请从银行得到一笔资金,即借款或取款。
第2种是向银行投入一笔资金,即存款或还款。
银行有2个服务窗口,相应地有2个队列。
客户到达银行后先排第一个队。
处理每个客户业务时,如果属于第一种,且申请额超出银行现存资金总额而得不到满足,则立刻排入第2个队等候,直至满足时才离开银行,否则业务处理完后立刻离开银行。
每接待完一个第二种业务的客户,则顺序检查和处理(如果可能)第二个队列中的客户,对能满足的申请者予以满足,不能满足者重新排到第二个队列的队尾。
注意,在此检查过程中,一旦银行资金总额少于或等于刚才第一个队列中最后一个客户(第2种业务)被接待之前的数额,或者本次以将第2个队列检查或者处理了一遍,就停止检查(因为此时已不可能还有能满足者)转而继续接待第一个队列的客户。
任何时刻都只能开一个窗口。
假设检查不需要时间。
营业时间结束时所有客户立即离开银行。
写一个上述银行业务的事件驱动模拟系统,通过模拟方法求出客户在银行内逗留的平均时间。
1.4问题分析
本问题主要需要处理两个队列(fq,sq)和一个事件表(eq)。
一个队列(fq)表示第一种业务(存款或还款),另一个队列(sq)表示第二种业务(取款或借款)。
事件有两类:
到达银行和离开银行。
到达事件发生时随机地设置此用户的交易时间和距下一到达事件之间的时间间隔。
每个客户要办理的款额也应该是随机确定的。
初始时银行现存资金总额为total。
开始营业后的第一个事件是客户到达,营业时间从0到closetime。
Total和closetime以及随机产生的时间和款项的上下界都是交互地从让用户从输入,作为模拟参数。
要得到相关的结果:
由于下班时间到而没能办理的顾客数;分别列出需要办理两种业务的顾客数;分别列出已成功办理两种业务的顾客数;分别列出两种业务的成功办理率;客户在银行内的平均逗留时间;下班时银行所剩余的资金总额。
1.5需求分析
设计任务:
编写银行业务的事件驱动模拟系统,通过模拟方法求出客户在银行内逗留的平均时间。
按照使用者输入的数据输出相应的一些统计数据:
办理存款业务的人数,办理取款业务的人数,成功办理这两个业务的人数以及各自的成功办理率,还有最后银行剩余的资金总额。
实现:
1.输入:
用户需要在程序运行开始时输入以下数据:
1 银行初始资金total(测试数据为10000)
2 银行营业时间closetime(测试数据为600)
3 客户交易时间上下界dealmaxtime和dealmintime,用于给随机数产生函数传递参数,产生一个介于这两个值之间的值。
4 客户到达时间间隔上界arrivemaxtimearrivemintime,用于给随机数产生函数传递参数,产生一个介于这两个值之间的值。
5 交易额的最大上限dealMaxMoney.用于给随机函产生函数参数,产生一个介于-dealMaxMoney和dealMaxMoney之间的值,作为顾客到银行办理业务的交易额。
2.输出:
本程序用dos界面模拟输出整个银行业务办理及排队的结果,最后给出一下数据:
1 由于下班时间到而没能办理的顾客数
2 分别列出需要办理两种业务的顾客数
3 分别列出已成功办理两种业务的顾客数
4 分别列出两种业务的成功办理率。
5 客户在银行内的平均逗留时间
6 下班时银行所剩余的资金总额
所有数据均要正确输入,并对输入合法性进行检测,如只能输入数字,且上界必须不小于下界。
1.6指导教师
肖钟捷
第二章系统概要信息
用户打开运行程序以后会出现如下的首届面:
用户按任意键继续,则会出现如下主界面:
用户选择1开始模拟。
按提示输入相应的数据设置,就能得到想要的模拟结果。
退出选择0.
对用户输入要求如下:
1.银行开始的资金总额应该大于零。
2.银行的营业时间必须小于1440分钟(24小时)。
3.最大到达时间间隔必须小于营业时间。
4.最小到达时间间隔必须介于零和最大到达时间之间。
5.最大处理时间必须小于营业时间。
6.最小处理时间必须介于零和最大处理时间之间。
7.最大交易额应该小于银行开始时的资金总额且小于程序设置的50000。
如果输入不符合上述要求,程序会提示用户哪里输入出错,可以再次输入。
每个输入数据都有3次机会。
当三次输入都错的时候,程序停止运行,按任意键退出。
现给出最小处理时间出错时的三种提示截图:
调试分析
1.在做队列的扫描算法时,发现运行演示的效果很奇怪,经过仔细检查和阅读题目,终于发现是我的理解有误。
在处理顾客的第一种业务(从银行取出一笔钱)时,我没有特殊处理,仅是和处理第二种一样,即是等到该顾客交易时间结束时才开始判断,如果不够取进入第二队列等候。
但题目中有“如果属于第一种,且申请额超出银行现存资金总额而得不到满足,则立刻排入第2个队等候,直至满足时才离开银行,否则业务处理完后立刻离开银行”。
想想这也是符合实际。
于是改算法的判断为以下算法:
伪码:
{
if(!
empry(fq)&&fq.head->money<0)
//如果队列一不空,而且队头客户办理第二种业务
{
//1.可以办,完成离开(即银行现金足够取)
//2.不可以办,排队2等候
}
else
//存款,第一种业务
if(!
empty(fq)&&temped=searchAndDel(sq,total)))
//当交易时间到
{
//办理该业务,并开始扫描第二队列,看是否能满足//第二队列需要
}
if(currentime==next_arrtime)
{
//当当前时间等于即将到来的顾客到来时间,初始化//该顾客节点,并设置下一顾客到达时间
}
}
2.对于怎样计算已办理顾客的人数,一直没成功,最后在主函数中调用存款,取款函数中计算,才完成。
此时可以输入相应数据:
a.银行初始资金total(测试数据为10000)
b.银行营业时间closetime(测试数据为600)
c.客户交易时间上下界dealmaxtime和dealmintime
d.客户到达时间间隔上下界arrivemaxtime和arrivemintime
e.交易额的上限dealmaxmoney.
程序模拟结束
最后给出一下数据:
1.由于下班时间到而没能办理的顾客数
2.分别列出需要办理两种业务的顾客数
3.分别列出已成功办理两种业务的顾客数
4.分别列出两个窗口办理业务的效率
5.客户在银行内的平均逗留时间
6.下班时银行的剩余总额。
测试结果
正确输入并开始模拟运行:
1 输入一般数据:
a.银行初始资金total=10000
b.银行营业时间closetime=600
c.客户到达时间间隔上界arrivemaxtime=10和arrivemintime=5
d.客户交易时间上界dealmaxtime=10和dealmintime=5
2 当到达时间之间的间隔很短,但客户的交易时间很长:
a.银行初始资金total=10000
b.银行营业时间closetime=600
c.客户到达时间间隔上界arrivemaxtime=2和arrivemintime=1
d.客户交易时间上界dealmaxtime=100和dealmintime=1
从上面截图可以看出,当到达时间之间的间隔很短,但客户的交易时间很长时,最后会出现很多顾客到银行下班时间还没办理的现象。
两种业务的办理成功率比较低的。
3.当到达时间之间的间隔很长,但客户的交易时间很短:
a.银行初始资金total=10000
b.银行营业时间closetime=600
c.客户到达时间间隔上界arrivemaxtime=100arrivemintime=1
d.客户交易时间上下界dealmaxtime=2和dealmintime=1
从上面两幅截图可以看出,当到达时间之间的间隔很长,但客户的交易时间很短时,未处理客户基本上是没有的,两个窗口的办业务成功率很高。
第三章系统详细信息
结构体的定义如下:
structservice
{
intnum;//客户号
stringtype;//到达或离开
intbeginTime;//到达时间
intendTime;//离开时间
intmoney;//正数为存款,负数为取款
service*next;//指针域
};
队列的抽象数据类型定义如下:
ADTQueue{
数据对象:
D={ai|ai∈ElemSet,i=1,2,...,n,n≥0}
数据关系:
R1={|ai-1,ai∈D,i=2,...,n}
基本操作:
voidinit_Q(Queue&Q);
操作结果:
构造空队列Q
intQ_empty(QueueQ);
初始条件:
队列Q存在
操作结果:
若Q为空队列,则返回TRUE,否则FALSE
intQ_length(QueueQ);
初始条件:
队列Q存在
操作结果:
返回队列Q的元素个数,即队列长度
intgethead_Q(QueueQ);
初始条件:
队列Q存在
操作结果:
返回队列Q的队头元素
voiden_Q(Queue&Q,inte);
初始条件:
队列Q存在
操作结果:
插入元素e为Q的新的队尾元素。
voidde_Q(Queue&Q,int&e);
初始条件:
队列Q存在
操作结果:
删除Q的队头元素。
}ADTQueue
模块及相互调用关系
本程序包含3个模块:
1 主程序模块
2 队列模块--实现队列抽象数据类型
各模块相互调用关系如下:
主程序模块
队列模块
算法设计
队列类型
typedefstructQNode
{//队列节点类型
intdata;
structQNode*next;
}QNode,*PQNode;
typedefstruct{
PQNodefront;//队头指针
PQNoderear;//队尾指针
}Queue;
队列的基本操作设置如下:
voidinit_Q(Queue&Q);
//初始化,构造空队列Q(Q.front=Q.rear)
intQ_empty(QueueQ);
//若队列Q存在
//若Q为空队列,则返回TRUE,否则FALSE
intQ_length(QueueQ);
//若队列Q存在
//返回队列Q的元素个数,即队列长度
intgethead_Q(QueueQ);
//若队列Q存在
//返回队列Q的队头元素
voiden_Q(Queue&Q,inte);
//若队列Q存在
//插入元素e为Q的新的队尾元素。
voidde_Q(Queue&Q,int&e);
//若队列Q存在
//删除Q的队头元素。
其中操作算法:
voidinit_Q(Queue&Q)
{
//初始化,构造空队列Q(Q.front=Q.rear)
Q.front=Q.rear=(PQNode)malloc(sizeof(QNode));
if(!
Q.front)exit(-1);
}
intQ_empty(QueueQ)
{
//若队列Q存在
//若Q为空队列,则返回TRUE,否则FALSE
if(Q.front==Q.rear)return1;
elsereturn0;
}
intgethead_Q(QueueQ)
{
//若队列Q存在
//返回队列Q的队头元素
if(!
Q_empty(Q))returnQ.front->next->data;
elsereturn0;
}
intQ_length(QueueQ)
{
//若队列Q存在
//返回队列Q的元素个数,即队列长度
intcount=0;
PQNodep=Q.front;
while(p!
=Q.rear)
{
count++;
p=p->next;
}
returncount;
}
voiden_Q(Queue&Q,inte)
{
//若队列Q存在
//插入元素e为Q的新的队尾元素。
PQNodep=(PQNode)malloc(sizeof(QNode));
if(!
p)exit(-1);
p->data=e;p->next=NULL;
Q.rear->next=p;
Q.rear=p;
}
voidde_Q(Queue&Q,int&e)
{
//若队列Q存在
//删除Q的队头元素
PQNodep;
if(Q.front==Q.rear)return;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p)Q.rear=Q.front;
free(p);
}
队列扫描算法
伪码:
{
if(!
empry(fq)&&fq.head->money<0)
//如果队列一不空,而且队头客户办理第二种业务
{
//1.可以办,完成离开(即银行现金足够取)
//2.不可以办,排队2等候
}
else
//存款,第一种业务
if(!
empty(fq)&&temped=searchAndDel(sq,total)))
//当交易时间到
{
//办理该业务,并开始扫描第二队列,看是否能满足//第二队列需要
}
if(currentime==next_arrtime)
{
//当当前时间等于即将到来的顾客到来时间,初始化//该顾客节点,并设置下一顾客到达时间
}
}
其他函数算法
intrand_num(intmax,intmin)//随机数生成
{
//根据传进的max和min,生成介于max和min的一个//整数
randomize();
RandSeed=Now();
return(random(100000)%(max-min+1)+min);
}
第四章程序源代码
//银行业务模拟系统
#include
#include
#include
#include
usingnamespacestd;
structservice
{
intnum;//客户号
stringtype;//到达或离开
intbeginTime;//到达时间
intendTime;//离开时间
intmoney;//正数为存款,负数为取款
service*next;//指针域
};
structqueue
{//队列
service*head;//队列头指针
service*rear;//队列尾指针
};
inttotal;//初始时银行现存资金总额
intcloseTime;//营业结束时间
intarriveMaxTime;//两个到达事件之间的间隔上限
intarriveMinTime;//两个到达事件之间的间隔下限
intdealMaxTime;//客户之间交易的时间上限
intdealMinTime;//客户之间交易的时间下限
intdealMaxMoney;//交易额上限
intndn=0;//需要存款的人数
intnwn=0;//需要取款的人数
intsdn=0;//成功存款的人数
intswn=0;//成功取款的人数
intcurrentTime=0;//当前时间
inttotalTime=0;//客户逗留总时间
intcounter=0;//客户总数
intnumber=1;//初始客户序列号
boolstate=1;//用于判断是否有窗口在处理
intcurrentTimeOfDeal=0;
intthearriveMaxTime=0;
queueeq;//事件队列
queuefq;//队列一
queuesq;//对列二
//初始化三个队列
service*front(queue&q)
{//返回队首元素
returnq.head;
}
service*back(queue&q)
{//返回队尾元素
returnq.rear;
}
voidpush(queue&q,intd)
{//插入元素d为Q的新的队尾元素
service*temp=newservice;
temp->money=d;
temp->next=NULL;
if(q.head==NULL)
{//队列为空,初始化
q.head=temp;
q.rear=temp;
}//if
else
{//队列不为空,插入元素d
q.rear->next=temp;
q.rear=q.rear->next;
}//else
}
voidpop(queue&q)
{//若队列不空,出对列函数
service*temp;
temp=q.head;
if(q.head->next==NULL)
q.head=q.rear=NULL;
else
q.head=q.head->next;
deletetemp;
}
voidarrive()
{//"到达"函数,随机产生顾客,进入队列一产生到达事件进入事件队列
push(fq,(rand()%(2*dealMaxMoney)-dealMaxMoney));//随机产生顾客加入第一队列
back(fq)->beginTime=currentTime;
back(fq)->num=number;
push(eq,(back(fq)->money));//将产生事件加入事件队列
back(eq)->beginTime=currentTime;
back(eq)->type="到达";
back(eq)->num=number;
++number;
}
voidputMoney()
{//"存款"函数
total+=front(fq)->money;//更新资金总额
push(eq,front(fq)->money);//加入事件队列离开
back(eq)->type="离开";
back(eq)->num=front(fq)->num;
back(eq)->endTime=(front(fq)->beginTime+rand()%(dealMaxTime-dealMinTime+1)+dealMinTime);
++counter;//更新客户总数
totalTime+=(back(eq)->endTime-front(fq)->beginTime);//更新逗留时间
pop(fq);//删除第一队列第一个业务
currentTimeOfDeal=back(eq)->endTime;
state=0;
}
voidgetMoney()
{//"取款"函数
if((-fq.head->money)>total)
{//资金短缺加入第二队列
push(sq,front(fq)->money);
back(sq)->beginTime=front(fq)->beginTime;
back(sq)->num=front(fq)->num;
pop(fq);
}//if
else
{
total+=back(fq)->money;
push(eq,front(fq)->money);//加入事件队列离开
back(eq)->type="离开";
back(eq)->num=front(fq)->num;
back(eq)->endTime=(front(fq)->beginTime+rand()%(dealMaxTime-dealMinTime+1)+dealMinTime);
back(eq)->beginTime=0;
currentTimeOfDeal=back(eq)->endTime;
++counter;//更新客户总数
totalTime+=(back(eq)->endTime-back(fq)->beginTime);//更新逗留时间
pop(fq);//删除第一队列第一个业务
state=0;
}//else
}
service*searchAndDel(queue&q,intm)
{//"搜索"函数,在对列中寻找可处理元素
service*sign=q.head;//标记头节点
service*temp;
while(q.head!
=NULL)
{
if((-(q.head->money)){//队首元素可以处理
if(q.head==q.rear)
{
temp=q.head;
q.head=q.rear=NULL;
returntemp;
}//if
else
{//队首元素出列
temp=q.head;
q.head=q.head->next;//首节点后移