模擬電梯管理系統

我們要做一個程式,來模擬一棟有 10 層樓的建築裡,2 部電梯怎麼載人最快。

1. 模擬環境設定

  • 大樓: 總共 10 層樓
  • 電梯: 共有 2 部,每部最多載 5 個人
  • 呼叫: 每層樓只有一組上/下按鈕給兩部電梯共用。
  • 時間消耗:
    • 跑一層樓: 1 秒。
    • 停下來開門/關門: 1 秒。
  • 產生乘客:1 秒 隨機「生出」1 個新的乘客,並隨機指定他的所在樓層和目標樓層。
  • 模擬目標: 總共要載送 40 個人次。

2. 挑戰任務

我們的程式必須完成以下 4 點:

  1. 列出設計藍圖: 說清楚我們需要哪些物件(例如:電梯本人、乘客、大樓控制中心)和它們需要哪些功能
  2. 寫出運作SOP: 描述整個過程的步驟:人怎麼出現 怎麼按電梯 電梯怎麼決定去哪 怎麼把人送到。
  3. 定義電梯策略(最重要): 設計一個聰明的調度規則,讓這 2 部電梯合作,決定誰去接哪個人,以及怎麼跑最有效率(不要亂跑、不要讓客人等太久)。
  4. 跑起來看結果:
    • 讓程式真的能跑。
    • 印出詳細的過程紀錄(Log),讓大家知道電梯什麼時候在哪裡、載了誰、放了誰。
    • 程式要一直跑到所有 40 個乘客都到達目的地為止,並統計一共花了多少秒

🚀 核心要求

總之,目標就是設計一個最有效率的電梯系統,用最少的總時間把 40 個客人送到位!


解法

好的,這是一個關於電梯群控系統模擬程式的詳細設計說明。

我將分為兩部分進行闡述:首先是程式的運作流程,然後是設計概念、物件與函式的功能描述。


📝 運作流程 (Simulation Flow)

整個模擬程式的運作核心是 Building.run_simulation() 函式,它通過一個主迴圈來模擬時間的流逝,直到所有 40 人次被服務完畢。

主迴圈 (每秒 T 的流程)

步驟函式/物件動作描述目的
1. 乘客生成Building.generate_person()若未達總人數 (40),在 T 秒產生一個隨機乘客,將其加入對應樓層的等待隊伍。滿足「每秒產生 1 人」的條件。
2. 狀態更新 (時間)Elevator.processing_time_left檢查所有電梯的忙碌計時器。若 processing_time_left > 0,則減 1。確保電梯在分配任務前狀態為最新(如 $T=3$ 忙碌 1 秒,在 $T=4$ 開始時釋放)。
3. 收集叫車Building.get_external_calls()收集所有樓層等待隊伍中的叫車請求 (樓層, 方向)。為群控調度提供全局視圖。
4. 群控調度Building.assign_calls()將尚未被指派的外部叫車,分配給最接近且完全空閒的電梯。實現「效率優先,不重複服務」的調度邏輯。
5. 電梯執行Elevator.get_action()遍歷每部電梯,決定其在 T 秒的動作。模擬電梯的決策過程。
5.1 忙碌如果 processing_time_left > 0,跳過決策。確保動作耗時被遵守。
5.2 決策Elevator.get_action()優先處理內部停靠點、其次是已指派的任務,最後是順路的外部叫車。遵循電梯的運行規則(LOOK/SCAN)。
5.3 執行Building.run_simulation()依據動作執行:
STOP: 乘客下電梯 (完成旅程);移除內部停靠點;乘客上電梯;設定 processing_time_left = 1
MOVE: 移動一層樓;設定 processing_time_left = 0 (因為移動在當前秒完成,但在 $T+1$ 才開始下一個動作)。
消耗時間並更新電梯狀態。
6. 時間推進self.time += 1進入下一秒 $T+1$。推進模擬時間。
7. 結束判斷檢查 total_finished_people 是否達到 40 人。若達到,輸出統計結果並結束。滿足模擬結束條件。

🎨 設計概念、物件與函式功能

本程式採用物件導向設計 (OOP),核心概念是將乘客、電梯和大樓管理器獨立抽象化。

1. 物件 (Class) 功能

物件設計概念主要屬性 (Attributes)
Person (乘客)記錄個體的行程資訊和時間戳記。id_str (P01, P02…), current_floor, target_floor, direction (1 or -1), spawn_time, ride_start_time, finish_time
Elevator (電梯)記錄電梯的實時狀態和當前任務。current_floor, direction (1: 上, -1: 下, 0: 靜止), passengers (車內乘客列表), stops (內部停靠樓層集合), assigned_call (被指派的外部叫車), processing_time_left (忙碌剩餘時間)。
Building (大樓/管理器)核心控制器,負責時間、乘客生成、群控調度和全局狀態管理。elevators (電梯列表), floors (樓層等待隊伍列表), time (當前模擬時間), total_finished_people (已完成人次統計)。

2. 主要函式 (Function) 功能

