Part 5 · 1 chapters · ~8 min
Living with Hard Problems
Exponential algorithms with pruning for small inputs, approximation algorithms with guarantees, heuristics and local search, SAT, SMT, constraint programming and integer linear programming solvers, fixed-parameter tractability, and product design that avoids needing exact answers.
7
A solver in twenty lines
code
# Python, Google OR-Tools CP-SAT: assign 6 support shifts to 4 agents, no one twice in a row, balance load
from ortools.sat.python import cp_model
m = cp_model.CpModel()
agents, shifts = range(4), range(6)
x = {(a, s): m.NewBoolVar(f"x{a}_{s}") for a in agents for s in shifts}
for s in shifts: m.AddExactlyOne(x[a, s] for a in agents) # every shift covered once
for a in agents:
for s in range(5): m.Add(x[a, s] + x[a, s + 1] <= 1) # no back-to-back shifts
m.Add(sum(x[a, s] for s in shifts) <= 2) # at most 2 shifts each
solver = cp_model.CpSolver(); solver.parameters.max_time_in_seconds = 2.0
status = solver.Solve(m) # OPTIMAL / FEASIBLE / INFEASIBLEcode
// heuristic: first-fit decreasing bin packing (uses at most about 11/9 of the optimal bins plus a constant)
const pack = (sizes: number[], cap: number) => sizes.sort((a, b) => b - a).reduce((bins: number[], s) => {
const i = bins.findIndex(b => b + s <= cap); i < 0 ? bins.push(s) : (bins[i] += s); return bins; }, []);LIVING WITH HARD PROBLEMS
what engineers actually do
swipe the figure sideways, or tap expand for full screen
1/4
brute force is fine sometimes
A 10-stop route has 10! = 3,628,800 orderings, checkable in about a second; at 20 stops there are 2.4 × 10¹⁸ and brute force is hopeless. Know your n.
know your nsmall instances are easy