WAITING LINES / SIMULATION LAB

Queueing Simulation Lab.

A formula can predict how long customers wait at a service counter, but it rests on assumptions. This lab plays out every customer instead. Each one arrives at a random moment, waits in line and gets served. Watch the line grow and shrink. Then check the results against the formula as the run goes, or across 20 repeat runs.

Waiting line · simulated customer by customer

Watch a line form, then check it against the formula

random arrivals · repeatable runs · formula check

SIM 0h 00m · 20 MIN/S · SEED 7

DRAG TO ROTATE

Customers arrive at random. When every server is busy, they wait in one line for the next free one. Early on, the running averages jump around. Watch them settle toward the dashed lines in the charts, which show what the formula (Erlang C) predicts.

Waiting now0run average – · formula 2.84
Average wait–formula 21.3 min · 0 served
Servers busy–share of time · formula 80%
Served per hour–arrivals average 8/hr

Scenario

Arrivals bring enough work to keep 1.60 servers busy full time (the workload, a = λ/μ). Spread over 2, that’s ρ = 0.80, the share of time each server is busy. Changing a setting restarts the run, so every number matches what you see.

Speed: simulated minutes per real second

Run

Seed 7 fixes the random draws (a seed is the starting number for the random numbers). Reset replays this exact run, and New random run draws a fresh one. Arrival times and service times are drawn separately. So with the same seed, the same customers show up with the same service needs, however many servers there are.

Number waiting over simulated time

Collecting the first simulated minutes…

Running averages of customers who have left

Waiting for the first departures…

This run against the formula (Erlang C)

MeasureSimulatedFormulaGap
Number waiting (Lq)–2.84–
Number inside (L)–4.44–
Wait in line, min (Wq)–21.3–
Time inside, min (W)–33.3–
Servers busy–80.0%–
Share who wait–71.1%–
Served per hour–8.00–

One run is one random sample, so most of each gap is luck. At ρ ≈ 0.8, separate 2000-hour runs land about ±10% from the formula’s wait (one standard deviation, the typical size of a miss). These numbers start from an empty room and include the warm-up, the early stretch while the line is still building up. A rough ± range on the average wait (Wq) appears after 449 simulated hours here. It waits until the run has lasted 50 “relaxation times” and should have at least 1500 departures and 50 waits. One relaxation time, 1/(cμ(1−√ρ)²), is roughly how long the line takes to forget how it started. A range shown any earlier would be far too sure of itself.

Little’s Law on this run

CheckCountedλobs × averageGap
L = λobs · W–––
Lq = λobs · Wq–––

Little’s Law: the average number of people inside equals the arrival rate times the average time each one spends inside. “Counted” is the average number inside over time: the area under the count-over-time curve ÷ elapsed time. λobs is the arrival rate this run saw: arrivals ÷ elapsed time = –/hr. W and Wq only average customers who have left, so the gap comes from the customers still inside. It shrinks as the run gets longer, unless ρ ≥ 1, where the people left inside keep piling up.

Last customers out

#ArrivedStartedLeftWaitServer
No departures yet.

How long customers wait

Collecting waits: 0 of 20 customers have left. The chart needs 20 before it means anything.

Repeat runs: one run is one sample

Runs 20 separate copies of this scenario. Each one throws away a 100 h warm-up (the start, while the line fills up from empty) and then measures 500 h. Then it checks whether the formula’s values fall inside the 95% confidence intervals: the ranges where the true averages very likely sit. That’s the least you need to turn a simulation into an estimate.

How the model works, how it’s checked and what it leaves out

How it works

This is an M/M/c queue (shorthand for random arrivals, random service times and c servers), played out one event at a time. Customers arrive at random at an average rate of λ per hour (a Poisson arrival process, with exponential gaps between arrivals). Each server works at an average rate of μ per hour, with random (exponential) service times. There are c identical servers and one first-come, first-served (FIFO) line. The waiting room has no limit, and nobody gives up.

The clock jumps straight from one event to the next: an arrival or a finished service. A binary heap, a to-do list that always knows what comes next, keeps the events in order. So there are no fixed time steps.

A customer who finds a free server goes to the one that’s been free longest. A server that finishes while people are waiting takes the person at the front. Gaps between arrivals and service times come from two separate random streams, each with its own seed. Lq and L are the areas under the count-over-time curves ÷ elapsed time. Wq and W average the customers who have left. Utilization, the share of time servers are busy, is busy-server time ÷ (c × T), where T is the elapsed time. A live run stops at 2000 simulated hours. With arrivals stopped, it runs past that only until the last customer has left.

How it’s checked

Checked in code, without the 3D view. The area under the number-inside curve equals the total of every customer’s time inside, to about 10⁻¹³ relative error. Little’s Law holds on the simulated averages to better than 0.01%. With seed 2024, 40,000-hour runs in five settled cases land within 5% of Erlang C on Wq and Lq, and within 0.5% on utilization. Across 20 other seeds, single runs that long missed by up to 5.6% on Lq and 0.7% on utilization (M/M/1 at ρ = 0.8). A fixed seed replays the exact same run.

With the same seed, adding a server never made any customer wait longer in the cases tested. Stopping arrivals empties the room, and after that the number served per hour is exactly zero. The repeat-run ranges held the Erlang C wait in 183–187 of 200 trials at ρ = 0.6 and 0.8. When fewer than 20 waits were expected across all the runs, the Wq range held it only 66–85% of the time. That’s why those rows say “too rare”. The single-run ± range stays hidden until 50 relaxation times have passed. In ten scenarios it then held the true average in 83–89% of runs, and 84–95% by 2000 h. That’s why it’s called rough.

What it leaves out

Nobody gives up and leaves (abandonment), and nobody sees a long line and walks away (balking). The waiting room never fills, there are no priority customers, and any server can help anyone. Service times always follow the exponential pattern, and the arrival rate and the staff on duty never change through the day. Walking time isn’t modelled either. In the model, a customer is in line or at a server the instant the event happens. The walk you see is just the animation catching up.

← Queueing & StaffingAll LabsProcess Stability →

The Inner Universe

Still in the works.

I’m still writing this part of the site: a map of the big questions I keep coming back to. Check back soon.