函式所屬物件作用關鍵邏輯
run_simulation()Building驅動整個模擬過程,是主迴圈的入口。執行上述 7 步驟的流程控制和時間管理。
generate_person()Building根據設定的機率和總人數限制生成乘客。隨機產生不相等的起始樓層和目標樓層。
get_external_calls()Building掃描所有樓層的等待隊伍。匯集所有樓層未接載的叫車請求 (樓層, 方向)。
assign_calls()Building群控調度核心。選擇:檢查未被指派的叫車,優先將任務分配給最近且完全空閒的電梯。
get_action()Elevator決定電梯在當前狀態下的下一步行動。決策優先順序:內部停靠 > 執行指派任務 > 同向順路 > 反向/靜止
log()Building標準化輸出模擬過程中的事件。輸出帶有時間戳記 [T=xxx] 的電梯和乘客狀態變化。

log

[T=000] --- 模擬開始:10層樓,2部電梯,目標40人次 ---
[T=000] 👤 P01 出現,在 F2,目標 F1
[T=000] 🈯️ E1 指派任務:前往 F2 接 下 向乘客
[T=000] ⬆️ E1 開始移動 (F1) 方向: 上
[T=000] 😴 E2 (F1) 靜止等待
[T=001] 👤 P02 出現,在 F5,目標 F4
[T=001] 🈯️ E2 指派任務:前往 F5 接 下 向乘客
[T=001] 🛑 E1 (F2) 停靠處理 (耗時 1s)
[T=001] ⬆️ P01 進入 E1 (F2 -> F1)
[T=001] ⬆️ E2 開始移動 (F1) 方向: 上
[T=002] 👤 P03 出現,在 F4,目標 F3
[T=002] ⬆️ E1 開始移動 (F2) 方向: 下
[T=002] ⬆️ E2 開始移動 (F2) 方向: 上
[T=003] 👤 P04 出現,在 F2,目標 F9
[T=003] 🛑 E1 (F1) 停靠處理 (耗時 1s)
[T=003] 🚪 P01 抵達 F1,完成旅程。
[T=003] ⬆️ E2 開始移動 (F3) 方向: 上
[T=004] 👤 P05 出現,在 F2,目標 F10
[T=004] 🈯️ E1 指派任務:前往 F4 接 下 向乘客
[T=004] ⬆️ E1 開始移動 (F1) 方向: 上
[T=004] ⬆️ E2 開始移動 (F4) 方向: 上
[T=005] 👤 P06 出現,在 F7,目標 F1
[T=005] ⬆️ E1 開始移動 (F2) 方向: 上
[T=005] 🛑 E2 (F5) 停靠處理 (耗時 1s)
[T=005] ⬆️ P02 進入 E2 (F5 -> F4)
[T=006] 👤 P07 出現,在 F1,目標 F2
[T=006] ⬆️ E1 開始移動 (F3) 方向: 上
[T=006] ⬆️ E2 開始移動 (F5) 方向: 下
[T=007] 👤 P08 出現,在 F9,目標 F10
[T=007] 🛑 E1 (F4) 停靠處理 (耗時 1s)
[T=007] ⬆️ P03 進入 E1 (F4 -> F3)
[T=007] 🛑 E2 (F4) 停靠處理 (耗時 1s)
[T=007] 🚪 P02 抵達 F4,完成旅程。
[T=008] 👤 P09 出現,在 F1,目標 F9
[T=008] 🈯️ E2 指派任務:前往 F7 接 下 向乘客
[T=008] ⬆️ E1 開始移動 (F4) 方向: 下
[T=008] ⬆️ E2 開始移動 (F4) 方向: 上
[T=009] 👤 P10 出現,在 F4,目標 F9
[T=009] 🛑 E1 (F3) 停靠處理 (耗時 1s)
[T=009] 🚪 P03 抵達 F3,完成旅程。
[T=009] ⬆️ E2 開始移動 (F5) 方向: 上
[T=010] 👤 P11 出現,在 F7,目標 F4
[T=010] 🈯️ E1 指派任務:前往 F1 接 上 向乘客
[T=010] ⬆️ E1 開始移動 (F3) 方向: 下
[T=010] ⬆️ E2 開始移動 (F6) 方向: 上
[T=011] 👤 P12 出現,在 F8,目標 F10
[T=011] ⬆️ E1 開始移動 (F2) 方向: 下
[T=011] 🛑 E2 (F7) 停靠處理 (耗時 1s)
[T=011] ⬆️ P06 進入 E2 (F7 -> F1)
[T=011] ⬆️ P11 進入 E2 (F7 -> F4)
[T=012] 👤 P13 出現,在 F5,目標 F1
[T=012] 🛑 E1 (F1) 停靠處理 (耗時 1s)
[T=012] ⬆️ P07 進入 E1 (F1 -> F2)
[T=012] ⬆️ P09 進入 E1 (F1 -> F9)
[T=012] ⬆️ E2 開始移動 (F7) 方向: 下
[T=013] 👤 P14 出現,在 F3,目標 F7
[T=013] ⬆️ E1 開始移動 (F1) 方向: 上
[T=013] ⬆️ E2 開始移動 (F6) 方向: 下
[T=014] 👤 P15 出現,在 F6,目標 F5
[T=014] 🛑 E1 (F2) 停靠處理 (耗時 1s)
[T=014] 🚪 P07 抵達 F2,完成旅程。
[T=014] ⬆️ P04 進入 E1 (F2 -> F9)
[T=014] ⬆️ P05 進入 E1 (F2 -> F10)
[T=014] ⬆️ E2 開始移動 (F5) 方向: 下
[T=015] 👤 P16 出現,在 F3,目標 F4
[T=015] ⬆️ E1 開始移動 (F2) 方向: 上
[T=015] 🛑 E2 (F4) 停靠處理 (耗時 1s)
[T=015] 🚪 P11 抵達 F4,完成旅程。
[T=016] 👤 P17 出現,在 F6,目標 F2
[T=016] ⬆️ E1 開始移動 (F3) 方向: 上
[T=016] ⬆️ E2 開始移動 (F4) 方向: 下
[T=017] 👤 P18 出現,在 F2,目標 F7
[T=017] ⬆️ E1 開始移動 (F4) 方向: 上
[T=017] ⬆️ E2 開始移動 (F3) 方向: 下
[T=018] 👤 P19 出現,在 F2,目標 F6
[T=018] ⬆️ E1 開始移動 (F5) 方向: 上
[T=018] ⬆️ E2 開始移動 (F2) 方向: 下
[T=019] 👤 P20 出現,在 F6,目標 F10
[T=019] ⬆️ E1 開始移動 (F6) 方向: 上
[T=019] 🛑 E2 (F1) 停靠處理 (耗時 1s)
[T=019] 🚪 P06 抵達 F1,完成旅程。
[T=020] 👤 P21 出現,在 F5,目標 F1
[T=020] 🈯️ E2 指派任務:前往 F2 接 上 向乘客
[T=020] ⬆️ E1 開始移動 (F7) 方向: 上
[T=020] ⬆️ E2 開始移動 (F1) 方向: 上
[T=021] 👤 P22 出現,在 F8,目標 F9
[T=021] ⬆️ E1 開始移動 (F8) 方向: 上
[T=021] 🛑 E2 (F2) 停靠處理 (耗時 1s)
[T=021] ⬆️ P18 進入 E2 (F2 -> F7)
[T=021] ⬆️ P19 進入 E2 (F2 -> F6)
[T=022] 👤 P23 出現,在 F2,目標 F7
[T=022] 🛑 E1 (F9) 停靠處理 (耗時 1s)
[T=022] 🚪 P09 抵達 F9,完成旅程。
[T=022] 🚪 P04 抵達 F9,完成旅程。
[T=022] ⬆️ P08 進入 E1 (F9 -> F10)
[T=022] ⬆️ E2 開始移動 (F2) 方向: 上
[T=023] 👤 P24 出現,在 F2,目標 F9
[T=023] ⬆️ E1 開始移動 (F9) 方向: 上
[T=023] ⬆️ E2 開始移動 (F3) 方向: 上
[T=024] 👤 P25 出現,在 F5,目標 F10
[T=024] 🛑 E1 (F10) 停靠處理 (耗時 1s)
[T=024] 🚪 P05 抵達 F10,完成旅程。
[T=024] 🚪 P08 抵達 F10,完成旅程。
[T=024] ⬆️ E2 開始移動 (F4) 方向: 上
[T=025] 👤 P26 出現,在 F6,目標 F10
[T=025] 🈯️ E1 指派任務:前往 F2 接 上 向乘客
[T=025] ⬆️ E1 開始移動 (F10) 方向: 下
[T=025] ⬆️ E2 開始移動 (F5) 方向: 上
[T=026] 👤 P27 出現,在 F4,目標 F2
[T=026] ⬆️ E1 開始移動 (F9) 方向: 下
[T=026] 🛑 E2 (F6) 停靠處理 (耗時 1s)
[T=026] 🚪 P19 抵達 F6,完成旅程。
[T=026] ⬆️ P20 進入 E2 (F6 -> F10)
[T=026] ⬆️ P26 進入 E2 (F6 -> F10)
[T=027] 👤 P28 出現,在 F1,目標 F4
[T=027] ⬆️ E1 開始移動 (F8) 方向: 下
[T=027] ⬆️ E2 開始移動 (F6) 方向: 上
[T=028] 👤 P29 出現,在 F5,目標 F2
[T=028] ⬆️ E1 開始移動 (F7) 方向: 下
[T=028] 🛑 E2 (F7) 停靠處理 (耗時 1s)
[T=028] 🚪 P18 抵達 F7,完成旅程。
[T=029] 👤 P30 出現,在 F4,目標 F2
[T=029] ⬆️ E1 開始移動 (F6) 方向: 下
[T=029] ⬆️ E2 開始移動 (F7) 方向: 上
[T=030] 👤 P31 出現,在 F7,目標 F5
[T=030] ⬆️ E1 開始移動 (F5) 方向: 下
[T=030] ⬆️ E2 開始移動 (F8) 方向: 上
[T=031] 👤 P32 出現,在 F8,目標 F6
[T=031] ⬆️ E1 開始移動 (F4) 方向: 下
[T=031] ⬆️ E2 開始移動 (F9) 方向: 上
[T=032] 👤 P33 出現,在 F3,目標 F6
[T=032] ⬆️ E1 開始移動 (F3) 方向: 下
[T=032] 🛑 E2 (F10) 停靠處理 (耗時 1s)
[T=032] 🚪 P20 抵達 F10,完成旅程。
[T=032] 🚪 P26 抵達 F10,完成旅程。
[T=033] 👤 P34 出現,在 F6,目標 F4
[T=033] 🈯️ E2 指派任務:前往 F7 接 下 向乘客
[T=033] 🛑 E1 (F2) 停靠處理 (耗時 1s)
[T=033] ⬆️ P23 進入 E1 (F2 -> F7)
[T=033] ⬆️ P24 進入 E1 (F2 -> F9)
[T=033] ⬆️ E2 開始移動 (F10) 方向: 下
[T=034] 👤 P35 出現,在 F5,目標 F2
[T=034] ⬆️ E1 開始移動 (F2) 方向: 上
[T=034] ⬆️ E2 開始移動 (F9) 方向: 下
[T=035] 👤 P36 出現,在 F10,目標 F3
[T=035] ⬆️ E1 開始移動 (F3) 方向: 上
[T=035] ⬆️ E2 開始移動 (F8) 方向: 下
[T=036] 👤 P37 出現,在 F9,目標 F4
[T=036] ⬆️ E1 開始移動 (F4) 方向: 上
[T=036] 🛑 E2 (F7) 停靠處理 (耗時 1s)
[T=036] ⬆️ P31 進入 E2 (F7 -> F5)
[T=037] 👤 P38 出現,在 F3,目標 F8
[T=037] ⬆️ E1 開始移動 (F5) 方向: 上
[T=037] ⬆️ E2 開始移動 (F7) 方向: 下
[T=038] 👤 P39 出現,在 F7,目標 F5
[T=038] ⬆️ E1 開始移動 (F6) 方向: 上
[T=038] ⬆️ E2 開始移動 (F6) 方向: 下
[T=039] 👤 P40 出現,在 F9,目標 F4
[T=039] 🛑 E1 (F7) 停靠處理 (耗時 1s)
[T=039] 🚪 P23 抵達 F7,完成旅程。
[T=039] 🛑 E2 (F5) 停靠處理 (耗時 1s)
[T=039] 🚪 P31 抵達 F5,完成旅程。
[T=039] ⬆️ P13 進入 E2 (F5 -> F1)
[T=039] ⬆️ P21 進入 E2 (F5 -> F1)
[T=039] ⬆️ P29 進入 E2 (F5 -> F2)
[T=039] ⬆️ P35 進入 E2 (F5 -> F2)
[T=040] ⬆️ E1 開始移動 (F7) 方向: 上
[T=040] ⬆️ E2 開始移動 (F5) 方向: 下
[T=041] ⬆️ E1 開始移動 (F8) 方向: 上
[T=041] ⬆️ E2 開始移動 (F4) 方向: 下
[T=042] 🛑 E1 (F9) 停靠處理 (耗時 1s)
[T=042] 🚪 P24 抵達 F9,完成旅程。
[T=042] ⬆️ E2 開始移動 (F3) 方向: 下
[T=043] 🈯️ E1 指派任務:前往 F7 接 下 向乘客
[T=043] ⬆️ E1 開始移動 (F9) 方向: 下
[T=043] 🛑 E2 (F2) 停靠處理 (耗時 1s)
[T=043] 🚪 P29 抵達 F2,完成旅程。
[T=043] 🚪 P35 抵達 F2,完成旅程。
[T=044] ⬆️ E1 開始移動 (F8) 方向: 下
[T=044] ⬆️ E2 開始移動 (F2) 方向: 下
[T=045] 🛑 E1 (F7) 停靠處理 (耗時 1s)
[T=045] ⬆️ P39 進入 E1 (F7 -> F5)
[T=045] 🛑 E2 (F1) 停靠處理 (耗時 1s)
[T=045] 🚪 P13 抵達 F1,完成旅程。
[T=045] 🚪 P21 抵達 F1,完成旅程。
[T=046] 🈯️ E2 指派任務:前往 F8 接 下 向乘客
[T=046] ⬆️ E1 開始移動 (F7) 方向: 下
[T=046] ⬆️ E2 開始移動 (F1) 方向: 上
[T=047] ⬆️ E1 開始移動 (F6) 方向: 下
[T=047] ⬆️ E2 開始移動 (F2) 方向: 上
[T=048] 🛑 E1 (F5) 停靠處理 (耗時 1s)
[T=048] 🚪 P39 抵達 F5,完成旅程。
[T=048] ⬆️ E2 開始移動 (F3) 方向: 上
[T=049] 🈯️ E1 指派任務:前往 F8 接 上 向乘客
[T=049] ⬆️ E1 開始移動 (F5) 方向: 上
[T=049] ⬆️ E2 開始移動 (F4) 方向: 上
[T=050] ⬆️ E1 開始移動 (F6) 方向: 上
[T=050] ⬆️ E2 開始移動 (F5) 方向: 上
[T=051] ⬆️ E1 開始移動 (F7) 方向: 上
[T=051] ⬆️ E2 開始移動 (F6) 方向: 上
[T=052] 🛑 E1 (F8) 停靠處理 (耗時 1s)
[T=052] ⬆️ P12 進入 E1 (F8 -> F10)
[T=052] ⬆️ P22 進入 E1 (F8 -> F9)
[T=052] ⬆️ E2 開始移動 (F7) 方向: 上
[T=053] ⬆️ E1 開始移動 (F8) 方向: 上
[T=053] 🛑 E2 (F8) 停靠處理 (耗時 1s)
[T=053] ⬆️ P32 進入 E2 (F8 -> F6)
[T=054] 🛑 E1 (F9) 停靠處理 (耗時 1s)
[T=054] 🚪 P22 抵達 F9,完成旅程。
[T=054] ⬆️ E2 開始移動 (F8) 方向: 下
[T=055] ⬆️ E1 開始移動 (F9) 方向: 上
[T=055] ⬆️ E2 開始移動 (F7) 方向: 下
[T=056] 🛑 E1 (F10) 停靠處理 (耗時 1s)
[T=056] 🚪 P12 抵達 F10,完成旅程。
[T=056] 🛑 E2 (F6) 停靠處理 (耗時 1s)
[T=056] 🚪 P32 抵達 F6,完成旅程。
[T=056] ⬆️ P15 進入 E2 (F6 -> F5)
[T=056] ⬆️ P17 進入 E2 (F6 -> F2)
[T=056] ⬆️ P34 進入 E2 (F6 -> F4)
[T=057] 🈯️ E1 指派任務:前往 F10 接 下 向乘客
[T=057] 🛑 E1 (F10) 停靠處理 (耗時 1s)
[T=057] ⬆️ P36 進入 E1 (F10 -> F3)
[T=057] ⬆️ E2 開始移動 (F6) 方向: 下
[T=058] ⬆️ E1 開始移動 (F10) 方向: 下
[T=058] 🛑 E2 (F5) 停靠處理 (耗時 1s)
[T=058] 🚪 P15 抵達 F5,完成旅程。
[T=059] ⬆️ E1 開始移動 (F9) 方向: 下
[T=059] ⬆️ E2 開始移動 (F5) 方向: 下
[T=060] ⬆️ E1 開始移動 (F8) 方向: 下
[T=060] 🛑 E2 (F4) 停靠處理 (耗時 1s)
[T=060] 🚪 P34 抵達 F4,完成旅程。
[T=060] ⬆️ P27 進入 E2 (F4 -> F2)
[T=060] ⬆️ P30 進入 E2 (F4 -> F2)
[T=061] ⬆️ E1 開始移動 (F7) 方向: 下
[T=061] ⬆️ E2 開始移動 (F4) 方向: 下
[T=062] ⬆️ E1 開始移動 (F6) 方向: 下
[T=062] ⬆️ E2 開始移動 (F3) 方向: 下
[T=063] ⬆️ E1 開始移動 (F5) 方向: 下
[T=063] 🛑 E2 (F2) 停靠處理 (耗時 1s)
[T=063] 🚪 P17 抵達 F2,完成旅程。
[T=063] 🚪 P27 抵達 F2,完成旅程。
[T=063] 🚪 P30 抵達 F2,完成旅程。
[T=064] 🈯️ E2 指派任務:前往 F9 接 下 向乘客
[T=064] ⬆️ E1 開始移動 (F4) 方向: 下
[T=064] ⬆️ E2 開始移動 (F2) 方向: 上
[T=065] 🛑 E1 (F3) 停靠處理 (耗時 1s)
[T=065] 🚪 P36 抵達 F3,完成旅程。
[T=065] ⬆️ E2 開始移動 (F3) 方向: 上
[T=066] 🈯️ E1 指派任務:前往 F3 接 上 向乘客
[T=066] 🛑 E1 (F3) 停靠處理 (耗時 1s)
[T=066] ⬆️ P14 進入 E1 (F3 -> F7)
[T=066] ⬆️ P16 進入 E1 (F3 -> F4)
[T=066] ⬆️ P33 進入 E1 (F3 -> F6)
[T=066] ⬆️ P38 進入 E1 (F3 -> F8)
[T=066] ⬆️ E2 開始移動 (F4) 方向: 上
[T=067] ⬆️ E1 開始移動 (F3) 方向: 上
[T=067] ⬆️ E2 開始移動 (F5) 方向: 上
[T=068] 🛑 E1 (F4) 停靠處理 (耗時 1s)
[T=068] 🚪 P16 抵達 F4,完成旅程。
[T=068] ⬆️ P10 進入 E1 (F4 -> F9)
[T=068] ⬆️ E2 開始移動 (F6) 方向: 上
[T=069] ⬆️ E1 開始移動 (F4) 方向: 上
[T=069] ⬆️ E2 開始移動 (F7) 方向: 上
[T=070] ⬆️ E1 開始移動 (F5) 方向: 上
[T=070] ⬆️ E2 開始移動 (F8) 方向: 上
[T=071] 🛑 E1 (F6) 停靠處理 (耗時 1s)
[T=071] 🚪 P33 抵達 F6,完成旅程。
[T=071] 🛑 E2 (F9) 停靠處理 (耗時 1s)
[T=071] ⬆️ P37 進入 E2 (F9 -> F4)
[T=071] ⬆️ P40 進入 E2 (F9 -> F4)
[T=072] ⬆️ E1 開始移動 (F6) 方向: 上
[T=072] ⬆️ E2 開始移動 (F9) 方向: 下
[T=073] 🛑 E1 (F7) 停靠處理 (耗時 1s)
[T=073] 🚪 P14 抵達 F7,完成旅程。
[T=073] ⬆️ E2 開始移動 (F8) 方向: 下
[T=074] ⬆️ E1 開始移動 (F7) 方向: 上
[T=074] ⬆️ E2 開始移動 (F7) 方向: 下
[T=075] 🛑 E1 (F8) 停靠處理 (耗時 1s)
[T=075] 🚪 P38 抵達 F8,完成旅程。
[T=075] ⬆️ E2 開始移動 (F6) 方向: 下
[T=076] ⬆️ E1 開始移動 (F8) 方向: 上
[T=076] ⬆️ E2 開始移動 (F5) 方向: 下
[T=077] 🛑 E1 (F9) 停靠處理 (耗時 1s)
[T=077] 🚪 P10 抵達 F9,完成旅程。
[T=077] 🛑 E2 (F4) 停靠處理 (耗時 1s)
[T=077] 🚪 P37 抵達 F4,完成旅程。
[T=077] 🚪 P40 抵達 F4,完成旅程。
[T=078] 🈯️ E2 指派任務:前往 F1 接 上 向乘客
[T=078] 🈯️ E1 指派任務:前往 F5 接 上 向乘客
[T=078] ⬆️ E1 開始移動 (F9) 方向: 下
[T=078] ⬆️ E2 開始移動 (F4) 方向: 下
[T=079] ⬆️ E1 開始移動 (F8) 方向: 下
[T=079] ⬆️ E2 開始移動 (F3) 方向: 下
[T=080] ⬆️ E1 開始移動 (F7) 方向: 下
[T=080] ⬆️ E2 開始移動 (F2) 方向: 下
[T=081] ⬆️ E1 開始移動 (F6) 方向: 下
[T=081] 🛑 E2 (F1) 停靠處理 (耗時 1s)
[T=081] ⬆️ P28 進入 E2 (F1 -> F4)
[T=082] 🛑 E1 (F5) 停靠處理 (耗時 1s)
[T=082] ⬆️ P25 進入 E1 (F5 -> F10)
[T=082] ⬆️ E2 開始移動 (F1) 方向: 上
[T=083] ⬆️ E1 開始移動 (F5) 方向: 上
[T=083] ⬆️ E2 開始移動 (F2) 方向: 上
[T=084] ⬆️ E1 開始移動 (F6) 方向: 上
[T=084] ⬆️ E2 開始移動 (F3) 方向: 上
[T=085] ⬆️ E1 開始移動 (F7) 方向: 上
[T=085] 🛑 E2 (F4) 停靠處理 (耗時 1s)
[T=085] 🚪 P28 抵達 F4,完成旅程。
[T=086] ⬆️ E1 開始移動 (F8) 方向: 上
[T=086] 😴 E2 (F4) 靜止等待
[T=087] ⬆️ E1 開始移動 (F9) 方向: 上
[T=087] 😴 E2 (F4) 靜止等待
[T=088] 🛑 E1 (F10) 停靠處理 (耗時 1s)
[T=088] 🚪 P25 抵達 F10,完成旅程。
[T=088] 😴 E2 (F4) 靜止等待

