资源描述
东北大学信息科学与工程学院
数据结构课程设计报告
题目 银行业务模拟
课题组长 侯永跃
课题组成员 林浩成 李博然 韩硕
专业名称 计算机科学与技术
班级 计1307
指导教师 杨雷
2015 年 1月
课程设计任务书
题目:
银行业务模拟
问题描述:
银行有N个窗口对外接待客户。从早晨银行营业开始到晚间营业终止不断有客户办理业务。银行门口设有取号机,分为一般业务和特殊业务,设每个一般业务不超过5分钟,每个特殊业务不超过15分钟。客户在等候区内等候叫号。
设计要求:
设计银行客户业务的模拟程序。
(1)采用有序链表、队列等数据结构。
(2)分类统计一天内客户在银行逗留的平均时间和人数。
(3)若特殊业务窗口空闲,可办理一般业务。
(4)可以随机或人工输入客户到达银行的时间。
(5)其它完善性功能。
指导教师签字:
年 月 日
目录
1 课题概述 4
1.1 课题任务 4
1.2 课题原理 4
1.3 相关知识 5
2 需求分析 5
2.1 课题调研 5
2.2 用户需求分析 6
3 方案设计 6
3.1 总体功能设计 6
3.2 数据结构设计 9
3.3 函数原型设计 10
3.4 主算法设计 11
3.5 用户界面设计 14
4 方案实现 15
4.1 开发环境与工具 15
4.2 程序设计关键技术 16
4.3 个人设计实现(按组员分工)
4.3.1 侯永跃设计实现 16
4.3.2 李博然设计实现 23
4.3.3 林浩成设计实现 28
4.3.4 韩硕设计实现 32
5 测试与调试 35
5.1 个人测试 35
5.1.1 侯永跃测试 35
5.2 组装与系统测试 38
5.3 系统运行 41
6 课题总结 43
6.1 课题评价 43
6.2 团队协作 44
6.3 下一步工作 44
6.4 个人设计小结(按组员分工) 44
6.4.1 侯永跃设计小结 44
6.4.2 李博然设计小结 45
6.4.3 林浩成设计小结 45
6.4.4 韩硕设计小结 46
7 附录A 课题任务分工 46
A-1 课题程序设计分工 46
A-2 课题报告分工 49
附录B 课题设计文档(光盘) 49
B-1课程设计报告(电子版) 49
B-2源程序代码(*.H,*.CPP) 49
B-3工程与可执行文件) 49
B-4屏幕演示录像文件(可选) 49
附录C 用户操作手册(可选) 49
C.1 运行环境说明 49
C.2 操作说明 49
1 课题概述
1.1课题任务
银行业务模拟
【问题描述】
银行有N个窗口对外接待客户。从早晨银行营业开始到晚间营业终止不断有客户办理业务。银行门口设有取号机,分为一般业务和特殊业务,设每个一般业务不超过5分钟,每个特殊业务不超过15分钟。客户在等候区内等候叫号。
【设计要求】
设计银行客户业务的模拟程序。
(1)采用有序链表、队列等数据结构。
(2)分类统计一天内客户在银行逗留的平均时间和人数。
(3)若特殊业务窗口空闲,可办理一般业务。
(4)可以随机或人工输入客户到达银行的时间。
(5)其它完善性功能。
通过本实验理解并掌握数据结构及其应用,巩固程序设计基础知识,初步学习软件工程、可视化程序设计的知识,提高编程水平和分析问题、解决问题的能力,增强学习能力和合作能力。
1.2 课题原理
本程序主要采用链表、队列等数据结构,链表定义为有序链表,实现了事件之间的联系。本设计关键在于对事件的处理,将银行业务模拟分为客户到达银行办理业务事件、办理业务结束离开银行事件。客户到达银行、离开银行等事件的信息都存入链表中,对链表可以进行插入、写入文件等操作。
另外我们将银行办理业务的不同窗口定义为不同的数组,客户在各自窗口前排队成为队列,银行有N个窗口,各自为不同的数组,在各个窗口前,每个队列的队头是正在窗口办理业务的客户,队头出队的时刻就是客户离开银行的时刻,也即客户离开事件的发生,此时可以利用对有序表的处理引入下一名客户到达事件。
程序主要通过模拟银行一天从早到晚客户到达、客户入队、客户离开的事件来完成程序的功能。通过合理的随机模拟,模拟出一天内银行的总客户数、客户累计逗留时间、客户平均逗留的时间、一般业务与特殊业务的办理次数等。这些信息写入到文件中,便于查找和统计。
1.3 相关知识
(1) 对有序链表的构造和处理。
(2) 对离散事件的模拟,理解动态规划的有关概念。
(3) 对队列中元素的插入,删除等操作。
(4) 对文件的操作。
(5) 一定的MFC有关知识。
2 需求分析
2.1 课题调研
调研结果:
在实际生活中,人们经常去银行办理业务,如何能够正确有效地安排客户的顺序成为关键。所以程序应注重高效性。
另外,银行大多有办理不同种业务的窗口。银行开设的窗口有一般业务窗口和特殊业务窗口,特殊业务窗口往往较少。
客户办理业务时的情况分为两种,一种是客户到来时恰好有合适的办理业务的窗口,另一种是客户不全在办理业务(即等待)。银行办理一般业务与特殊业务的客户数量肯定会相差很多,一般业务往往比特殊业务的客户数多。因此,如果特殊窗口无人办理业务,可以让办理一般业务的客户去特殊窗口办理。另外,客户叫号后几乎没有在等待过程中离开的,因此程序中忽略客户叫号后离开不办理业务的情形。
2.2 用户需求分析
用户分为两类:银行客户和银行职员。程序的用户操作端分为客户端和银行端,客户端由银行客户操作,银行客户通过客户端叫号,等待或者去窗口办理业务(程序中的客户到达时间由此决定,这些操作通过随机模拟实现);银行端由银行窗口的职员操作,主要办理业务、设置业务窗口数(程序中的客户离开时间由此决定,这些操作也通过模拟实现)。银行端还可以对当日的业务人数以及办理的时间等信息进行统计和分析,将其与之前模拟的信息比较,得出有关的业务情况。
3 方案设计
3.1 总体功能设计
经过讨论,我们决定程序总体可以实现以下功能:
(1) 能够模拟银行一天内的客户办理业务的事件。
(2) 统计银行业务的客户数、办理业务的时间等信息。
(3) 可以查询以往模拟的信息。
(4) 可以实现数据的输入来更改其中的信息。
(5) 当前窗口的人员分布情况动态显示。
(6) 界面要求达到有合理的提示,根据提示可以完成相关功能要求。
具体分析:程序主要通过模拟银行一天从早到晚客户到达事件、离开的时刻、逗留时间,可通过保存、查询、打印以上信息,来完成程序的功能。通过合理的随机模拟,模拟出一天内银行的总客户数、客户累计逗留时间、客户平均逗留的时间、一般业务与特殊业务的办理次数,一般业务与特殊业务的办理人数和逗留时间。这些信息写入到文件中,用户可以再次查询、打印、统计。并且,在应用MFC之后,这些有关数据可以在界面中得以直观显示。以下是可视化UML图:
基于MFC可视化的UML类图
以下是银行业务模拟的相应流程图:
3.2 数据结构设计
程序的数据结构包括链表、队列和有关的数组等。 事件链表记录客户的事件,窗口队列记录客户排队的信息,记录信息链表将所有的客户信息按照不同的类别保存到链表中。银行窗口q[5]是一个数组,保存窗口队列头结点。
//事件结构体
typedef struct Event_Node{
int OccurTime; //事件发生时刻
int NType; //事件类型,-1表示到达,0-4代表在那个窗口离开
int type; //0-4
Event_Node *next; //指向下一个
}Event_Node, *Event; //事件类型,有序链表LinkList的数据元素类型
//队列结构体
typedef struct Queue_Node{
int ArrivalTime; //到达时刻
int Duration; //办业务持续时间
int type; //客户的真实业务类型0-4
Queue_Node *next; //指向下一个结点
}Queue_Node, *Queue, LinkList_Node, *LinkList; //队列的数据元素类型
//客户信息记录链表
typedef struct EndLinkList_node{
int OrderNum; //编号
int ArrivalHour; //到达小时数
int ArrivalMin; //到达分钟数
int DepartureHour; //离开小时数
int DepartureMin; //离开分钟数
EndLinkList_node *next;
}EndLinkList_Node,*EndLinkList;
3.3 函数原型设计
事件结点类成员函数原型:
OrderInsertEv(Event_Node *e); //按序插入事件表中
EmptyEve(); //判断事件表是否为空
DeletefirEve(); //删除事件表的第一个结点
队列结点类成员函数原型:
OutQueue(); //出队列函数
EnQueue(Queue_Node *p); //入队列函数
EmptyQue(); //判断队列是否为空
Queue_Node* Getfirnode(); //获取队列第一个结点
QueueLength(); //队列的长度
信息记录结点类成员函数原型:
Record_Linklist();
事件类成员函数原型:
Simulate();
CustomArrived(); //客户到达事件函数
CustomerDeparture(); //处理客户离开事件,en.NType>0
Bank_Simulation(); //银行业务模拟函数
OpenForDay(); //准备函数
InitAll(); //初始化函数
Random(int &durtime, int &intertime, int &type); //随机函数
Minimum(); //查找最小队列
RecordLinkList(int i, int j); //记录客户的信息链表,分别记录不同窗口的信息
ChangeForm(); //客户信息转化函数
Output(); //输出模拟完的结果
Print_former(); //输出以前信息菜单函数
Print_current(); //输出当前信息菜单函数
StoreFile(); //永久保存到文件中
StoreTotempfile(); //保存到临时文件中
3.4 主算法设计
//初始化函数
void InitAll()
{
TotalTime = 0; //总时间
CustomerNum = 0; //总人数
spetotaltime = 0; //办理特殊业务的总逗留时间
comtotaltime = 0; //办理一般业务的总逗留时间
EV = new Event_Node; //事件表创建头结点
//初始化三种链表以及队列
InitQueue(q[i]); //i = 0,1,2,3,4
a[j] = new LinkList_Node;
b[k] = new EndLinkList_Node; //k =0,1,2,3,4,5
}
//客户到达事件函数
void CustomArrived()
{
int durtime; //逗留时间
int intertime; //下一个客户到达的时间
int type; //窗口类型
int t; //记录下一个客户到达的时刻
Event e; //事件变量
Event temp = new Event_Node;
Queue s = new Queue_Node; //队列结点变量
int i; //最小的队列的下标
Random(durtime, intertime, type); //产生随机值:逗留时间,下一个客户到达时间,以及下一个客户的业务类型
t = en->OccurTime + intertime; //下一个客户到达的具体时间
if(t < CloseTime)
{
++CustomerNum; //号数增加
TotalTime += durtime;
e = CreateEvNode(t, -1, type); //构造下一个客户的事件
OrderInsert(e); //插入到事件链表中
s->ArrivalTime = en->OccurTime; //当前事件的到达时间
s->Duration = durtime; //当前事件的逗留时间
s->next = NULL;
s->type = en->type;
if(en->type == 0)
{
i = 0;
spe++;
spetotaltime += durtime;
EnQueue(q[i], s);
} //如果当前结点的业务类型是特殊业务,则入到第0个队列里
else
{
i = Minimum(); //否则查找最小的队列
com++;
comtotaltime += durtime;
EnQueue(q[i], s); //将其插入到队列中
}
if(QueueLength(q[i]) == 1)
{
temp->NType = i;
temp->OccurTime = s->ArrivalTime + s->Duration;
temp->type = en->type;
temp->next = NULL;
OrderInsert(temp);
}
}
}
//处理客户离开事件,en.NType > -1
void CustomerDeparture()
{
int i = en->NType, j = en->type;
Queue p = new Queue_Node;
Event e = new Event_Node;
RecordLinkList(a[j], q[i]); //记录客户事件
outQueue(q[i]);
if(!(QueueEmpty(q[i]))) //判断事件表是否为空
{
p = GetHead(q[i]);
e->OccurTime = p->ArrivalTime + p->Duration;
e->NType = i;
e->type = p->type;
OrderInsert(e);
}
}
//银行业务模拟函数
void Bank_Simulation()
{
OpenForDay();
int i = 6;
while(!EmptyEventList())
{
en = EV->next;
DelFirstNode();
if(en->NType == -1) //判断事件类型
CustomArrived();
else
CustomerDeparture();
}
ChangeForm();
StoreTofile(); //将模拟的信息保存到文件中
Output();
}
3.5 用户界面设计
概要设计原理:
本实验基于MFC的界面的设计,是通过创建对话框类来实现,在窗口的控件中定义一些事件对象,来调用其成员函数模拟,信息查询需要创建一个子窗口,在此窗口中,需要使用MFC中的文件类来打开文件,然后将相应的模拟信息输出。最后回到主窗口,退出或者模拟。此件需要用到按钮控件,编辑框控件,还有显示框控件等控件。
4 方案实现
4.1 开发环境与工具
开发环境:windows 7
开发工具:VC++6.0
4.2 程序设计关键技术
根据我们的理解,本实验主要有以下要点:
(一) 如何对客户发生的事件处理。我们采用了将这些事件记录在有序的链表当中,将客户的到达,离开这些事件定义为结点,然后插入事件表中,构建事件表,然后对每一个事件进行处理,要么到达,要么入队列,要么离开。通过这种方式,我们不需要在产生客户事件的时候去不停的判断窗口的状态。这是一个动态的过程。
(二) 如何产生随机的事件,比如随机时间,随机逗留时间,随机窗口类型。我们在随机函数中设置了一个sleep()函数,用来暂停一段时间,让函数产生的随机数更准确,不重复。我们也设置了每两个客户之间的时间间隔。这些数据设置对产生的随机数有很大的影响。比如,如果我们将暂停时间设置得非常小,那么产生的随机数重复的概率就会大大的增加,以此我们不停的模拟,然后设置,还有是两个客户之间的时间间隔也会影响窗口的客户数量,我们模拟然后设置后发现将此间隔时间设置为10-15分钟之间时,客户在每个窗口的数量会比较均匀。
4.3 个人设计实现(按组员分工)
4.3.3侯永跃程序设计实现
main()
{
menu();
return 0;
}
//输出当前信息函数
void Print_current()
{
int i;
system("cls");
system("color 8a");
cout<<"\t\t\t\t输出本次模拟的客户信息\n\n\n"<<endl;
cout<<"0.输出本次模拟的特殊窗口的客户信息"<<endl;
cout<<"1.输出本次模拟的1号一般窗口的客户信息"<<endl;
cout<<"2.输出本次模拟的2号一般窗口的客户信息"<<endl;
cout<<"3.输出本次模拟的3号一般窗口的客户信息"<<endl;
cout<<"4.输出本次模拟的4号一般窗口的客户信息"<<endl;
cout<<"5.输出本次模拟的一整天的客户信息(客户编号,客户到达时间,客户离开时间)\n\n\n"<<endl;
cin>>i;
while(1)
{
if(i >= 0 && i <= 5)
{
switch(i)
{
case 0: Print_file(0); break;
case 1: Print_file(1); break;
case 2: Print_file(2); break;
case 3: Print_file(3); break;
case 4: Print_file(4); break;
case 5: Print_file(5); break;
}
break;
}
else
{
cout<<"你的输入有问题,请重新输入!"<<endl;
cin>>i;
}
}
}
//输出信息菜单
void Print_fun()
{
int i; //选择
system("cls");
system("color 9a");
cout<<"\t\t\t\t模拟的信息输出\n\n\n\n";
cout<<"\t\t\t1.以前模拟客户信息输出\t2.当前模拟客户信息输出\n\n\n";
cin>>i;
while(1)
{
if(i == 1)
{
//Print_former();
break;
}
else if(i == 2)
{
Print_current();
break;
}
else
{
cout<<"\t\t\t你的选择有误,请重新输入!"<<endl;
cin>>i;
}
}
}
//打印文件信息
void Print_file(int num) //打印文件信息,num代表文件的编号
{
FILE *fp;
char ch;
switch(num)
{
case 0: fp = fopen("0本次模拟特殊窗口客户信息文件.txt", "r"); break;
case 1: fp = fopen("1本次模拟1号一般窗口客户信息文件.txt", "r"); break;
case 2: fp = fopen("2本次模拟2号一般窗口客户信息文件.txt", "r"); break;
case 3: fp = fopen("3本次模拟3号一般窗口客户信息文件.txt", "r"); break;
case 4: fp = fopen("4本次模拟4号一般窗口客户信息文件.txt", "r"); break;
case 5: fp = fopen("5本次模拟客户所有信息文件.txt", "r"); break;
default: cout<<"\n\n\t\t\t你的的输入有问题,请重新输入!"; fclose(fp); return ;
}
if(fp == NULL)
{
cout<<"\t\t\t\t文件内容为空!"<<endl;
fclose(fp);
return ;
}
while(!feof(fp))
{
//cin.sync();
fscanf(fp, "%c", &ch);
putchar(ch);
}
fclose(fp);
}
//菜单显示
void menu()
{
system("cls");
system("color 9a");
int i;
cout<<"\t\t\t***********银行模拟系统***********\n\n\n\n\n";
cout<<"\t\t\t1.银行模拟"<<"\t\t2.打印信息"<<endl;
cin>>i;
switch(i)
{
case 1:
Bank_Simulation(); //模拟函数
Sleep(3000);
menu();
break;
case 2:
Print_fun();
Sleep(3000);
menu();
break;
default:
cout<<"您的输入有误!请重新输入:"<<endl;
Sleep(1000);
menu();
break;
}
}
//输出模拟完的结果
void Output()
{
system("cls");
system("color aa");
cout<<"\t\t\t\t模拟的结果显示如下:\n\n\n";
cout<<"*银行这一天7:00 -- 19:00,客户逗留时间和是:"<<TotalTime<<"分钟"<<endl;
cout<<"*这一天总的客户人数是:"<<CustomerNum<<endl;
cout<<"*平均每个客户逗留的时间是:"<<(double)TotalTime / CustomerNum<<"分钟"<<endl;
cout<<"*办理特殊业务的总人数是:"<<spe<<endl;
cout<<"zong shijian:"<<spetotaltime<<endl;
cout<<"*办理普通业务的总人数是:"<<com<<endl<<"zongshinjian: "<<comtotaltime<<endl<<endl;
system("pause");
}
//初始化函数
void InitAll()
{
TotalTime = 0;
CustomerNum = 0;
spetotaltime = 0; //办理特殊业务的总逗留时间
comtotaltime = 0; //办理一般业务的总逗留时间
EV = new Event_Node;
EV->next = NULL;
//初始化三种链表以及队列
for(int i = 0; i < 5; i++)
{
InitQueue(q[i]);
}
for(int j = 0; j < 6; j++)
{
a[j] = new LinkList_Node;
a[j]->next = NULL;
}
for(int k = 0; k < 6; k++)
{
b[k] = new EndLinkList_Node;
b[k]->next = NULL;
}
}
//创建事件结点
Event CreateEvNode(int OccurTime, int NType, int type)//创建结点
{
Event e;
e = new Event_Node;
if(!e)
return 0;
e->next=NULL;
e->OccurTime = OccurTime;
e->NType = NType;
e->type = type;
return e;
}
//初始化队列
void InitQueue(Queue &q)
{
q = new Queue_Node;
q->next = NULL;
q->ArrivalTime = 0;
q->Duration = 0;
}
4.3.2 李博然程序设计实现
//读取文件信息
int readFilec(har * filename)
{
int x = 0, y = 0; char line[szLINE]; int lines = 0;
FILE * f = fopen(filename, "r");
while(!feof(f)) {
memset(line, 0, szLINE);
fgets(line, szLINE - 1, f);
sscanf(line, "%d|%d", &x, &y);
//这里取到了x和y,用SetWindowText输出即可
printf("%d # \t = %d\n", x, y);
lines ++;
}//end while
fclose(f);
return lines;
}//end readFile:
void CustomArrived(); //客户到达事件函数
void Random(int &durtime, int &intertime, int &type); //随机函数
void StoreTofile(); //保存到文件中
//客户到达事件函数
void CustomArrived()
{
int durtime; //逗留时间
int intertime; //下一个客户到达的时间
int type; //窗口类型
int t; //记录下一个客户到达的时刻
Event e; //事件变量
Event temp = new Event_Node;
Queue s = new Queue_Node; //队列结点变量
int i; //最小的队列的下标
Random(durtime, intertime, type); //产生随机值:逗留时间,下一个客户到达时间,以及下一个客户的业务类型
t = en->OccurTime + intertime; //下一个客户到达的具体时间
if(t < CloseTime)
{
++CustomerNum; //号数增加
TotalTime += durtime;
e = CreateEvNode(t, -1, type); //构造下一个客户的事件
OrderInsert(e); //插入到事件链表中
s->ArrivalTime = en->OccurTime; //当前事件的到达时间
s->Duration = durtime; //当前事件的逗留时间
s->next = NULL;
s->type = en->type;
if(en->type == 0)
{
i = 0;
spe++;
spetotaltime += durtime;
EnQueue(q[i], s);
} //如果当前结点的业务类型是特殊业务,则入到第0个队列里
else
{
i = Minimum(); //否则查找最小的队列
com++;
comtotaltime += durtime;
展开阅读全文