佇列應用之銀行排隊 離散事件模擬
阿新 • • 發佈:2018-11-27
package demo; import impl.LinkedQueue; import org.omg.CORBA.DynAnyPackage.Invalid; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 佇列的應用之離散事件模擬 * 假設某銀行有4個視窗,每個視窗某一時刻只能接待一個客戶,因此客戶需要在每個視窗前順次排隊 * 對於剛進入銀行的客戶 如果某個視窗的業務員正空閒,則可上前辦理業務 * 若4個視窗均有客戶,他便會排在人最少的視窗隊尾 * 要求:計算一天中 客戶在銀行逗留的平均時間 * 平均時間等於=(每個客戶離開銀行的時間-每個客戶進入銀行的時間)之和 / 客戶的人數 * 這裡,將客戶進入銀行 和離開銀行 這兩個時刻發生的事情稱為事件 * 這一程式也稱為 事件驅動 * <p> * 演算法: * 事件型別:這裡只有五種事件型別, 0-客戶到達 1-A視窗客戶離開 2-B視窗客戶離開 3-B視窗客戶離開 4-B視窗客戶離開 * 到達的客戶事件,包括事件型別,到達時間 * 離開的客戶事件,包括事件型別,離開時間 * <p> * 處理客戶到達事件,新來的客戶,只會插入到四個視窗佇列中,最短的那個。同時 若銀行未關門 則生成一個新的客戶到達事件,該事件發生在前一個客戶到達事件之後 * 如果插入的佇列為空 則再產生一個客戶離開事件 * 佇列元素中包含,到達時間,辦事所需時間 * <p> * <p> * 處理客戶離開事件,根據事件型別 從相應的視窗佇列頭,將事件出隊,統計逗留時間,如果佇列不為空,則再產生一個客戶離開事件 * <p> * 所以 這裡使用兩個資料結構,事件佇列,視窗佇列 */ public class Simulation { private static Long TotalTime; //總逗留時間 private static int CustomerNum;//客戶總數 private static Long CloseTime = 18 * 3600L;//銀行關門時間 下午6點時刻的秒數 static class Event { //事件類 元素 private int eventType;//事件型別 private Long time;//事件發生的時間 } static class QEvent { //排隊的佇列元素 private Long ArrivalTime; //到達時間 private int Duration;//辦事所需的時間 } static class QueueWindow { private int windowNum;//視窗號 private LinkedQueue<QEvent> WindowQueue;//該視窗的排隊佇列 } private static LinkedQueue<Event> eventQueues = new LinkedQueue<Event>();//事件佇列 private static LinkedQueue<QEvent>[] queues = new LinkedQueue[3]; //4個視窗的客戶佇列 public static void OpenForDay() { //初始化客戶人數和逗留時間為0 TotalTime = 0L; CustomerNum = 1; //生成第一個客戶到達事件 Event event = new Event(); long rad= (long)( Math.random() * 30 * 60L); event.time = 8 * 3600L +rad; //8點開門 30分鐘內隨機來一名客戶 System.out.println("生成第一個客戶到達事件,該客戶為8點"+rad/60+"分到來"); event.eventType = 0; eventQueues.InitQueue(); //初始化事件佇列 eventQueues.EnQueue(event); //發生的事件進入事件佇列內 //初始化視窗佇列 for (int i = 0; i < queues.length; i++) { LinkedQueue<QEvent> qEventLinkedQueue = new LinkedQueue<QEvent>(); qEventLinkedQueue.InitQueue(); queues[i] = qEventLinkedQueue; } } public static char getEventType() { if (eventQueues.GetHead().eventType == 0) { return 'A'; } else { return 'D'; } } /** * 找出四個正在排隊的佇列中 最短的佇列 * * @return */ private static QueueWindow getShortestQueue() { int max = 0; int maxTag = 0; for (int i = 0; i < queues.length; i++) { if (queues[i].QueueLength() >= max) { max = queues[i].QueueLength(); maxTag = i; } } QueueWindow queueWindow = new QueueWindow(); queueWindow.windowNum = maxTag; queueWindow.WindowQueue = queues[maxTag]; return queueWindow; } /** * 處理客戶到達事件 * <p> * 處理客戶到達事件,新來的客戶,只會插入到四個視窗佇列中,最短的那個。 * 同時 若銀行未關門 則生成一個新的客戶到達事件,該事件發生在前一個客戶到達事件之後 * 如果插入的佇列為空 則再產生一個客戶離開事件 * 佇列元素中包含,到達時間,辦事所需時間 */ public static void CustomerArrived() { //原先事件佇列裡面的事件出隊 Event event = eventQueues.DeQueue(); //生成排隊事件 QEvent qEvent = new QEvent(); qEvent.ArrivalTime = event.time; qEvent.Duration = 5 * 60 + (int) (Math.random() * 20 * 60); //辦事所需時間 是5~20分鐘以內 //找出最短的視窗佇列 QueueWindow queueWindow = getShortestQueue(); //判斷這個佇列是否為空,是的話再產生一個客戶離開事件 if (queueWindow.WindowQueue.isEmpty()) { Event leaveEvent = new Event(); leaveEvent.time = qEvent.ArrivalTime + qEvent.Duration; //客戶的離開時間,等於其到達的時間+辦事的時間 leaveEvent.eventType = queueWindow.windowNum; eventQueues.EnQueue(leaveEvent); } //視窗事件入隊 queueWindow.WindowQueue.EnQueue(qEvent); //產生新的客戶到達事件 Event newEvent = new Event(); newEvent.eventType = 0; newEvent.time = event.time + 10 * 60L + (long) (Math.random() * 20 * 60L); //前一個客戶來到之後 10~30分鐘內隨機來一名客戶 if (newEvent.time >= CloseTime) { return; //終止產生新的客戶到達事件 } eventQueues.EnQueue(newEvent); //新的到達事件入隊 CustomerNum++;//客戶數+1 } /** * *處理客戶離開事件,根據事件型別 從相應的視窗佇列頭,將事件出隊,統計逗留時間 * 如果佇列不為空,則再產生一個客戶離開事件 * * @param */ private static void CustomerDeparture() { Event event = eventQueues.DeQueue(); QEvent qEvent = queues[event.eventType].DeQueue();//對應視窗佇列的排隊事件出隊 TotalTime = TotalTime + (event.time - qEvent.ArrivalTime);//逗留時間 等於離開事件發生時間減去到達時間 //如果該視窗不為空 則繼續產生下一個客戶離開事件 if (!queues[event.eventType].isEmpty()) { Event leaveEvent = new Event(); leaveEvent.eventType = event.eventType; leaveEvent.time = event.time + queues[event.eventType].GetHead().Duration;// 新的客戶離開時間 等於上一個客戶離開的時間+該客戶自己的辦事時間 eventQueues.EnQueue(leaveEvent); } } public static void main(String[] args) { OpenForDay();//初始化 開始一天 while (!eventQueues.isEmpty()) { //事件列表不為空時候 switch (getEventType()) { //事件型別 case 'A': CustomerArrived(); break; //處理客戶到達事件 case 'D': CustomerDeparture(); break;//處理客戶離開事件 default: System.out.println("事件型別錯誤"); ; } } System.out.println("今天總共:" + CustomerNum + "個客戶,總逗留時間為" + TotalTime + "秒" + ",等於" + TotalTime / 60 + "分"); System.out.println("每個人平均逗留時間為:" + TotalTime / CustomerNum + "秒, 也就是" + (TotalTime / CustomerNum) / 60 + "分"); } }
輸出結果
生成第一個客戶到達事件,該客戶為8點20分到來
今天總共:29個客戶,總逗留時間為25744秒,等於429分
每個人平均逗留時間為:887秒, 也就是14分
原始碼的github地址,可以下載到本地執行