==================================================
📊 模擬結果
==================================================
✅ 總人次處理完畢: 40 人
⏱️  總耗時: 88 秒
⏳ 平均等待時間: 22.62 秒
🚀 平均搭乘時間: 5.12 秒
 Gesamt 平均總共耗時 (從按鈕到抵達): 27.75 秒
==================================================

source code

import random
import time

# --- 設定參數 (保持不變) ---
NUM_FLOORS = 10 
NUM_ELEVATORS = 2
MAX_CAPACITY = 5
TOTAL_PEOPLE_TO_GENERATE = 40
FLOOR_MOVE_TIME = 1
STOP_PROCESS_TIME = 1

# (Person 和 Elevator 類維持不變)

class Person:
    """代表一個需要搭乘電梯的乘客。"""
    def __init__(self, person_id, current_floor, target_floor, spawn_time):
        self.id = person_id
        self.id_str = f"P{person_id:02d}"  # 格式化為 P01, P02
        self.current_floor = current_floor
        self.target_floor = target_floor
        self.direction = 1 if target_floor > current_floor else -1
        self.spawn_time = spawn_time
        self.wait_start_time = spawn_time
        self.ride_start_time = 0
        self.finish_time = 0
        
    def __repr__(self):
        return (f"{self.id_str} (F{self.current_floor} -> F{self.target_floor}) "
                f"Dir:{'Up' if self.direction == 1 else 'Down'}")

