Solve employee shift scheduling in Python
Shift scheduling (workforce rostering) asks: given how many staff each day needs and a set of allowed shift patterns, how few people cover every day? It is the classic days-off scheduling problem. With quicopt it is a short MILP in Python — and note this is rostering, distinct from balancing jobs across machines (that's the makespan guide).
The naive approach
Hand-rolled rostering loops or a greedy "assign until covered" pass produce a workable schedule but rarely the leanest one, and they get brittle as patterns and rules multiply. The problem is a linear integer program — solve it exactly.
Model it as a MILP
Each shift pattern covers a fixed set of days. An integer variable counts workers on each pattern; every day's coverage must meet its demand; minimize total staff:
from ortools.math_opt.python import mathopt
from quicopt import Client
demand = [17, 13, 15, 19, 14, 16, 11] # staff needed per weekday (Mon..Sun)
D = 7
# pattern p works 5 consecutive days starting day p (off the other 2)
covers = [[(1 if (d - p) % 7 < 5 else 0) for d in range(D)] for p in range(D)]
model = mathopt.Model(name="shift_scheduling")
w = [model.add_integer_variable(lb=0.0, name=f"pattern_{p}") for p in range(D)]
for d in range(D):
model.add_linear_constraint(sum(covers[p][d] * w[p] for p in range(D)) >= demand[d])
model.minimize(sum(w))
print(Client("https://try.quicoptapi.pgi.fz-juelich.de").solve(model).display)
├── status: optimal ├── feasible: true ├── objective: 23.0 ├── x: pattern_0=7, pattern_1=5, pattern_2=1, pattern_3=8, pattern_4=0, pattern_5=2, … (7 variables) └── solve_time: 0.0121 s
What you get
status: optimal means 23 workers is proven the fewest that cover every
day's demand with these five-on/two-off patterns. The solver picks how many staff
start on each weekday; a greedy roster would likely overshoot.
Coverage minimums, per-pattern costs, and skill requirements are all extra linear constraints on the same model — you change the data, not the method.
Next
- The problem class behind this: Mixed-integer linear (MILP)
- Balancing jobs across machines instead: Job scheduling (makespan)
- Set up the client and solve your first model: Getting started
Reference: K. R. Baker, Workforce Allocation in Cyclical Scheduling Problems: A Survey, Operational Research Quarterly, 1976.
A bigger roster — or a different problem?
Tell us what you're optimizing. We'll help you model it and point you at the right approach.
Frequently asked questions
Is this the same as the makespan scheduling guide?
No. That one balances jobs across machines to finish early. This one is workforce rostering — choosing shift patterns to cover each day's demand at minimum staff. Different model, both MILP.
Isn't this a nonlinear / binary-only model?
No — it uses general integer variables (how many workers on each pattern), which linear models (MILP) accept. The binary-only restriction only applies to nonlinear models on the free tier.
Is quicopt free to use?
Yes — pip install quicopt and your first call sets up a free key, no license.