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:

shift_scheduling.py
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)
$ python shift_scheduling.py
├── 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

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.

Talk to us →

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.