class Elevator:
    """代表一部電梯的運作狀態與邏輯。"""
    def __init__(self, elevator_id):
        self.id = elevator_id
        self.current_floor = 1
        self.direction = 0  # 0: 靜止, 1: 向上, -1: 向下
        self.passengers = []
        self.stops = set()  # 集合:電梯內乘客的目的樓層
        self.assigned_call = None # 被指派的外部叫車 (floor, direction)
        self.processing_time_left = 0 # 處理停靠或移動的剩餘時間
        
    def get_capacity(self):
        """返回當前電梯的可用容量。"""
        return MAX_CAPACITY - len(self.passengers)
        
    def add_stop(self, floor):
        """增加一個電梯需要停靠的內部樓層。"""
        self.stops.add(floor)
        
    def get_action(self, all_external_calls):
        """
        決定電梯下一秒的行動 (移動、靜止或改變方向)。
        """
        if self.processing_time_left > 0:
            return 'BUSY'
        
        # 1. 優先處理內部停靠點 (乘客目的樓層)
        if self.current_floor in self.stops:
            return 'STOP'
            
        # 2. 如果有被指派的外部叫車,且未接人,則以該叫車為優先方向
        if self.assigned_call:
            call_floor, call_direction = self.assigned_call
            
            if self.current_floor == call_floor:
                self.direction = call_direction
                self.assigned_call = None
                return 'STOP'
            
            target_dir = 1 if call_floor > self.current_floor else -1
            if self.direction != target_dir:
                 self.direction = target_dir
            
            return 'MOVE'

        # 3. 如果正在移動 (方向非0),執行既有任務 (SCAN/LOOK)
        if self.direction != 0:
            internal_targets_in_direction = [t for t in self.stops if (t - self.current_floor) * self.direction > 0]
            external_targets_in_direction = [c for c, d in all_external_calls
                                            if d == self.direction and (c - self.current_floor) * self.direction > 0]
            
            if internal_targets_in_direction or external_targets_in_direction:
                return 'MOVE'
            else:
                if self.stops:
                     return 'REVERSE'
                else:
                    self.direction = 0 
                    return 'IDLE' 
            
        # 4. 靜止時 (direction == 0 且無指派),等待 Building 分配任務
        return 'IDLE'


