1、問題描述與要求 模擬某校九層教學樓的電梯系統。該樓有一個自動電梯,能在每層停留,其中第一層是大樓的進出層,即是電梯的“本壘層”,電梯“空閑”時,將來到該層候命。 電梯一共有七個狀態,即正在開門(Opening)、已開門(Opened)、正在關門(Closing)、已關門(Closed)、等待(W ...
1、問題描述與要求
模擬某校九層教學樓的電梯系統。該樓有一個自動電梯,能在每層停留,其中第一層是大樓的進出層,即是電梯的“本壘層”,電梯“空閑”時,將來到該層候命。
電梯一共有七個狀態,即正在開門(Opening)、已開門(Opened)、正在關門(Closing)、已關門(Closed)、等待(Waiting)、移動(Moving)、減速(Decelerate)。
乘客可隨機地進出於任何層。對每個人來說,他有一個能容忍的最長等待時間,一旦等候電梯時間過長,他將放棄。
模擬時鐘從0開始,時間單位為0.1秒。人和電梯的各種動作均要消耗一定的時間單位(簡記為t),比如:
有人進出時,電梯每隔40t測試一次,若無人進出,則關門;
關門和開門各需要20t;
每個人進出電梯均需要25t;
電梯加速需要15t;
上升時,每一層需要51t,減速需要14t;
下降時,每一層需要61t,減速需要23t;
如果電梯在某層靜止時間超過300t,則駛回1層候命。
電梯調度規則:
1)就近原則:電梯的主要調度策略是首先響應沿當前行進方向上最近端的請求直到滿足最遠端請求。若該方向上無請求時,就改變移動方向;
2)在就近原則無法滿足的情況下,首先滿足更高層的請求;
3)電梯的最大承載人數為13人,電梯人數達到13人後,在有人出電梯之前,不接受進入電梯的請求;
4)乘客上下電梯時先出後進。進電梯時乘客是按發出乘坐請求的順序依次進入,每次只能進入一人且每個人花費的時間都為25t;
5)電梯在關門期間(電梯離開之前)所在層提出請求的乘客同樣允許進入。
要求:
按時序顯示系統狀態的變化過程,即發生的全部人和電梯的動作序列。
擴展要求:
實現電梯模擬的可視化界面。用動畫顯示電梯的升降,人進出電梯。設計有下列對象:電梯、人、電梯控制板及其上各種按鈕、定時器等。
2、設計
2.1 設計思想
數據由用戶自定義輸入,也可以進行改變使得通過用偽隨機數方式來產生相關數據。此程式採用用戶自定義輸入,這種輸入方式可以更加自由化的決定相關數據,但是同樣有弊端,不適合中途隨意更改數據。操作的功能如下:添加乘客、是否有乘客請求、判斷電梯方向、開關門的判斷設計、乘客進出設計、電梯移動的判斷設計、改變電梯狀態、判斷是否有人放棄來改變電梯的最遠請求。主要的操作必須每次都遍歷,所以時間會有點耗費過大,並且存儲結構設計不大合理,導致每次都需要遍歷所有數據,從而浪費時間。
存儲結構採用電梯外的等待隊列是單鏈表,電梯內部採用數組存儲。等待隊列的單鏈表設計為存儲乘客類數據的結點,這樣可以更加方便的訪問乘客的相關信息,但是放棄了鏈表內部的排序,因為等待隊列每次都需要遍歷,所以排序會浪費更多的時間,因此拋棄該項功能。電梯內部的數組是開闢了10個大小,因為數組的下標是由0開始,所以為了更好地儲存乘客前往的樓層,選擇開闢10個大小的數組,每個下標里存儲的數字代表著在該樓層下電梯的人數為多少人,選取數組存儲的原因是一旦乘客進入電梯,有用的信息就僅僅只是下電梯的樓層,因此數組是一個較優解。
該程式中涉及最多的演算法是窮舉搜索法,該演算法設計是為了每次都能夠遍歷所有的數據,但是該演算法存在缺陷,無法應對很多數據的操作,因此限制了該程式的數據量。該演算法使用for迴圈和while迴圈對數據進行遍歷,並且在迴圈中插入判斷語句,使得能夠在得到需要的數據後進行一系列的操作。
2.2 設計表示
2.3 詳細設計
鏈表結構體node中
成員有:(1)乘客類數據data:存儲乘客的相關信息,以便於在電梯模塊中使用。
(2)struct node*類型的next,用於鏈接下一個節點。
函數有:(1)append(形參為乘客類數據):因為有頭尾指針和頭節點,只要把形參鏈接到尾部即可。
(2)empty:只要頭指針指向的next為空就返回true,否則就返回false。
(3)Remove(形參為乘客的ID):if當前節點是否就是所要刪除的節點,如果是就根據是否是僅此一個節點或者是多個節點來分開處理;else當前節點不是要刪除的節點,那麼遍歷整個鏈表去尋找所要刪除的節點。
乘客類passenger中
成員有:(1)ID:用於設置乘客的編號。
(2)nowfloor:用於記錄乘客當前樓層。
(3)gofloor:用於記錄乘客需要去的樓層。
(4)whenwait:用於記錄乘客多久進入等待隊列。
函數有:(1)預設構造函數以及介面函數。
(2)setdata(參數為乘客ID):對該ID下的乘客進行賦值,並且限制乘客的當前樓層和所要前往的樓層為1-9,一旦超出範圍則提示需要重新輸入數據。
電梯類elevator中
成員有:(1)State:用於記錄電梯的狀態。
(2)floor:用於記錄電梯所處樓層。
(3)Wait:指向等待隊列頭節點的指針。
(4)DiantiLI[10]:電梯里的人員數組,下標用於記錄該層是否有人下電梯。
(5)All:記錄電梯里的人數。
(6)Dir:用於判斷電梯處於Up和Down以外時之前所處的狀態,以便於下一步的狀態判斷,-1為初始狀態,0為下降,1為上升。
函數有:(1)setnowState(參數為需要設定的電梯狀態):用於更改電梯狀態。
(2)setAll(參數為需要修改的乘客數量,有符號的整型):將參數與All參數進行加減。
(3)setDir(參數為想要把Dir改變的值):將Dir改變為參數值。
(4)JudgeGiveUp(參數為當前時間):用於判定當前是否有人放棄
如果(指針不為空){
如果(當前訪問的節點的等待時間+忍耐時間==當前時間)
刪除該節點並提示 }
(5)NoPassenger:主要是用於電梯處於閑置時的一些操作
如果電梯是Waiting狀態{
如果holdtime(一個全局變數,用來檢測是否達到300t)==300並且floor不等於1{
改變電梯狀態為下降,並且把holdtime重新置0,進入MoveDown函數,返回true
}
如果holdtim==300並且floor等於1{
輸出“電梯空閑無人”並把holdtime置為0,返回true
}
如果電梯裡外都沒有人{
輸出“電梯空閑無人”,並且holdtime自加,返回true
}
其他情況就調用JudgeDirction函數,並且返回false
}
如果電梯不處於Waiting狀態,調用JudgeDirction函數,並且返回false
(6)JudgeDirction:得出不同情況下的最遠請求,傳遞給MoveDirction用於改變狀態
如果狀態為Closed{
如果電梯裡外都沒有人{
改變電梯狀態並且返回空
}
如果floor為1{ //因為此時是電梯裡外肯定至少有一種情況不為空
改變電梯狀態為Up並且把Dir置為1
}
如果floor為9{
改變電梯狀態為Down並且把Dir置為0
}
如果電梯里有人{
遍曆數組DiantiLi得到下標i,並通過i來改變電梯狀態
}
如果電梯外不為空{
//此時根據電梯關門前的Dir來進行判定是否前往接乘客,此時分4種情況,2種上行,2種下行
如果Dir為1{
如果乘客樓層在當前樓層之上,才有可能前往接乘客{
如果該乘客是上行則將最遠請求與該乘客的前往樓層對比,併進行更改
否則如果乘客是下行,就將最遠請求與該乘客的當前樓層對比,併進行對比
}
對最遠請求進行對比和更改
}
如果Dir為0{
如果乘客樓層在當前樓層之下,才有可能前往接乘客{
如果該乘客是下行則將最遠請求與該乘客的前往樓層對比,併進行更改
否則如果乘客是上行,就將最遠請求與該乘客的當前樓層對比,併進行對比
}
對最遠請求進行對比和更改
}
}
}
如果狀態為Waiting{
迴圈遍歷等待隊列,找出最先按鍵的人,去響應該請求
如果ptemp(指向頭節點)不為空{
如果便利到進入隊列時間最早的乘客{
如果是電梯需要上行
則記錄上行的最遠請求
如果是電梯需要下行
則記錄下行的最遠請求
如果兩者皆有
則滿足先上後下的原則來改變電梯的狀態
}
}
}
如果電梯狀態為Up{
如果電梯里有人{
遍歷DiantiLi數組得到電梯里的乘客的最遠請求
}
如果等待隊列有人{
遍歷整個等待隊列
如果有乘客要去的樓層或者當前樓層比最遠請求大
則改變最遠請求
}
}
如果電梯狀態為Down{
如果電梯里有人{
遍歷DiantiLi數組得到電梯里的乘客的最遠請求
}
如果等待隊列有人{
遍歷整個等待隊列
如果有乘客要去的樓層或者當前樓層比最遠請求小
則改變最遠請求
}
}
最後調用MoveDirction函數,並把最遠請求傳入
(7)MoveDirction(參數為最遠請求):通過最遠請求和目前的狀態來調用不同的函數
如果最遠請求小於當前樓層
調用MoveDown並返回空
如果最遠請求大於當前樓層
調用MoveUp並返回空
如果電梯狀態目前為Opening
調用Open函數並返回空
如果電梯狀態目前為In
調用IN函數,並且調用JudgeClose函數進行判定此時是否還有人要進入,並返回空
如果電梯狀態目前為Out
調用OUT函數,並且調用JudgeClose函數進行判定此時是否還有人要進入,返回空
如果電梯狀態目前為Opened
JudgeClose判定是否關門
調用JudgeOut判定是否有人要出門,如果有人出去,調用OUT;調用JudgeIn判定是否有人要進入,如果有人進入,調用IN,然後返回空
如果電梯狀態目前為Closing
調用Close並返回空
如果最遠請求等於當前樓層並且電梯裡外都無人
將電梯置為Waiting,並且進入NoPassenger輸出電梯是空閑狀態
如果最遠請求等於當前樓層且電梯裡外是有人的
此時將電梯置為Opening,並且進入Open
(8)MoveDown:輸出電梯下樓每一t的狀態
如果record(全局變數,用於輸出電梯每一t的狀態)小於枚舉的值
record自加,輸出“電梯正在下樓”並返回空
如果等於枚舉的值時
電梯的樓層自減1,並且record置0
如果JudgeOpen為false則繼續調用MoveDown顯示下樓的狀態
如果JudgeOpen為true則將電梯的狀態置為Opening
(9)MoveUp:輸出電梯上樓每一t的狀態
如果record(全局變數,用於輸出電梯每一t的狀態)小於枚舉的值
record自加,輸出“電梯正在上樓”並返回空
如果等於枚舉的值時
電梯的樓層自加1,並且record置0
如果JudgeOpen為false則繼續調用MoveUp顯示上樓的狀態
如果JudgeOpen為true則將電梯的狀態置為Opening
(10)Open:顯示電梯開門狀態每一t的狀態
如果當前狀態不為Opening
設置當前狀態為Opening,把record置為0,然後返回空
如果record小於枚舉的值
record自加,輸出“電梯開門中”,返回空
如果是其他情況{
record置為0,輸出“開門完成”,將當前狀態置為Opened
如果JudgeOut為true就調用OUT
如果JudgeIn為true就調用IN
JudgeClose判斷是否關門
}
(11)IN:顯示哪些乘客進入電梯以及判定語句
如果電梯裡人少於13人{
如果record小於枚舉值{
如果當前狀態為Opened{
record自加,遍歷等待隊列,並且加入判斷乘客當前樓層必須是等於floor才能進入電梯
}
如果當前狀態為In{
record自加,輸出“乘客正在進入電梯”,返回空
}
}
如果是等於枚舉值{
Record置為0,並且將當前狀態置為Opened
}
}
如果電梯里的人數大於13{
輸出“電梯內人數已經達到最大值”,將當前狀態置為Closing
}
(12)JudgeClose:判斷當前電梯是否可以關門
如果當前狀態為Opened{
如果record小於枚舉值{
record自加,輸出“正在關門檢測”,然後返回空
}
如果等於枚舉值{
輸出“關門檢測結束”
如果當前狀態為Opened{
record置為0,調用Close函數
}
否則 record置為0,並且返回空
}
}
其他情況返回空
(13)Close:顯示電梯關門時每一t的狀態
如果record小於枚舉值{
record自加,設置當前狀態為Closing,輸出“電梯正在關門中”
}
其他情況{
record置為0,輸出“電梯已經關門”,設置當前狀態為Closed
如果最大請求為floor{
如果電梯裡外有人{
遍歷整個等待隊列
如果此時等待隊列有人發出請求
如果停止前電梯是向上走的
則判斷是否在當前樓層之上或者就是當前樓層,如果滿足就設置當前狀態為Opening
如果停止前電梯是向下走的
則判斷是否在當前樓層之下或者就是當前樓層,如果滿足就設置當前狀態為Opening
}
如果電梯裡外都沒人
設置當前狀態為Waiting,把Dir置為-1
}
如果最大請求不是當前樓層{
保持停止前電梯的狀態不變
}
}
調用NoPassenger
(14)OUT:記錄乘客下電梯的每一t的狀態
如果record小於枚舉值{
如果當前狀態為Opened{
record自加
如果電梯里有人要下電梯{
根據數組DiantiLi來判定下去多少個乘客並且輸出下去時每一t狀態,並且設置當前狀態為Out,返回空
}
}
如果當前狀態為Out{
record自加,輸出“乘客正在下電梯”,返回空
}
}
其他情況則輸出“需要下的乘客都已下去”,record置為0,並且把當前狀態置為Opened
}
(15)JudgeIn:判斷是否可以進乘客
如果All不為13{
如果等待隊列不為空{
如果乘客是向上走的並且當前樓層在該層並且電梯之前的方向為向上
則允許進入,返回true
如果乘客是向下走的並且當前樓層在該層並且電梯之前的方向為向下
則允許進入,返回true
}
}
遍歷完若沒有返回true則此時返回false
(16)JudgeOut:判斷乘客是否可以出去
如果電梯里有人{
數組DiantiLi中的該層樓的下標值中的數據不為0,則返回true
}
否則返回false
3、源程式清單
passenger.h //乘客類頭文件,存放乘客的相關信息以及更改值的介面函數
elevator.h //電梯類頭文件,存放枚舉信息以及電梯的屬性和相關判斷函數
Node.h //節點類頭文件,用於創建鏈表以及鏈表相關的操作函數
main.cpp //主函數,主要調用乘客類的設置屬性函數,電梯類的JudgeGiveUp函數,以及電梯類的addpassenger函數,並且顯示現在是多少t
4、源代碼
passenger.h //乘客類頭文件,存放乘客的相關信息以及更改值的介面函數
1 #ifndef PASSENGER_H 2 #define PASSENGER_H 3 #include<iostream> 4 using namespace std; 5 6 class passenger { 7 private: 8 int ID; 9 int nowfloor; 10 int gofloor; 11 int whenwait; 12 public: 13 passenger(); 14 void setdata(int ID1); 15 void setnowfloor(int nowfloor1); 16 void setgofloor(int gofloor1); 17 void setwhenwait(int whenwait1); 18 int getnowfloor()const; 19 int getgofloor()const; 20 int getID()const; 21 int getwhenwait()const; 22 }; 23 24 passenger::passenger() { 25 ID = 0; 26 nowfloor = 0; 27 gofloor = 0; 28 whenwait = 0; 29 } 30 31 void passenger::setdata(int ID1) { 32 ID = ID1; int i = 1; 33 while (i) { 34 cout << "請輸入第" << ID << "位乘客的信息" << endl; 35 cout << "該乘客目前在哪一層:"; cin >> nowfloor; 36 cout << "該乘客去哪一層:"; cin >> gofloor; 37 cout << "該乘客何時上電梯:"; cin >> whenwait; 38 if (nowfloor > 9 || nowfloor < 0) { 39 cout << "乘客目前的樓層有誤,請重輸入!" << endl; 40 } 41 if (gofloor > 9 || gofloor < 0) { 42 cout << "乘客要去的樓層有誤,請重輸入!" << endl; 43 } 44 else i = 0; 45 } 46 } 47 48 void passenger::setnowfloor(int nowfloor1) { 49 nowfloor = nowfloor1; 50 } 51 52 void passenger::setgofloor(int gofloor1) { 53 gofloor = gofloor1; 54 } 55 56 void passenger::setwhenwait(int whenwait1) { 57 whenwait = whenwait1; 58 } 59 60 int passenger::getnowfloor()const { 61 return nowfloor; 62 } 63 64 int passenger::getgofloor()const { 65 return gofloor; 66 } 67 68 int passenger::getID()const { 69 return ID; 70 } 71 72 int passenger::getwhenwait()const { 73 return whenwait; 74 } 75 76 #endif // !PASSENGER_H 77 #pragma once
elevator.h //電梯類頭文件,存放枚舉信息以及電梯的屬性和相關判斷函數
1 #ifndef ELEVATOR_H 2 #define ELEVATOR_H 3 #include"Node.h" 4 #include"passenger.h" 5 6 enum state { 7 Opening, 8 Opened, 9 Closing, 10 Closed, 11 Waiting, 12 Up, 13 Down, 14 In, 15 Out, 16 Decelerate 17 }; 18 19 int holdtime = 0,record=0,near=0; 20 enum timeX 21 { 22 test = 40, 23 open = 20, 24 close = 20, 25 in = 25, 26 out = 25, 27 quick = 15, 28 up = 51, 29 updecelerate = 14, 30 down = 61, 31 downdecelerate = 23, 32 peoplewait = 500, 33 wait = 300 34 }; 35 36 class elevator { 37 private: 38 state State=Waiting; 39 int floor = 1; 40 PNODE Wait=p_head; 41 int DiantiLi[10] = {0}; 42 int All = 0; 43 int Dir=-1;//判斷上下的情況 44 public: 45 state getnowState()const; 46 void setnowState(state t); 47 int getfloor()const; 48 void setfloor(int floor1); 49 int getAll()const; 50 void setAll(int num);//num為外部上電梯的人數 51 int getDir()const; 52 void setDir(int x); 53 void addpassenger(const passenger &x);//添加乘客 54 bool NoPassenger();//判斷是否有乘客請求 55 void JudgeDirction();//判斷電梯行走方向 56 bool JudgeOpen();//判斷是否開門 57 void Open();//電梯門打開 58 bool JudgeOut();//判斷乘客出去 59 void OUT();//乘客出去 60 bool JudgeIn();//判斷乘客進入 61 void IN();//乘客進入 62 void Close();//關門 63 void MoveUp();//向上移動 64 void MoveDown();//向下移動 65 void JudgeClose();//40t時間來判斷是否關門 66 void MoveDirction(const int floor1);//用來改變電梯的狀態 67 void JudgeGiveUp(int waittime);//判斷是否有人放棄,用在函數最開始 68