You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
text2/README.md

523 lines
19 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 银行业务模拟系统
2023.6.18
刘彩月,刘梦琦,金雨佳,赵亚璇
摘要:针对银行营业的工作记录和计算一天中客户在银行的逗留时间的问题。实现了模拟客户排队,窗口,管理客户,队列的功能。为了有效的存储和处理客户(客户到达,客户离开)采用有序表,其主要操作是插入和删除事件,用一个单链表表示。为了有效存储和处理窗口( 客户到达时间,离开时间)采用了队列。为了解决客户找到最短等待队列使用了排序和插入算法为了解决客户离开事件使用了删除算法。删除和插入的算法效率为On。针对银行开门关门营业窗口数目客户到达时间采用人机交互。完成了保存银行营业的工作记录储存客户的到达时间和离开时间显示出在某一天整个银行系统中客户在银行逗留的平均时间。
项目开发过程中采用Kanban (看板)进行任务管理和分工协作并使用Git对程序代码和文档进行版本管理。
任务分工及完成情况:
|任务|设计|开发|测试|文档|
|-----|----|----|----|----|
| 1和4| 刘彩月 | 刘彩月 | 赵亚璇 | 刘彩月 |
| 2.1 | 赵亚璇 | 赵亚璇 | 金雨佳 | 赵亚璇 |
| 2.2和2.3 | 金雨佳 | 金雨佳 | 刘梦琦 | 金雨佳 |
| 3 | 刘梦琦 | 刘梦琦 | 刘彩月| 刘梦琦 |
|代码|金雨佳|刘梦琦|赵亚璇|刘彩月|
每个成员的工作量(百分比):
|刘彩月|赵亚璇|金雨佳|刘梦琦|
|------|-----|-----|-----|
| 25 | 25 | 25 | 25 |
# 1.系统分析
## 1.1问题描述
银行有n个窗口对外接待客户从早晨银行开门起不断有客户进入银行。由于每个窗口在某时刻只能接待一个客户因此在客户人多时需分别在各个窗口前排队。对于刚进入银行的客户如果某个窗口的营业员正在空闲则可上前办理业务反之若n个窗口均有客户正在办理业务新来的客户便会排在人数最少的队伍后面。
设计一个银行业务模拟系统,模拟银行的业务运行并计算一天中客户在银行逗留的平均时间。
1通过人机互交的方式设定程序所需要的参数银行的开门时间和关门时间营业窗口数目。
2客户的到达时间可通过人机互交文件导入或随机产生的方式输入。
3保存银行营业的工作记录储存客户的到达时间和离开时间。
4显示出在某一天整个银行系统中客户在银行逗留的平均时间。
要求系统运行正常,功能完整;数据结构使用得当,算法有较高的效率;代码规范,可读性高,结构清晰,具有一定的健壮性,可靠性和可维护性。
## 1.2可行性分析
运用了单链表和链队列,运用了排序,插入和删除的算法。
客户排队的过程是一个按照到达时间先到先接受服务的过程这一过程可以通过队列实现。n个不同的窗口对应n个队列队列中每一个元素对应一个客户。
算法中处理的事件有两类:一类是客户到来事件,另一类是客户离开事件。客户到来事件发生的时刻随客户到来自然形成,客户到来后,使用排序算法找到最短的等待队列,将客户插入队列;客户离开事件发生的时刻由银行窗口为其办理业务的完成时间决定,业务办理完成后,客户从等待队列中出队,窗口为下一个客户(如果存在的话)服务。
本项目涉及的对象主要包括客户、银行窗口,可以设计两个数据结构实现客户和窗口的功能。此外,管理客户、队列的功能可以单独实现(类似银行进门处的接待员,引导客户在哪里排队)。
## 1.3 需求分析
### 1输入和输出
主要输入:银行的营业时间
主要输出:根据随机数输出实时银行运行状态;输出在银行营业时间内客户在银行逗留的平均时间。
随机数:
1客户办理业务的时间当前客户
2当前客户和下一个客户到达的间隔时间
### 2数据字典
描述系统中需要处理的所有数据包含的具体信息。例如:
OccurTime表示事件发生的时间
NType表示客户的状态办理业务中或离开
ArrivalTime表示客户到达时间
CustomerNum记录客户的数目
CloseTime银行关门时间
durtime客户办理业务的时间
每个客户在银行的等待时间取决于队列里前一个节点的离开时间,而不是自己的到达时间+服务时间即en.OccurTime + intertime;
在银行营业时间内总的办理业务的时间TotalTime += en.OccurTime - customer.ArrivalTime;
### 3数据文件
1当前客户的柜台被服务时间当前客户和下一个客户到达的间隔时间用随机产生的方式输入。
2保存银行营业的工作记录储存客户的到达时间和离开时间。
3显示出在某一天整个银行系统中客户在银行逗留的平均时间。
### 4参数设定
提示信息:请输入银行的营业时间(min)
用户输入:银行的营业时间(min)
### 5链表功能
该功能的主要作用是:给客户分配银行窗口
### 6链队列功能
该功能的主要作用是是:让客户有序高效办理业务
# 2. 系统设计
## 2.1 概要设计
主程序的流程\n主程序先是让外部进行测试数据输入待测试数据输入完后执行银行业务模拟系统产生需要取款的客户人数成功办理存款的客户人数成功办理取款的客户人数存款成功办理率取款成功办理率客户逗留平均时间银行当前余额等信息。
本设计中用到的数据结构ADT定义如下
```
ADT Queue{
数据对象D{ ai | ai∈ElemSet, i=1,2,...,n, n≥0 }
数据关系R1{ <ai-1, ai>|ai-1, ai∈D, i=2,...,n }
基本操作:
void InitQueue(Queue &Q);
操作结果构造空队列Q
CustNode *Queuefront(Queue &Q);
初始条件队列Q存在
操作结果:返回队首元素
CustNode *Queuerear(Queue &Q);
初始条件队列Q存在
操作结果:返回队尾元素
void EnQueue(Queue &Q,int e);
初始条件队列Q存在
操作结果插入元素e为Q的新的队尾元素。
void DeQueue(Queue &Q);
初始条件队列Q存在
操作结果删除Q的队头元素。
}ADT Queue
```
## 2.2 数据结构设计
首先,分析对比几种可选的数据结构设计方案。如图可以采用邻接矩阵,也可以采用邻接表,表示集合可以用普通的查找表,还可以用不相交集。给出每一种设计方案的特点(优势、不足等)。然后,综合考虑各种因素(空间、时间、乃至团队成员的水平等),给出你的选择。
### 1xxx结构
给出核心数据结构的设计,包括文字描述和示意图。讲清楚数据是如何组织的。多个数据结构,逐一列出。
### 2xxx结构
给出核心数据结构的设计,包括文字描述和示意图。讲清楚数据是如何组织的。
## 2.3 算法设计
首先,分析对比几种可选的算法设计方案。如是否排序,广度优先或深度优先搜索等。给出每一种设计方案的特点(优势、不足)。然后,综合考虑各种因素(空间、时间、乃至团队成员的水平等),给出你的选择。
### 1XXX算法
给出核心算法的设计,包括伪代码或流程图。多个核心算法,逐一列出。只列举解决问题的核心算法,重点讲清楚是如何解决问题的。
### 2XXX算法
给出核心算法的设计,包括伪代码或流程图。
# 3. 系统实现
语言:C语言
开发工具:VS Code
采用模块化设计,通过大量的函数实现,银行开门、客户进门、客户离开等功能进而统计一天内客户在银行逗留的平均时间
## 3.1 核心数据结构的实现
首先通过有序链表,实现一部分功能如:先初始化有序表在第i个位置之前插入元素e删除链表中第一个结点并以q返回已知p指向线性链表中的一个结点返回p所指结点中元素的值等等。其次通过队列实现入队和出队。
```
typedef struct
{
int OccurTime;//事件发生时刻
int NType;//事件类型0表示到达事件1-4表示四个窗口的离开事件
}Event, ElemType;
typedef struct LNode
{
ElemType data;
struct LNode* next;
}LNode, * LinkList;
typedef LinkList EventList;
typedef struct
{
int ArrivalTime;//到达时刻
int Duration;//办理事务所需事件
}QElemType;
typedef struct QNode
{
QElemType data;
struct QNode* next;
}QNode, * QueuePtr;
typedef struct
{
QueuePtr front;//队头指针
QueuePtr rear;//队尾指针
}LinkQueue;
EventList ev;//事件表
Event en;//事件
LinkQueue q[5];//四个客户队列
QElemType customer;//客户记录
int TotalTime, CustomerNum, CloseTime;
```
链表
【1】通过一个个指针将节点串起来
【2】对于元素的随机访问需要使用计数器来访问指定的元素并且只能从头节点开始访问每访问一个节点计数器加1直到给定的“下标”
【3】增加元素和删除元素的效率很高
队列
【1】先进先出
【2】线程池中的线程就是从任务队列中取出任务
## 3.2 核心算法的实现
```
Status InitList(LinkList& L)//链表初始化
{
L = (LinkList)malloc(sizeof(LNode));
if (!L)
{
exit(OVERFLOW);
}
L->next = NULL;
return OK;
}
Status ListInsert_L(LinkList& L, int i, ElemType e)//在第i个位置之前插入元素e
{
LinkList p = L;
int j = 0;
while (p && j < i - 1)//注意是i-1,因为要找被插入元素的前一个元素
{
p = p->next;
j++;
}
if (!p || j > i - 1)
{
return ERROR;
}
LinkList s = (LinkList)malloc(sizeof(LNode));
if (!s)
{
exit(OVERFLOW);
}
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
Status ListEmpty(LinkList L)//判断链表是否为空
//空表头指针和头结点仍然存在但头结点指向NULL
{
if (L->next)
{
return FALSE;
}
else
{
return TRUE;
}
}
Status DelFirst(LinkList L, LNode*& q)//删除链表中第一个结点并以q返回
{
if (!L->next)
{
return ERROR;
}
q = L->next;
L->next = q->next;
return OK;
}
LNode* GetHead(LinkList L)//返回链表头结点
{
return L;
}
ElemType GetCurElem(LNode* p)//已知p指向线性链表中的一个结点返回p所指结点中元素的值
{
return p->data;
}
void PrintEventList()//打印事件链表 
{
printf("Current Eventlist is:\n");
ListTraverse(ev);
}
Status ListTraverse(LinkList& L) //遍历链表  
{
LNode* p = L->next;
if (!p) {
printf("List is empty.\n");
return ERROR;
}
while (p != NULL) {
printf("OccurTime:%d,Event Type:%d\n", p->data.OccurTime, p->data.NType);
p = p->next;
}
printf("\n");
return OK;
}
Status InitQueue(LinkQueue& Q)//链队列的初始化
{
Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
if (!Q.front)
{
exit(OVERFLOW);
}
Q.front->next = NULL;
return OK;
}
Status EnQueue(LinkQueue& Q, QElemType e)//入队
{
QNode* p = (QueuePtr)malloc(sizeof(QNode));
if (!p)
{
exit(OVERFLOW);
}
p->data = e;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
return OK;
}
Status DeQueue(LinkQueue& Q, QElemType& e)//出队
{
if (Q.front == Q.rear)
{
return ERROR;
}
QNode* p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)//注意这里要考虑到,当队列中最后一个元素被删后,队列尾指针也丢失了,因此需对队尾指针重新复制(指向头结点)
{
Q.rear = Q.front;
}
free(p);
return OK;
}
int QueueLength(LinkQueue Q)//返回队列的长度
{
int count = 0;
QNode* p = Q.front->next;
while (p) {
p = p->next;
count++;
}
return count;
}
Status GetHead(LinkQueue Q, QElemType& e)//获取队头元素
{
if (Q.front == Q.rear)
{ return ERROR;}
e = Q.front->next->data;
}
Status QueueEmpty(LinkQueue Q)//判断队列是否为空
{
if (Q.front == Q.rear)
{
return TRUE;
}
return FALSE;
}
void PrintQueue()//打印队列
{
//打印当前队列  
int i;
for (i = 1; i <= 4; i++) {
printf("窗口 %d 有 %d 个客户:", i, QueueLength(q[i]));
QueueTraverse(q[i]);
}
printf("\n");
}
Status QueueTraverse(LinkQueue Q)//遍历队列Q  
{
QNode* p = Q.front->next;
if (!p) {
printf("--Is empty.\n");
return ERROR;
}
while (p) {
printf("(到达时刻 %d min 办理业务需要花费 %d min) ", p->data.ArrivalTime, p->data.Duration);
p = p->next;
}
printf("\n");
return OK;
}
```
# 4.系统测试
```
请输入银行的营业时间(min)30
********action********
一个新客户在银行营业 0min后进来办理业务花费了25min下一个客户过了 1min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 0 个客户:--Is empty.
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:1,Event Type:0
OccurTime:25,Event Type:1
********action********
一个新客户在银行营业 1min后进来办理业务花费了 6min下一个客户过了 7min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 1 min 办理业务需要花费 6 min)
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:7,Event Type:2
OccurTime:8,Event Type:0
OccurTime:25,Event Type:1
********action********
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 0 个客户:--Is empty.
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:8,Event Type:0
OccurTime:25,Event Type:1
********action********
一个新客户在银行营业 8min后进来办理业务花费了 6min下一个客户过了 7min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 8 min 办理业务需要花费 6 min)
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:14,Event Type:2
OccurTime:15,Event Type:0
OccurTime:25,Event Type:1
********action********
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 0 个客户:--Is empty.
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:15,Event Type:0
OccurTime:25,Event Type:1
********action********
一个新客户在银行营业15min后进来办理业务花费了25min下一个客户过了 0min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:15,Event Type:0
OccurTime:25,Event Type:1
OccurTime:40,Event Type:2
********action********
一个新客户在银行营业15min后进来办理业务花费了 5min下一个客户过了 7min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 1 个客户:(到达时刻 15 min 办理业务需要花费 5 min)
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:20,Event Type:3
OccurTime:22,Event Type:0
OccurTime:25,Event Type:1
OccurTime:40,Event Type:2
********action********
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 0 个客户:--Is empty.
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:22,Event Type:0
OccurTime:25,Event Type:1
OccurTime:40,Event Type:2
********action********
一个新客户在银行营业22min后进来办理业务花费了13min下一个客户过了 3min后进来
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 1 个客户:(到达时刻 22 min 办理业务需要花费 13 min)
窗口 4 有 0 个客户:--Is empty.
Current Eventlist is:
OccurTime:25,Event Type:0
OccurTime:25,Event Type:1
OccurTime:35,Event Type:3
OccurTime:40,Event Type:2
********action********
窗口 1 有 1 个客户:(到达时刻 0 min 办理业务需要花费 25 min)
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 1 个客户:(到达时刻 22 min 办理业务需要花费 13 min)
窗口 4 有 1 个客户:(到达时刻 25 min 办理业务需要花费 27 min)
Current Eventlist is:
OccurTime:25,Event Type:1
OccurTime:35,Event Type:3
OccurTime:40,Event Type:2
OccurTime:52,Event Type:4
********action********
窗口 1 有 0 个客户:--Is empty.
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
窗口 3 有 1 个客户:(到达时刻 22 min 办理业务需要花费 13 min)
窗口 4 有 1 个客户:(到达时刻 25 min 办理业务需要花费 27 min)
Current Eventlist is:
OccurTime:35,Event Type:3
OccurTime:40,Event Type:2
OccurTime:52,Event Type:4
********action********
窗口 1 有 0 个客户:--Is empty.
窗口 2 有 1 个客户:(到达时刻 15 min 办理业务需要花费 25 min)
Current Eventlist is:
List is empty.
The Average Time is 15.285714
```
# 5. 总结
本项目是模拟银行业务的相关问题,通过本次项目我们用单链表和链队列的相关知识完成了银行客户的排队问题。提高了我们的团队协作能力和问题分析能力。
遇到的问题:代码的实现问题
解决方法:通过查阅严蔚敏, 吴伟民的数据结构和B站的视频和代码来理解项目的过程。
个人小结:
成员1刘彩月:
经过这次数据结构的实训,我认识到了数据结构和生活应用的联系。用数据结构中的队列和单链表的知识,可以模拟解决银行的排队问题。同时我也感受到了个人技术和团队合作的重要性,在学习数据结构的同时,我应该联合生活中的一些具体的问题,联系具体问题来巩固数据结构的相关知识;通过数据结构解决生活中的相关问题。努力提高自己的个人技术,提高自己的编程能力。良好的团队合作往往可以达到事半功倍的效果,我还应该注重团队合作的重要性。和队员之间相互合作,相互信任等都是团队合作中最基础的。
成员2金雨佳
成员3赵亚璇
成员4刘梦琦
通过这次数据结构实训,让我意识到了团队协作的重要性,运用数据结构的知识模拟银行系统,更加熟练地掌握单链表和队列的应用,将所学的知识用在实践项目上时我此次实训的最大收获,在未来的生活中,我要努力学习技术,将知识联系到实际生活当中,把所学用到所用。
# 参考文献
[1] 严蔚敏, 吴伟民. 数据结构C语言版. 北京: 清华大学出版社, 2007.
[2]回到唐朝当少爷 https://www.bilibili.com/read/cv15955830?from=search&spm_id_from=333.337.0.0