切割與包裝問題

工業切割問題是排箱問題的變體:

  • 鋼鐵廠:如何切割鋼板才能讓廢料最少?
  • 服裝廠:如何在布料上排列版型才能最省布?
  • 木工廠:如何切割木板才能得到最多的成品?

2D 排箱 (2D Bin Packing)

from ortools.sat.python import cp_model

# === 矩形物品 ===
rectangles = [
    (3, 2),   # 物品 0
    (4, 3),   # 物品 1
    (2, 2),   # 物品 2
    (5, 2),   # 物品 3
    (3, 3),   # 物品 4
    (2, 4),   # 物品 5
    (4, 2),   # 物品 6
    (3, 4),   # 物品 7
]

bin_width = 10
bin_height = 10

num_items = len(rectangles)

model = cp_model.CpModel()

# 每個物品的位置與旋轉
x = [model.NewIntVar(0, bin_width, f"x_{i}") for i in range(num_items)]
y = [model.NewIntVar(0, bin_height, f"y_{i}") for i in range(num_items)]

# 是否旋轉
rotated = [model.NewBoolVar(f"rot_{i}") for i in range(num_items)]

# 寬與高(考慮旋轉)
w = [model.NewIntVar(0, bin_width, f"w_{i}") for i in range(num_items)]
h = [model.NewIntVar(0, bin_height, f"h_{i}") for i in range(num_items)]

for i in range(num_items):
    # 如果旋轉則寬高交換
    model.Add(w[i] == rectangles[i][0]).OnlyEnforceIf(rotated[i].Not())
    model.Add(w[i] == rectangles[i][1]).OnlyEnforceIf(rotated[i])
    model.Add(h[i] == rectangles[i][1]).OnlyEnforceIf(rotated[i].Not())
    model.Add(h[i] == rectangles[i][0]).OnlyEnforceIf(rotated[i])
    # 在邊界內
    model.Add(x[i] + w[i] <= bin_width)
    model.Add(y[i] + h[i] <= bin_height)

# 物品不重疊(使用 NoOverlap2D)
intervals_x = [model.NewIntervalVar(x[i], w[i], x[i] + w[i], f"ix_{i}") 
               for i in range(num_items)]
intervals_y = [model.NewIntervalVar(y[i], h[i], y[i] + h[i], f"iy_{i}")
               for i in range(num_items)]
model.AddNoOverlap2D(intervals_x, intervals_y)

# 求解
solver = cp_model.CpSolver()
solver.parameters.max_time_in_seconds = 30
status = solver.Solve(model)

if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
    print(f"\n=== 2D 排箱結果 ===")
    for i in range(num_items):
        rx = solver.Value(x[i])
        ry = solver.Value(y[i])
        rw = solver.Value(w[i])
        rh = solver.Value(h[i])
        rot = "旋轉" if solver.Value(rotated[i]) else ""
        print(f"物品 {i} ({rectangles[i]}): ({rx},{ry}) → ({rx+rw},{ry+rh}) {rot}")
    
    # 計算使用率
    total_area = bin_width * bin_height
    used_area = sum(rw * rh for i in range(num_items)
                    for rw, rh in [(solver.Value(w[i]), solver.Value(h[i]))])
    print(f"\n板材面積: {total_area}")
    print(f"使用面積: {used_area}")
    print(f"使用率: {used_area/total_area:.1%}")
else:
    print("找不到可行排法")

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!