本章目標理解有限預算下的組合選擇為什麼不能只看單價或單一效益
用 Python 建立可重現的 0/1 背包求解流程
把採購、訂閱、訓練與功能排程轉成可驗證的最佳化問題
情境與限制情境:你手上只有固定預算,卻要從一串工具、課程、活動或功能需求中挑出最值得投入的組合
輸入:每個方案的名稱、成本與價值分數
輸出:在不超出預算的前提下,總價值最高的方案清單
限制:每個方案只能選一次,不能切半買,也不能超支
可重現規則本章程式碼位置:
examples/ch07/本章核心模組:
examples/ch07/budgeting.py測試:
pytest -q tests/test_ch07_budgeting_and_combinations.py典型用途:年度採購、教育訓練排程、SaaS 工具訂閱、需求排程取捨
有限預算下的組合選擇¶
很多現場決策不是「能不能做」,而是「錢不夠時該先做哪些」。如果你只看單價,可能會錯過一組雖然比較貴、但整體更有價值的組合;如果你只看單一方案的價值,也可能把預算卡在少數大項目上,反而失去更好的搭配。
這類問題很適合用 0/1 背包來理解。每個方案不是選,就是不選;我們的目標是在固定預算內,找出總價值最高的組合。這個模型很常出現在採購清單、課程編排、產品功能排程,甚至是團隊季度目標排序。
為什麼不能只看「價值最高」或「最便宜」¶
實務上常見兩種直覺,但都容易出錯:
| 直覺策略 | 可能的問題 |
|---|---|
| 先挑價值最高的方案 | 可能一下就花掉大半預算,剩下的資源不足以形成最佳組合 |
| 先挑最便宜的方案 | 雖然看起來買很多,但總價值可能偏低 |
背包問題的關鍵,在於它不是單看某一個方案,而是比較「整組搭配」的結果。
你可以把這一章想成在回答兩個問題:
在固定上限內,哪些方案組合起來最划算?
為什麼最佳答案有時不是最貴那個,也不是最多件那組?
實作範例¶
請參考 examples/ch07/budgeting.py。請在專案根目錄執行程式,以確保路徑正確。
from examples.ch07.budgeting import select_best_portfolio
items = [
{"name": "方案A (便宜但普通)", "cost": 1, "value": 15},
{"name": "方案B (中等)", "cost": 3, "value": 40},
{"name": "方案C (極好但昂貴)", "cost": 4, "value": 50},
{"name": "方案D (便宜且不錯)", "cost": 2, "value": 20},
]
budget = 5
best_value, selected_items = select_best_portfolio(items, budget)
print(f"最大價值: {best_value}")
# 最大價值: 60
print("選擇的方案:", [item["name"] for item in selected_items])
# 選擇的方案: ['方案B (中等)', '方案D (便宜且不錯)']
# 說明:方案B(40) + 方案D(20) = 60,成本 3+2 = 5。勝過單選方案C(50)。示範流程包含:
驗證預算與方案成本是否合法
用動態規劃比較每個預算上限下的最佳總價值
回溯還原實際被選中的方案清單
在價值相同時保留穩定且可解釋的選擇結果