
我們要做一個程式,來模擬一棟有 10 層樓的建築裡,2 部電梯怎麼載人最快。
1. 模擬環境設定
- 大樓: 總共 10 層樓。
- 電梯: 共有 2 部,每部最多載 5 個人。
- 呼叫: 每層樓只有一組上/下按鈕給兩部電梯共用。
- 時間消耗:
- 跑一層樓: 1 秒。
- 停下來開門/關門: 1 秒。
- 產生乘客: 每 1 秒 隨機「生出」1 個新的乘客,並隨機指定他的所在樓層和目標樓層。
- 模擬目標: 總共要載送 40 個人次。
2. 挑戰任務
我們的程式必須完成以下 4 點:
- 列出設計藍圖: 說清楚我們需要哪些物件(例如:電梯本人、乘客、大樓控制中心)和它們需要哪些功能。
- 寫出運作SOP: 描述整個過程的步驟:人怎麼出現 → 怎麼按電梯 → 電梯怎麼決定去哪 → 怎麼把人送到。
- 定義電梯策略(最重要): 設計一個聰明的調度規則,讓這 2 部電梯合作,決定誰去接哪個人,以及怎麼跑最有效率(不要亂跑、不要讓客人等太久)。
- 跑起來看結果:
- 讓程式真的能跑。
- 要印出詳細的過程紀錄(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()
