# GenAIEd Python Learning 這份筆記整理了資訊科技在商管領域的應用演進、程式設計的基本概念、Python 語言的基礎語法與控制結構,以及如何運用程式解決實際的作業管理問題。 [Toc] 資料來源:賓州大學 GenAIed 學習平臺課程 使用 NotebookLM 進行整理 copyright @ytseiung-tech --- ## 第一部分:資訊科技與商管的結合 (Module 1) * **資訊科技對商管的影響演進** 七十年來,影響逐漸改變,主要分為三個階段: * **IT-assisted Management (資訊科技輔助管理)** * 起源於管理資訊系統 (Management Information Systems, MIS),利用商用資訊系統輔助企業經營。 * 應用實例: * 辦公室自動化 (Office Automation Systems, OAS) - Word, Excel, PowerPoint, Access;交易處理系統 (Transaction Processing Systems, TPS) - 銀行、零售、工廠、快遞、航空業。 >[!Tip]核心概念 >「電腦做事比較快」(Computers can compute) * **IT-supported Management (資訊科技支援管理)** * 人們發現電腦不僅做得比較快,還可以做得「比較好」。 * 應用實例: * 決策支援系統 (Decision Support Systems, DSS) - 優化作業流程、輔助企業決策。航空公司排班表是個複雜的例子,需要資訊系統來決定約 1000 名地勤人員的季度或每日班表。 >[!Tip]核心概念 >「電腦可以搜尋」(Computers can search) * **IT-enabled Management (資訊科技促成管理)** * 現階段,有些事情「沒有電腦就做不到」。 * 應用實例: * **套利 (arbitrage) 與高頻交易 (high-frequency trading)**:套利機會稍縱即逝,需要自動化程式交易系統來抓住極短暫的市場變化,例如買賣價差或跨交易所價差。新聞交易系統會從線上資訊源 (Bloomberg, 新聞網站, Twitter) 辨識公司名稱、關鍵字、甚至進行語義分析,以在人類交易員前反應消息。 * **網路購物與推薦系統 (精準行銷)**:收集銷售記錄 (交易資料) 和行為資料 (點擊記錄、未吃完餐點、YouBike 借還記錄等) 是導入推薦系統的必要條件,這些是「大數據」的例子。 * **自動化「競爭標價」系統**:Amazon.com 上的書商會使用自動化系統調整價格,例如設定價格為競爭對手的 99.83%。有趣的例子像是兩百萬美金的書,可能是因為書商設定價格為最低競爭對手的 1.27 倍,而最低競爭對手又設定為前者的 99.83%,形成價格互相追逐,且其中一家書商可能只是想打廣告而非真的賣書。 * **共享經濟 (sharing economy)**:例如 Instacart,一個媒合雜貨代買人與顧客的平台。資訊科技為其創造獨特價值 (系統規劃採購路線、動態決定價格),帶來競爭優勢並創造新的商業模式。 >[!Tip]核心概念 >「電腦可以最佳化」(Computers can optimize) ## 第二部分:電腦程式設計入門 (Module 2) * **電腦程式 (Computer programs)** * 電腦中的運作元素,也稱作軟體 (software)。 * 是資料 (data) 和指令 (instructions) 結構化的組合,用來操作電腦產生特定結果。 * 優點 (Strength):高速計算 (High-speed computing), 大容量記憶體 (large memory)。 * 缺點 (Weakness):需要人 (程式設計師) 告訴它們要做什麼 (People (programmers) need to tell them what to do)。 * **程式語言類型** * **低階語言 (Low-level languages)**:機械語言 (Machine language) 和組合語言 (Assembly language),直接控制硬體,但難讀寫程式。 * **高階語言 (High-level languages)**:易讀寫程式,但需要「翻譯器」。大多數應用軟體用高階語言開發。 * **Python 語言** * 本課程學習的高階語言。 * 由 Guido van Rossum 約 1996 年發明。 * 非常適合初學者 (very good for beginners):它簡單 (simple), 容易開始 (easy to start), 且功能強大 (powerful)。 * **程式執行方式** * **直譯器 (Interpreter)**:逐行翻譯程式碼為組合語言並執行。Python 使用直譯器,因此 Python 也是一種腳本語言 (scripting language)。許多其他高階語言使用編譯器 (Compiler)。 * **互動模式 (Interactive mode)**:輸入一行程式,立即看到結果。在提示字元 (prompt, `>>>`) 後輸入語句。 * **腳本模式 (Script mode)**:將多行程式寫入檔案 (`.py` 結尾),一次性執行所有語句。 * **錯誤訊息 (Error messages)** * 程式可能包含錯誤 (errors)。 * 遇到錯誤訊息時,應盡力閱讀理解。提供錯誤訊息給尋求幫助者,或將錯誤訊息複製貼上到搜尋引擎通常很有幫助。 * **基本算術運算 (Basic arithmetic)** * 電腦擅長計算。 * 基本運算子: * `+`:加法 (addition)。 * `-`:減法 (subtraction)。 * `*`:乘法 (multiplication)。 * `/`:浮點數除法 (floating-point division)。 * `//`:地板除法 (floor division),取商的整數部分 (捨去小數)。 * `%`:求餘數 (modulo)。 * `**`:次方 (square of a number),例如 `3 ** 2` 是 3 的 2 次方。 * `()`:括號用於決定計算順序。 ## 第三部分:變數、資料型態與輸入/輸出 (Module 2 & 3) * **輸入 (input)** * `print()` 函數將資料輸出到控制台輸出。 * `input()` 函數從控制台輸入 (通常是鍵盤) 接收資料輸入。 * `input()` 讀取使用者輸入的任何內容,並將其視為字串 (string)。因此,如果需要數字,必須手動進行型別轉換 (casting)。 * 程式執行到 `input()` 函數時會暫停,等待使用者輸入。 * 需要一個「容器」來儲存輸入的資料,這就是變數 (variable)。 * **變數與資料型態 (Variables and data types)** * 變數是一個儲存值的容器。宣告變數時,系統會在記憶體中分配空間。 * 變數具有資料型態 (data type)。作業系統 (Operating System, OS) 根據型態來理解值。即使儲存相同的位元序列,整數和浮點數代表的值不同。 * 目前重要的資料型態: * `int`:整數 (integer)。 * `float`:浮點數 (fractional numbers)。 * `string`:字串 (sequence of characters)。在 Python 中,即使一個單一字元也被視為長度為 1 的字串。 * `bool`:布林值 (Boolean),只有 `True` 和 `False` 兩個值。 * 變數的三個主要屬性:型態 (Type), 名稱 (Name), 值 (Value)。 * **變數宣告 (Variable declaration)** * 使用變數前必須先宣告。需要指定名稱,以及型態、初始值或兩者。 * Python 通常直接賦予變數初始值進行宣告。直譯器會根據初始值自動設定變數型態。 * 使用 `type()` 函數可以查看變數的型態。 * 可以使用 `int()`, `float()`, `str()`, `bool()` 函數來宣告變數而不給初始值。例如 `a = int()` 初始化為 0,`b = float()` 初始化為 0.0,`c = ""` 初始化為空字串,`a = bool()` 初始化為 `False`。 * 變數的值可以被覆寫 (overwritten)。 * **型別轉換 (Casting)** * 將一個值從一種型態轉換為另一種型態的過程。 * `int()`:轉換為整數。嘗試將浮點數或看起來像整數的字串轉為整數會成功,但將非數字字串轉為整數會出錯。 * `float()`:轉換為浮點數。整數或看起來像浮點數的字串轉為浮點數會成功,但將非數字字串轉為浮點數會出錯。 * `str()`:轉換為字串。整數或浮點數轉為字串會成功。 * **更多關於 print** * `print()` 函數會列印其後的內容。這些內容實際上會被轉換為字串後才列印。 * 可以使用逗號 `,` 分隔多個要列印的變數/值 (tokens)。Python 會在用逗號分隔的項目之間自動插入一個空白字元。 * 如果不想自動插入空白,可以使用字串串接運算子 `+` 手動串接。非字串項目必須先用 `str()` 轉為字串,否則會導致執行期錯誤 (runtime error)。 * **指定 (Assignment)** * `=` 是指定運算子 (assignment operator)。將右側 (Right-Hand Side, RHS) 的值賦予給左側的變數。 * `a = a + 2` 這樣的語句是自我指派 (self-assignment)。它會先計算右側 `a + 2` 的值,再將結果賦予給左側的變數 `a`。與數學上的等號不同。 * 常見的自我指派縮寫運算子:`+=`, `-=`, `*=`, `/=`, `//=`, `**=`, `%=`。例如 `a += 2` 等同於 `a = a + 2`。 * 層遞指派 (cascade assignment):`a = b = 10` 將相同的值賦予給多個變數。 ## 第四部分:條件判斷 (Conditionals) (Module 3 & 4) * **概念** * 根據某些條件來選擇要執行的語句 (或跳過某些語句)。 * 使用 `if`, `else`, `elif` 關鍵字。 * **if 語句** * `if condition:` 後面的語句塊 (if block) 會在條件 (`condition`) 為 `True` 時執行,否則跳過。 * 冒號 `:` 是必需的。 * if 塊內可以有多個語句。 * **縮排 (Indention) 非常重要**:if 塊內的語句必須都有一個層級的縮排。沒有縮排的語句被視為在 if 塊之外。縮排大小不限,但同一個塊內必須一致。 * **if-else 語句** * 根據條件的真假執行不同的語句集。 * `if condition: statements 1 else: statements 2`。如果條件為 `True` 執行 `statements 1`;如果條件為 `False` 執行 `statements 2`。 * `else` 必須有一個相關聯的 `if`。 * **布林資料型態 (Boolean data type)** * 值只有 `True` 和 `False` 兩種。 * 可以使用 `bool()` 創建。也可以直接賦予 `True` 或 `False`。 * 在 Python 中,`False` 表示 0,`True` 表示非 0。`bool(0)` 為 `False`,`bool(非0)` 為 `True`。 * **比較運算子 (Comparison operators)** * 比較兩個運算元,返回布林值。 * `>`:大於 (bigger than)。 * `<`:小於 (smaller than)。 * `>=`:不小於 (not smaller than)。 * `<=`:不大於 (not bigger than)。 * `==`:等於 (equals)。注意與指定運算子 `=` 的區別。 * `!=`:不等於 (not equals)。 * 比較:`==` 是「等於嗎?」,`=` 是「變成...」。 * **巢狀 if-else (Nested if-else statement)** * `if` 或 `if-else` 語句可以巢狀地放在另一個 `if` 或 `else` 塊中。 * 可以有多個層級的巢狀。 * 在 Python 中,`else` 只與同一個縮排層級的最近一個 `if` 相關聯。 * **三元條件運算子 (Ternary if operator)** * 當 `if-else` 的後續操作很簡單時可以使用。 * 語法:`operation A if condition else operation B`。如果條件為 `True` 執行 `operation A`,否則執行 `operation B`。 * 可以使用括號增加可讀性。可以巢狀使用,但不建議 (可讀性變差)。 * **else-if 語句 (elif)** * 當需要回應多個條件時使用。 * 是巢狀 `if-else` 的更簡潔形式,提高了程式的可讀性。 * 語法:`if condition1: ... elif condition2: ... else: ....` ## 第五部分:邏輯運算子 (Logical Operators) (Module 4) * **概念** * 用於組合多個條件。 * 運算子包括:`and`, `or`, `not`。 * 運算子有優先權規則,但不需記憶,使用括號即可。 * **and 運算子** * 作用於兩個條件。 * 當兩個條件都為 `True` 時返回 `True`,否則返回 `False`。 * `if condition 1 and condition 2: statements`。 * 可以使用 `and` 操作符替換巢狀 `if` 語句。 * Python 允許鏈式比較,如 `a < b < c`,等同於 `a < b and b < c`。但避免寫複雜且難以理解的鏈式表達式。 * 每個條件必須完整獨立:`b > a and < c` 是錯誤的。 * **or 運算子** * 當至少一個條件為 `True` 時返回 `True`,否則返回 `False`。 * `if condition 1 or condition 2: statements`。 * 可以組合多個 `or` 條件或組合 `or` 和 `and` 條件 (注意優先權或使用括號)。 * **not 運算子** * 返回條件的相反值。`not (True)` 返回 `False`,`not (False)` 返回 `True`。 * 當 `if` 塊中自然沒有什麼要執行,而 `else` 塊有特定動作時,可以使用 `not` 來反轉條件,將動作移至 `if` 塊以減少縮排或提高可讀性。 ## 第六部分:程式碼格式化 (Formatting a Program) (Module 4) * **重要性** * 保持程式碼良好的格式非常重要,可以提高可讀性。 * **通用準則** * 雖然每個程式設計師可能有自己的風格,但 Python 有一些通用準則。 * **空白字元與空行 (White spaces and empty lines)**: * 在二元運算子周圍添加兩個空白字元 (例如 `num1 + num2`)。 * 在每個逗號後面添加一個空白字元 (例如 `print("The sum is", num1 + num2)`)。 * 使用空行分隔不同的程式碼塊或邏輯組。 * **變數名稱 (Variable names)**:給予變數可理解的名稱,而非單一字母或難以理解的縮寫。例如 `dice1`, `dice2`, `sum` 比 `a`, `b`, `c` 更清晰。 * **註解 (Comments)**:程式設計師的筆記,會被編譯器或直譯器忽略。 * 單行註解 (`#`):`#` 後面的同行的所有內容都是註解。 * 區塊註解 (`""" Docstring """`):在三個雙引號 (`"""`) 之間的所有內容都是註解,可以跨越多行。 ## 第七部分:電腦、資料型態與精確度 (Module 5) * **電腦結構** * 現代電腦的基本組成:輸入 (Input)、輸出 (Output)、儲存 (Storage)、中央處理器 (Central Processing Unit, CPU)、記憶體 (Memory)。 * 程式儲存在儲存裝置。執行程式時,會在記憶體中創建變數儲存值,值在 CPU 中進行運算,結果存回記憶體。也會讀取輸入、寫入輸出、讀寫儲存裝置。 * CPU:進行算術運算。 * 記憶體 (Memory):揮發性儲存空間。 * 儲存 (Storage):非揮發性儲存,如硬碟、光碟、隨身碟。 * **變數詳情 (OS 記錄)** * 作業系統 (Operating System, OS) 為每個變數記錄四個資訊:記憶體位址 (Memory address), 名稱 (Identifier), 值 (Value), 型態 (Type)。 * **資料型態 revisited** * Python 自動根據初始值決定變數型態。 * `int`:整數 (integer)。 * `string`:字元序列 (sequence of characters)。 * `float`:浮點數 (floating-point numbers)。 * **整數 (Integers)** * 電腦使用二進制系統儲存值。 * 位元 (bits) 組成二進制數字。N 位元可以表示 $2^N$ 個值。常用長度有 16, 32, 64, 96, 128 位元。1 byte = 8 bits。 * 帶符號整數 (Signed integers):使用第一位元表示正負號,可以表示負數。Python 的整數預設是帶符號的。 * 宣告方法:直接賦值或使用 `int()`。 * **浮點數 (Floating-point numbers)** * 用於表示小數 (fractional numbers)。 * 核心概念是使用指數形式表示數值,小數點是「浮動的」,以表示更多數值或增加精確度。 * Python 的浮點數預設是帶符號的。 * 宣告方法:直接賦值 (含小數點) 或使用 `float()`。 * **精確度問題 (Precision Issue)**:這是一個大問題。 * 大多數十進制小數在電腦的二進制表示中只能被近似 (approximated)。 * 例如 `f = pow(i, 1/2)` (計算平方根) 時,`f` 不一定精確儲存 `i` 的平方根,會存在誤差。導致 `f * f != i` 的情況發生。 * 解決方法:使用「不精確」的比較 (imprecise comparisons)。例如 `if abs(f * f - i) > 0.0001:` 來檢查兩者之差是否大於一個很小的容錯值 (error tolerance)。 * 容錯值設定很重要,不宜過大或過小,應根據問題特性設定。 * **字元 (Characters)** * 電腦不能直接儲存字元,而是將每個字元編碼成整數。 * 常見編碼標準: * ASCII:用一個位元組表示英文字母、數字、符號和特殊字元 (如換行符號)。不能表示中文字元。 * UTF-8, Big-5:用於表示中文等非英文字元。 * `chr()` 函數可以將整數轉換為對應的字元。 * Python 中的單一字元也被視為長度為 1 的字串。 * **字串操作 (String operations)** * `len()`:返回字串的長度 (字元數)。 * `+`:字串串接運算子 (string concatenation operator),連接兩個字串。 * 半形 (halfwidth) 和全形 (fullwidth) 字元:英文字元和符號是半形,全形符號是非英文符號。 ## 第八部分:迴圈 (Iterations) (Module 5 & 6) * **概念** * 重複執行一組程式碼。 * **while 迴圈 (while statement)** * 基於條件重複執行程式碼塊。 * `while condition: statements`。只要條件為 `True`,就重複執行 `statements`。 * 可以像一個會重複的 `if` 語句。 * 常用於迴圈計數器 (loop counter) 的更新,例如 `i = i + 1` 或 `i += 1`。 * **無窮迴圈 (Infinite loops)** * 永不終止的迴圈。 * 通常是程式設計師的邏輯錯誤。發生時需檢查程式。 * 有時也會有意創建,然後使用 `break` 語句終止。 * **break 和 continue** * 用於改變迴圈的執行流程。 * `break`:立刻終止當前層級的迴圈。執行流程跳到迴圈後面的語句。在巢狀迴圈中,`break` 只跳出內層迴圈。 * `continue`:跳過 `continue` 後面在當前迭代中的語句,立即回到迴圈開頭檢查條件。如果條件滿足,開始下一個迭代。在巢狀迴圈中,`continue` 只跳到內層迴圈的條件檢查。 * 有意創建無窮迴圈 (`while True:`) 並使用 `break` 退出,這種技巧廣泛用於消除重複程式碼。在一些語言中稱為 "do-while loop"。 * 使用 `break` 和 `continue` 會讓迴圈有多個出口點,可能使程式流程追蹤和狀態判斷變得困難,需注意可讀性。 * **for 迴圈 (for statement)** * 另一種實現迴圈的方式,通常用於遍歷 (traverse) 序列或已知迭代次數的情況。 * 語法:`for variable in list: statements`。`variable` 會依序取得 `list` 中的每一個值。 * `list` 可以是一個明確列出的列表 `(1, 2, 3)` 或字串 (遍歷字元)。 * `range()` 函數常用於創建整數列表。 * `range(n)`:返回 0, 1, 2, ..., n-1 的序列。 * `range(m, n)`:返回 m, m+1, m+2, ..., n-1 的序列。 * `range(m, n, k)`:返回 m, m+k, m+2k, ... 的序列,步長為 k。 * 在 `for` 迴圈中手動修改迴圈計數器 (`variable`) 是無效的。 * **巢狀迴圈 (Nested loops)** * 迴圈可以像條件判斷一樣巢狀使用。外層迴圈、內層迴圈等。 * 當需要處理多維度情況時非常有用。例如印出 (x, y) 座標對或乘法表。 * `print()` 函數的 `end` 參數可以控制結尾字元,預設是換行 `\n`,可以設為 `end=" "` 來在每次列印後追加空白而非換行。 ## 第九部分:列表 (Lists) 和字串 (Strings) (Module 7) * **一次讀取多個值** * 使用 `input()` 讀取一行文字,然後使用字串的 `split()` 方法將其分割成列表。`split()` 預設按空白分割。CSV 檔案使用逗號分隔。 * **列表 (Lists)** * 一個有序的容器,儲存項目 (items)。 * 項目可以是不同資料型態的值 (整數, 浮點數, 字串, 甚至其他列表)。 * 使用索引運算子 `[]` 存取項目。第一個項目的索引是 0。 * 使用 `len()` 函數獲取列表的長度 (項目數)。 * **列表宣告 (List declaration)**: * 空列表:`a_list = []`。 * 包含重複項目的列表:`a_list = [0] * 3` 創建包含三個 0 的列表。 * 包含不同型態項目的列表:`a_list = [0, "hi", True]`。 * **向列表添加項目 (Putting items into a list)**:使用 `append()` 方法在列表末尾添加項目。 * **遍歷列表 (Traversing a list)**:使用 `for` 迴圈遍歷列表中的每個項目。 * **列表修改 (List modification)**:使用索引 `[]` 和指定運算子 `=` 修改列表中特定位置的值。 * 列表作為查找表 (lookup table) 的例子:根據輸入數字 (月份) 查找對應的名稱。 * **多維列表 (Multi-dimensional list)**:列表可以包含其他列表,形成二維或更高維的結構,例如表示井字遊戲棋盤。 * **列表操作方法 (List operations)**:列表物件提供了許多內建方法來操作列表。 * `<list>.append(x)`:在列表末尾添加元素 `x`。 * `<list>.sort()`:對列表排序。 * `<list>.reverse()`:反轉列表。 * `<list>.index(x)`:返回元素 `x` 第一次出現的索引。 * `<list>.insert(i, x)`:在索引 `i` 處插入元素 `x`。 * `<list>.count(x)`:返回元素 `x` 在列表中出現的次數。 * `<list>.remove(x)`:刪除列表中元素 `x` 第一次出現的位置。 * `<list>.pop(i)`:刪除列表中索引 `i` 處的元素並返回其值。 * **列表複製與參考 (List Copying and References)** * 在 Python 中,列表變數是一個**「參考」(reference)**,指向一組值所在的記憶體位置。 * 簡單的指定 `b_list = a_list` 只是複製了參考。這意味著 `a_list` 和 `b_list` 指向同一個列表物件。通過任一變數修改列表,都會影響到另一個。 * **手動深度複製 (Manual deep copy)**:如果需要創建一個完全獨立的列表副本,需要手動遍歷原列表並將每個項目添加到新列表中。例如使用 `for` 迴圈和 `append()`。 ## 第十部分:作業管理的應用 (Module 11-14) * **作業管理 (Operations Management, OM)** * 處理公司內部的「作業」(operations)。起初是生產管理 (Production Management),現包括服務管理、決策分析、供應鏈管理等。 * 典型問題:設施選址、生產計劃與排程、庫存控制、物流與運輸等。 * 可以通過編寫電腦程式來解決 OM 問題,本質是實現演算法 (algorithms)。 * **演算法 (Algorithms)** * 解決問題的一步一步的程序 (step-by-step procedure)。 * 精確描述了在每個時刻要做什麼來完成任務。 * 每個步驟 (action) 都必須是可行的 (doable)。例如撲克牌排序就是一個演算法。 * **排程 (Scheduling) 應用:最小化最大完工時間 (Makespan minimization)** * 問題:將一批工作 (jobs) 分派到多台機器 (machines) 上加工,已知每項工作的加工時間 (processing time $p_j$),目標是最小化所有工作中最晚的完工時間 (makespan)。 * 最大完工時間最小化是為了負載均衡 (load balancing)。 * 這類問題通常是「困難的」(hard),難以找到通用的最佳解。 * 常使用啟發性演算法 (heuristic algorithms):易實現、易執行、時間效率高,結果通常不錯 (接近最佳解)。 * **最長加工時間優先法則 (Longest processing time first, LPT)** * 一種著名的最小化最大完工時間啟發性演算法。 * 步驟:1. 按加工時間降序排序工作。2. 在每個迭代中,將當前工作分派到目前負載最輕的機器 (即完工時間最早的機器)。 * LPT 是一個迭代演算法 (iterative algorithm) (分迭代執行) 和貪婪演算法 (greedy algorithm) (每步做出當時最好的選擇)。 * 即使跳過排序步驟,演算法表現仍然「不錯」。 * **LPT 實現 (不排序版本)** * 程式步驟:1. 讀取輸入 (n 工作數, m 機器數, p 加工時間列表)。2. 準備機器負載列表 (`loads`, 初始化為 0) 和工作分派結果列表 (`assignment`)。3. 迭代分派:遍歷每個工作 `j`,找到目前負載最輕的機器,將工作 `j` 分派給它,更新機器負載 (`loads[least_loaded_machine] += p[j]`) 和工作分派結果 (`assignment[j] = least_loaded_machine + 1`)。4. 輸出結果 (`assignment`, `loads`)。 * LPT 有最差性能保證 (worst-case performance guarantee),證明其結果 $z_{LPT}$ 與最佳解 $z^*$ 的比值 $z_{LPT} / z^* \le 4/3$。不排序版本的近似因子為 2。實現通常比理論分析容易。 * **庫存控制 (Inventory Control) 應用:(Q, R) 策略** * 庫存常見於零售、製造業,作用是緩衝供需、平衡訂購和持有成本。 * (Q, R) 策略:當庫存水平降至或低於再訂購點 R 時,訂購數量 Q 的貨物。 * (Q, R) 策略實現:讀取 Q, R, 初始庫存 I,進入無限迴圈模擬每天的銷售 `sales`。更新庫存 `I = I - sales` (需處理庫存不足的情況,設為 0)。檢查 `if I < R:`,如果滿足則訂購並更新庫存 `I = I + Q`。列印每天結束時的庫存。 * 優化 (Q, R) 參數:目標是最小化總成本,包括庫存成本 (Inventory cost)、訂購成本 (Ordering cost) 和缺貨成本 (Shortage cost)。 * 尋找最佳 R 的代理方法:給定 Q 和過去的銷售數據,尋找在這些銷售數據下總成本最低的 R 值。這假設未來需求模式不變,且需要過去的需求數據而非僅銷售數據。 * 實現尋找最佳 R:1. 準備過去銷售數據列表 (`sales`)。2. 準備成本參數 (`stg_cost`, `inv_cost`), Q, 初始 I。3. 遍歷可能的 R 值 (例如從 0 到 Q-1)。4. 對每個 R,模擬過去銷售數據下的庫存變化,計算總成本。5. 追蹤並更新總成本最低的 R 值 (`best_r`) 和對應成本 (`cost_of_best_r`)。6. 列印 `best_r`。 * **物流與運輸 (Logistics and Transportation) 應用:旅行推銷員問題 (Traveling Salesperson Problem, TSP)** * 物流與運輸是供應鏈和服務管理常見問題,涉及路徑、數量、時機等。 * TSP 是一個著名的問題,目標是找到一條經過所有指定地點一次並最終回到起點的最短路徑。它是車輛路徑問題 (Vehicle Routing Problem) 的特殊情況 (車輛容量無限)。 * TSP 的貪婪演算法 (Greedy algorithm for TSP):一種簡單的啟發性演算法。從起點開始,總是選擇前往未訪問地點中離當前位置最近的那個。重複此步驟直到回到起點。 * 貪婪演算法不一定找到最佳路徑,但通常提供不錯的近似解。 * **貪婪演算法實現 (TSP)** * 程式步驟:1. 設定地點數量 (`num_loc`), 設定距離矩陣 (`dst`, 二維列表,`dst[i][j]` 是地點 i 到地點 j 的距離)。2. 設定起點 (`origin`)。3. 準備表示路徑的列表 (`tour`, 初始化為起點) 和未訪問地點列表 (`unvisited`, 初始化包含除起點外的所有地點)。總距離 (`tour_len`, 初始化為 0)。4. 迭代:迴圈執行 `num_loc - 1` 次 (因為要訪問 `num_loc - 1` 個其他地點)。在每次迭代中,找到離當前位置 (`cur`) 最近的未訪問地點 (`next`)。將 `next` 從 `unvisited` 移到 `tour`。更新 `tour_len`。將 `cur` 更新為 `next` 進入下一個迭代。5. 完成路徑:從最後一個訪問地點回到起點,更新 `tour_len`。6. 列印結果 (`tour`, `tour_len`)。 * **改進輸入過程** * 為了方便測試,可以將輸入數據保存在純文字檔案中 (例如 `"in.txt"`)。 * 在終端機執行程式時,使用輸入重定向 `<` 來從檔案讀取輸入,例如 `python TSP.py < TSP_in.txt`。 * **應用總結** * 許多量化決策可以通過電腦和程式來支援。 * 前提是掌握好的演算法並知道如何程式設計。 * 解決實際問題需要領域知識 (domain knowledge)。 * 懂得程式設計使你能實現解決方案 (自己做或與工程師合作),嘗試不同的解決方案,並促進「做中學」(learning by doing)。 ## 第十一部分:函數 (Functions) (Module 9 & 10) * **基本概念 (Basic concepts)** * 函數是一個命名的語句序列 (named sequence of statements),執行一個計算任務。 * 使用函數需要定義 (Define) 和呼叫 (Call / invoke)。 * 可以向函數傳遞輸入變數 (即引數, arguments)。 * 函數可以返回輸出值 (傳回值, return values)。 * Python 有許多有用的內建函數 (built-in functions)。 * 定義函數使用關鍵字 `def`,後接函數名和參數列表 (parameters),以冒號 `:` 結尾。 * 函數體內的語句必須縮排。縮排大小不限但需一致。 * 可以在函數定義後提供文件字串 (`""" Docstring """`) 說明函數功能。 * **參數 (Parameters) 與 引數 (Arguments)** * 函數定義中的變數是參數 (parameters),用於接收輸入值。 * 呼叫函數時傳遞的實際值是引數 (arguments)。 * 通過傳遞引數來指定參數的值。 * **預設引數 (Default Arguments)**:可以在函數定義時給部分參數設定預設值。呼叫時如果沒有為該參數傳遞引數,就使用預設值。 * **使用參數名稱呼叫 (Calling with Parameter Names)**:呼叫函數時可以使用 `參數名=引數值` 的方式傳遞引數。這樣做時,引數的順序就不重要了。 * **變數視野 (Scope)** * 函數內定義的參數和變數是區域變數 (local variables)。只存在於函數內部,函數結束後消失。 * 在 `.py` 檔案最外層定義的變數是全域變數 (global variables)。可以在函數內部存取全域變數。 * 函數內不能直接修改同名的全域變數的值,Python 會創建一個同名的區域變數。 * 如果需要在函數內部修改全域變數的值,必須使用 `global` 關鍵字來宣告該變數是全域變數。否則,外部世界看不到函數內對該全域變數的修改。 * **傳回值 (Return Values)** * 函數使用 `return` 關鍵字將值發送回呼叫者。 * 就像數學函數一樣,可以將函數的輸出作為另一個函數的輸入 (chaining)。 * 函數可以返回多個值,它們會被打包成一個 tuple 返回。呼叫者可以使用多個變數來接收並自動解包 (unpack) 這些返回值。 * **如果函數沒有 `return` 語句**:Python 仍然會執行到函數結束。所有 Python 函數都會返回值,如果沒有顯式指定,函數會返回一個特殊對象 `None`。 * 常見錯誤是編寫一個應返回值但忽略 `return` 的函數。 * **引數傳入法 (Arguments Passing) - Call by Assignment** * 函數能否通過引數改變外部變數的值,取決於程式語言的設計 (Call by Reference vs. Call by Value)。 * Python 採用 Call by Assignment。其效果取決於傳入的引數物件是不可變的 (immutable) 還是可變的 (mutable)。 * **不可變物件 (Immutable objects)**:字串 (string), 整數 (int), 浮點數 (float), decimal, complex, bool, tuple, range, frozenset, bytes 等。函數內對不可變物件的修改不會影響函數外部的變數。效果類似 Call by Value。 * **可變物件 (Mutable objects)**:列表 (list), 字典 (dictionary), 集合 (set) 等。函數內對可變物件的修改會影響函數外部的變數。效果類似 Call by Reference。 * **遞迴 (Recursion) 與 除錯 (Debugging)** * **遞迴 (Recursion)**:函數呼叫自身。計算階乘是經典例子。 * 遞迴定義需要基本情況 (base case) (終止遞迴的條件,例如 0! = 1) 和遞迴步驟 (recursive step) (將問題分解為更小的相同問題,例如 n! = n * (n-1)!)。 * **處理非法輸入 (Handling Illegal Inputs)**:遞迴函數尤其需要檢查輸入有效性,否則可能導致無限遞迴和執行期錯誤 (RuntimeError)。 * 創建守衛 (guardian) 來檢查輸入型態 (`isinstance()`) 和值範圍,如果無效則返回 `None` 或拋出錯誤。 * **除錯 (Debugging)**:找出並修復程式錯誤。 * 函數的使用有助於將問題隔離到特定函數中。 * 常見問題原因:輸入錯誤 (無效引數)、函數邏輯錯誤、函數輸出被錯誤使用。 * **如何定位問題 (Pin down the causes)**: * **列印狀態 (Print out status)**:在程式關鍵點插入 `print()` 語句,輸出變數值或流程信息,觀察程式執行情況。這些 `print` 語句可以看作是「鷹架」(scaffolding),除錯完成後再移除。 * 使用內建的除錯器 `pdb`。