Build a versioned shift simulator, compare baseline and flex staffing on paired scenarios, and publish a decision packet with numerical and model uncertainty kept separate.
Project: simulate warehouse capacity under shared shocks
Set the release contract
The decision is whether to reserve three flex workers per shift. One shift is one replication. Model routine orders from 34 through 47. A disruption occurs with probability 0.27, adds 18 through 26 orders and removes two of eight scheduled workers. Each available worker clears seven orders; unresolved orders cost 210 units each, and each flex worker costs 85 units per shift. These are illustrative fixture inputs, not calibrated operating estimates. The estimand is expected net cost difference and overflow risk under this model.
Verify a hand-calculated shift
A disrupted shift with 41 routine orders and 21 shock orders has 62 total orders and six base workers. Baseline capacity is 42, leaving 20 unfinished. Three flex workers increase capacity to 63, leaving none unfinished. Baseline penalty is 4,200 cost units; flex pays 255 units for the workers. That paired shift favors flex by 3,945 cost units. Check this calculation before any large random run.
Preserve the joint operating state
Draw disruption once, then use it for both the demand increment and staff loss. Applying independent disruption draws to those inputs would make the dangerous high-demand, low-staff combination artificially rare. Pass the same realized shift to both policies. The joint-input lesson shows how even correct marginal distributions can yield a wrong overflow estimate.
Quantify simulation and model uncertainty
Run a fixed replication budget and retain paired per-shift net savings. Report mean cost for each policy, overflow shares, mean net saving and the Monte Carlo standard error of that saving. Then rerun with plausible disruption frequencies, labor costs and per-order penalties; a larger replication count only reduces numerical error under the selected inputs. Replication planning and scenario stress address different uncertainty layers.
Package the decision for review
Archive input distributions with their historical date ranges, a shift-level data-quality check, seed, code revision, run count, one worked shift and output summaries. State whether the cost model includes customer impact of delayed orders and whether flex staff are available during disruptions. If the historical disruption rate is based on a tiny sample or the cost ranking flips under credible inputs, present both policies as unresolved and name the data needed next. The code below supplies only a transparent teaching baseline.
Implementation
from math import sqrt
from random import Random
from statistics import mean, stdev
def evaluate_capacity_policy(replications, seed):
if replications < 2:
raise ValueError("at least two shifts required")
draw_stream = Random(seed)
baseline_costs, flex_costs, savings = [], [], []
baseline_overflows = flex_overflows = 0
for _ in range(replications):
disrupted = draw_stream.random() < 0.27
routine_orders = draw_stream.randint(34, 47)
extra_orders = draw_stream.randint(18, 26) if disrupted else 0
orders = routine_orders + extra_orders
available_staff = 8 - (2 if disrupted else 0)
baseline_backlog = max(0, orders - 7 * available_staff)
flex_backlog = max(0, orders - 7 * (available_staff + 3))
baseline_cost = 210 * baseline_backlog
flex_cost = 3 * 85 + 210 * flex_backlog
baseline_costs.append(baseline_cost)
flex_costs.append(flex_cost)
savings.append(baseline_cost - flex_cost)
baseline_overflows += baseline_backlog > 0
flex_overflows += flex_backlog > 0
return {"baseline_mean_cost": mean(baseline_costs),
"flex_mean_cost": mean(flex_costs),
"mean_net_saving": mean(savings),
"saving_mc_error": stdev(savings) / sqrt(replications),
"baseline_overflow_share": baseline_overflows / replications,
"flex_overflow_share": flex_overflows / replications}
report = evaluate_capacity_policy(24000, 499)
assert report["baseline_overflow_share"] >= report["flex_overflow_share"]
assert abs(report["mean_net_saving"] -
(report["baseline_mean_cost"] - report["flex_mean_cost"])) < 1e-9Performance and operating cost
Generating R shifts and evaluating two fixed policies costs O(R) time and O(R) space in this auditable implementation. Streaming means and variance reduce memory to O(1). Input estimation, validation, and stress scenarios are separate work and cannot be replaced by more replications.
Common Mistakes
- Do not treat fixture costs and disruption rates as observed business facts.
- Do not draw a new shift for the second policy in a paired comparison.
- Do not confuse a small Monte Carlo error with a reliable operational forecast.