class Building:
    """管理整個電梯系統、乘客生成和模擬計時。"""
    def __init__(self, num_elevators, num_floors):
        self.elevators = [Elevator(i + 1) for i in range(num_elevators)]
        self.floors = [[] for _ in range(num_floors + 1)]
        self.time = 0
        self.person_id_counter = 1
        self.generated_people_count = 0
        self.total_finished_people = 0
        self.finished_people_data = []
        
    def generate_person(self):
        # ... (不變) ...
        if self.generated_people_count < TOTAL_PEOPLE_TO_GENERATE:
            while True:
                start = random.randint(1, NUM_FLOORS)
                end = random.randint(1, NUM_FLOORS)
                if start != end:
                    break
            
            person = Person(self.person_id_counter, start, end, self.time)
            self.floors[start].append(person)
            self.person_id_counter += 1
            self.generated_people_count += 1
            return person
        return None

    def get_external_calls(self):
        # ... (不變) ...
        calls = set()
        for floor in range(1, NUM_FLOORS + 1):
            for person in self.floors[floor]:
                calls.add((person.current_floor, person.direction))
        return calls
        
    def log(self, message):
        """列印帶時間戳記的 log。"""
        print(f"[T={self.time:03d}] {message}")

    def assign_calls(self, all_external_calls):
        """
        電梯群控調度邏輯:將未被指派的外部叫車分配給最合適的空閒電梯。
        """
        
        assigned_calls = {e.assigned_call for e in self.elevators if e.assigned_call}
        unassigned_calls = all_external_calls - assigned_calls

        if not unassigned_calls:
            return

        for call_floor, call_direction in list(unassigned_calls):
            
            best_elevator = None
            min_cost = float('inf') 
            
            for e in self.elevators:
                # 關鍵檢查:確保電梯在 T 秒初完全空閒 (processing_time_left 已經在前面清零)
                if e.processing_time_left > 0 or e.stops or e.assigned_call:
                     continue
                
                # 條件 2: 選擇距離最近的空閒電梯 (SSTF)
                cost = abs(e.current_floor - call_floor)
                
                if cost < min_cost:
                    min_cost = cost
                    best_elevator = e
            
            if best_elevator:
                # 分配任務
                best_elevator.assigned_call = (call_floor, call_direction)
                self.log(f"🈯️ E{best_elevator.id} 指派任務:前往 F{call_floor} 接 {'上' if call_direction == 1 else '下'} 向乘客")


    def run_simulation(self):
        """執行整個模擬過程。"""
        self.log(f"--- 模擬開始:{NUM_FLOORS}層樓,{NUM_ELEVATORS}部電梯,目標{TOTAL_PEOPLE_TO_GENERATE}人次 ---")

        while self.total_finished_people < TOTAL_PEOPLE_TO_GENERATE:
            
            # 1. 乘客生成階段 (T 秒開始時產生 1 人)
            new_person = self.generate_person()
            if new_person:
                self.log(f"👤 {new_person.id_str} 出現,在 F{new_person.current_floor},目標 F{new_person.target_floor}")
            
            # --- 2. 關鍵修正:電梯忙碌狀態更新 (處理時間結束) ---
            for elevator in self.elevators:
                if elevator.processing_time_left > 0:
                    elevator.processing_time_left -= 1
            
            # 3. 電梯群控調度階段 (分配任務給空閒電梯)
            all_external_calls = self.get_external_calls()
            self.assign_calls(all_external_calls)

            # 4. 電梯決策與行動階段
            for elevator in self.elevators:
                
                # A. 檢查是否仍在忙碌 (處理時間 > 0)
                if elevator.processing_time_left > 0:
                    continue 

                # B. 決策
                action = elevator.get_action(all_external_calls)
                
                if action == 'STOP':
                    stop_floor = elevator.current_floor
                    # 停靠處理 (接/送客)
                    self.log(f"🛑 E{elevator.id} (F{stop_floor}) 停靠處理 (耗時 {STOP_PROCESS_TIME}s)")
                    
                    # --- 乘客下電梯 ---
                    passengers_out = [p for p in elevator.passengers if p.target_floor == stop_floor]
                    for p in passengers_out:
                        p.finish_time = self.time + STOP_PROCESS_TIME
                        elevator.passengers.remove(p)
                        self.total_finished_people += 1
                        self.finished_people_data.append(p)
                        self.log(f"🚪 {p.id_str} 抵達 F{p.target_floor},完成旅程。")
                        
                    # --- 移除內部停靠點 ---
                    if stop_floor in elevator.stops:
                        elevator.stops.remove(stop_floor)

                    # --- 乘客上電梯 ---
                    waiting_people = self.floors[stop_floor]
                    passengers_in = []
                    
                    current_dir = elevator.direction
                    
                    people_to_remove = []
                    for p in waiting_people:
                        if p.direction == current_dir and elevator.get_capacity() > 0:
                            passengers_in.append(p)
                            p.ride_start_time = self.time + STOP_PROCESS_TIME
                            elevator.passengers.append(p)
                            elevator.add_stop(p.target_floor)
                            people_to_remove.append(p)
                    
                    for p in people_to_remove:
                        self.floors[stop_floor].remove(p)
                        self.log(f"⬆️ {p.id_str} 進入 E{elevator.id} (F{p.current_floor} -> F{p.target_floor})")
                        
                    # 設置停靠處理時間
                    elevator.processing_time_left = STOP_PROCESS_TIME

                elif action == 'MOVE':
                    # 移動
                    self.log(f"⬆️ E{elevator.id} 開始移動 (F{elevator.current_floor}) 方向: {'上' if elevator.direction == 1 else '下'}")
                    elevator.current_floor += elevator.direction
                    elevator.processing_time_left = FLOOR_MOVE_TIME - 1

                elif action == 'REVERSE':
                    # 改變方向
                    elevator.direction *= -1
                    self.log(f"🔄 E{elevator.id} (F{elevator.current_floor}) 改變方向至 {'上' if elevator.direction == 1 else '下'}")

                elif action == 'IDLE':
                    # 靜止等待
                    if elevator.direction != 0:
                        elevator.direction = 0
                    self.log(f"😴 E{elevator.id} (F{elevator.current_floor}) 靜止等待")
                    pass
            
            # 5. 時間推進
            self.time += 1
            
            if self.time > 1000 and self.generated_people_count == TOTAL_PEOPLE_TO_GENERATE:
                self.log("⚠️ 模擬時間過長,強制結束。")
                break

        # 6. 統計結果
        total_time = self.time - 1
        
        # ... (統計結果計算保持不變) ...
        wait_times = [(p.ride_start_time - p.wait_start_time) for p in self.finished_people_data]
        trip_times = [(p.finish_time - p.ride_start_time) for p in self.finished_people_data]
        total_times = [(p.finish_time - p.wait_start_time) for p in self.finished_people_data]

        avg_wait = sum(wait_times) / len(wait_times) if wait_times else 0
        avg_trip = sum(trip_times) / len(trip_times) if trip_times else 0
        avg_total = sum(total_times) / len(total_times) if total_times else 0

        print("\n" + "="*50)
        print("📊 模擬結果")
        print("="*50)
        print(f"✅ 總人次處理完畢: {self.total_finished_people} 人")
        print(f"⏱️  總耗時: {total_time} 秒")
        print(f"⏳ 平均等待時間: {avg_wait:.2f} 秒")
        print(f"🚀 平均搭乘時間: {avg_trip:.2f} 秒")
        print(f" Gesamt 平均總共耗時 (從按鈕到抵達): {avg_total:.2f} 秒")
        print("="*50)


# --- 運行模擬 ---
if __name__ == "__main__":
    random.seed(42) 
    building = Building(NUM_ELEVATORS, NUM_FLOORS)
    building.run_simulation()

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *