正如标题所示的意思,该程序的目的是要模拟,银行客户办理业务所花费的平均时间。
该程序是事件驱动程序,按照事件驱动的思路进行。如此,首先需要建立一个事件列表,其中存储着按事件发生的时间顺序排列的事件,根据客户到银行的特点,可以发现,事件分为两种,1、客户到达银行事件。2、客户办理完事物离开银行事件。
为事件链表创建数据类型,数据类型我创建为结构体类型,因为对每个客户来说,一个客户到来就相当于是一个对应的事件,那么这个事件的数据类型就是该客户的基本特征,如此定义客户事件到来的时刻occursTime(可以是到来事件,也可以是离开事件),时间的类型。
创建银行办理业务的窗口队列,假设有四个。为队列添加数据类型,也为结构体类型,此时保存的是到来客户的到来时间arrividTime客户业务处理所要花费的时间durTime,客户排队需要花费的时间waiteTime,
接下来是创建处理这两种事件的事件处理方法,
对客户到来事件处理方法customerArrived():
1)、新客户到达银行事件的预测发生时间与该客户的时间间隔interTime,
2)、生成当前事件对应客户处理业务需要花费的事件
3)、将该事件的信息放入最短的队列中
4)、如果该队列只有该客户一个信息那么就给该客户添加一个离开事件,并添加到事件链表中。
对客户离开事件处理方法CustomerDeparture():
1)、计算总时间
2)、删除该离开事件对应的队列中的客户信息
3)、如果删除完这个信息后该队列后面仍有其他信息,那么就为该信息添加离开事件,并添加到事物链表中
上述是该事件驱动程序的核心方法,自然还有一些其他的方法,比如要是时间链表中的时间按时间顺序排列,那么就需要添加一个排序方法。在模拟程序执行中需要有初始化方法。等等其他直接上代码
以下是事件驱动程序的原理图:
下面分享我的源代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace ListTable
{
class Bank_Simulation
{
public struct _event
{
public int occurTime;//事件的开始时刻
public int nType;//事件类型
};//定义事件的结构类型
public struct _queue
{
public int arrivalTime;//该顾客要离开的时刻,即为occurTime+durTime
public int durTime;//顾客到达处理业务所需要的时间
public int waiteTime;//排队等待时间
};
//创建事件链表
private LinkList<_event> ev = new LinkList<_event>();
//创建队列
private QueueLink<_queue>[] qu = new QueueLink<_queue>[4];
private int[] timer = new int[4];//创建数组用来对应个队列 当前下一个要来的客户 需要排队等待的时间
private _event en;//用来承接当前处理事件
private int closeTime;//银行关门时间
private int totalTime;//顾客的总时间
private int customerNum;//顾客数量
public Bank_Simulation(int closeTime=50,int totalTime=0,int customer=0)
{
this.closeTime = closeTime;
this.totalTime = totalTime;
this.customerNum = customer;
en = new _event();
for(int i = 0; i < 4; i++)
{
qu[i] = new QueueLink<_queue>();
timer[i] = 0;
}
}
//本程序是事件驱动程序,故而函数的核心是围绕着事件列表处理的
//创建程序的主函数
public void BankSimulation()
{
Console.WriteLine("今日银行办理业务客户平均时间为:");
//执行初始化函数
OpenForDay();
while (!ev.IsEmpty())
{
en = ev[0];//获取事件链表的头结点
ev.DeleteAtIndex(0);//同时删除该结点
if (en.nType == 0)//如果此事件类型为0,即客户到来事件
{
//执行事件到来处理函数
CustomerArrived();
}
else//否则 即该事件为客户离开事件
{
//执行客户离开的时间处理函数
CustomerDeparture();
Console.WriteLine("客户离开事件执行");
}
Console.WriteLine("当前事件链表中事件个数:"+ ev.GetLength());
}
//计算输出最终的计算结果
Console.WriteLine(totalTime.ToString() );
Console.WriteLine(customerNum.ToString());
Console.WriteLine(totalTime/customerNum);
}
//初始化函数
void OpenForDay()
{
//主要的目的是初始化一些基本的数据,时间表,队列,
//同时将预测的第一个客户到达事件添加到时间表中
_event temp = new _event();
temp.occurTime = 0;//规定第一个客户到达的时刻为0
temp.nType = 0;//初始化事件类型为到达事件
ev.Add(temp);
//程序要求 事件表中的数据是按照发生时刻的先后顺序排列的 所以应该执行一个排序方法,
//但是此处是初始化 就一个结点所以就没必要了
}
//客户到达事件处理函数
void CustomerArrived()
{
//主要功能如下
//将顾客数量加1
//执行客户达到,应该完成的操作
//预见距离下一个客户到达的时间间隔intertime,生成当前客户的业务处理花费时间durtime
//将预见的下一个客户到达事件 添加到事件表中
//将当前客户添加到最短的队列中(即把客户必要的信息传递到队列中),
//同时判断当前客户是否处于队首 若处于队首,那么就将为客户 设置离开事件 并添加到事件列表中
customerNum++;
Random rd = new Random();
int interTime = rd.Next(1, 10);
int durtime = rd.Next(1, 10);
int t = en.occurTime + interTime;
//创建预见的客户将到达的事件 并添加到事件表中
if (t < closeTime)
{
_event temp1 = new _event();
temp1.occurTime = t;
temp1.nType = 0;
ev.Add(temp1);
Sorting(ev);
Console.WriteLine("客户到达事件执行");
}
//当前事件处理
//当前事件客户添加到 队列中
_queue temp2 = new _queue();
temp2.arrivalTime = en.occurTime;//客户离开时间
temp2.durTime = durtime;
//判断4个队列中最短的队列
int i = MinQu(qu);
temp2.waiteTime = timer[i];//该用户插入该队列要排队等待的时间
qu[i].EnQueue(temp2);//该到达客户信息插入队列
timer[i] += temp2.durTime;//计算当下一个客户到来时要等待的时间
if (qu[i].GetLength() == 1)
{//长度为1 为队首 添加离开事件 同时添加到事件列表中
Console.WriteLine("添加离开事件");
_event temp3 = new _event();
temp3.occurTime = qu[i].GetHead().arrivalTime + qu[i].GetHead().durTime+qu[i].GetHead().waiteTime;
temp3.nType = i;
ev.Add(temp3);
//此时应该有对事件表进行排序的算法
Sorting(ev);
}
}
private int MinQu(QueueLink<_queue>[] q)
{
int minLength = 0;
for(int i = 0; i < 3; i++)
{
if (q[minLength].GetLength() > q[i + 1].GetLength())
{
minLength = i + 1;
}
}
return minLength;
}
private void Sorting(LinkList<_event> eve)
{//冒泡排序法
for(int j = 0; j < eve.GetLength() - 1; j++)
{
for(int i = 0; i < eve.GetLength() - 1 - i; i++)
{
if (eve[i].occurTime > eve[i + 1].occurTime)
{
eve.ChangeAtIndex(i, i + 1);//调用我内置到链表函数的交换方法
}
}
}
}
//执行客户离开事件
private void CustomerDeparture()
{
totalTime += en.occurTime - qu[en.nType].GetHead().arrivalTime;
Console.WriteLine("当前队列长度" + qu[en.nType].GetLength());
timer[en.nType] -= qu[en.nType].GetHead().durTime;//当该队头的客户业务办理完了要走了,那么下一个要到来的客户等待时间就应该减去他 办理业务的时间
qu[en.nType].DeQueue();//删除该离开事件对应的队列中的客户
if (!qu[en.nType].IsEmpty())
{//若该队列不为空 那么将队首元素设置一个离开事件
Console.WriteLine("当前队列长度:" + qu[en.nType].GetLength());
_event temp = new _event();
temp.occurTime = qu[en.nType].GetHead().arrivalTime + qu[en.nType].GetHead().durTime+qu[en.nType].GetHead().waiteTime;//求得该事件的离开时刻
temp.nType = en.nType;
ev.Add(temp);
Sorting(ev);
}
}
}
}