Why Erlang C Needs a Numerical Method
For an M/M/c queue, Erlang C is the stationary probability that an arriving customer must wait because all \(c\) servers are busy. Its formula is compact, but a direct computation can be unreliable when \(c\) is large: terms such as \(a^n/n!\) may overflow or underflow even when their ratios, and the final probability, are ordinary-sized. The remedy is to compute relative weights recursively, scale them before summing, and treat the infinite tail as a geometric series.
Assume \(c\) is a positive integer, the arrival rate is \(\lambda\ge0\), and the service rate of each server is \(\mu>0\). Write \(a=\lambda/\mu\) for offered traffic. When \(\lambda>0\), stability requires \(a<c\); equivalently, the utilization \(\rho=a/c\) is less than \(1\). If \(\lambda=0\), no customer arrives and the delay probability is zero.
The queue-length process \((X_t)_{t\ge0}\) is a continuous-time Markov chain on \(S=\{0,1,2,\ldots\}\), on a probability space \((\Omega,\mathcal F,\mathbb P)\). In state \(n\), arrivals occur at rate \(\lambda\) and departures at rate \(\min(n,c)\mu\). Thus its generator has \(q_{n,n+1}=\lambda\), \(q_{n,n-1}=\min(n,c)\mu\) for \(n\ge1\), and \(q_{n,n}=-(\lambda+\min(n,c)\mu)\); the diagonal makes every generator row sum to zero. The stationary state probabilities are proportional to the weights \(w_n=a^n/n!\) for \(0\le n<c\) and \(w_n=a^n/(c!c^{n-c})\) for \(n\ge c\). These are the stationary weights for the stable birth-death queue; only their ratios are needed below.
Scaling and Recursion
Evaluating powers and factorials separately is unnecessary. For the finite set of weights \(w_0,\ldots,w_{c-1}\), choose an index \(m=\min(\lfloor a\rfloor,c-1)\) and scale by \(w_m\). Define \(x_n=w_n/w_m\). Since \(w_{n+1}/w_n=a/(n+1)\), the relative weights can be generated from \(x_m=1\) by stepping in either direction:
Set \(S_c=\sum_{n=0}^{c-1}x_n\), and extend the recursion once to obtain \(x_c=x_{c-1}a/c\). Because every weight has been divided by the same positive number \(w_m\), the scaled finite sum is \(S_c\), and the scaled tail weight is \(x_c/(1-\rho)\). Therefore the probability can be computed without constructing any unscaled \(w_n\).
For numerical work, it is often preferable to avoid forming the potentially large tail weight \(x_c/(1-\rho)\). Dividing numerator and denominator by that tail weight gives an equivalent expression:
When \(a\) and \(c\) are available, compute \(1-\rho\) as \((c-a)/c\), rather than subtracting a rounded value of \(\rho\) from \(1\). The recursion uses \(O(c)\) multiplications and additions. In floating-point arithmetic, a very small \(x_c\) or an extreme ratio can still exceed the representable range; a log-scale calculation is then safer. The scaled recursion greatly reduces avoidable overflow, but it cannot make every possible input representable.
Proof. For \(0\le n<c\), define \(w_n=a^n/n!\), where \(a>0\). Direct division gives \(w_{n+1}/w_n=a/(n+1)\) and \(w_{n-1}/w_n=n/a\) whenever the indicated indices exist. Starting at \(x_m=1=w_m/w_m\), each upward recursion therefore produces \(x_{n+1}=w_{n+1}/w_m\), and each downward recursion produces \(x_{n-1}=w_{n-1}/w_m\). Induction in each direction proves \(x_n=w_n/w_m\) for all \(0\le n<c\), and the extra upward step proves the same identity for \(x_c\). For \(n\ge c\), the stationary weights have successive ratio \(a/c=\rho\), so their total is \(w_c\sum_{r=0}^{\infty}\rho^r=w_c/(1-\rho)\); this series converges because \(0<\rho<1\). Dividing both finite and tail weights by \(w_m>0\) gives \(S_c\) and \(x_c/(1-\rho)\). Substitution into the defining probability proves the scaled formula. Dividing its numerator and denominator by \(x_c/(1-\rho)>0\) gives \(1/[1+S_c(1-\rho)/x_c]\), as claimed. \(\square\)
This argument also explains the role of the anchor index. The ratio \(w_{n+1}/w_n=a/(n+1)\) is at least \(1\) up to the mode and at most \(1\) after it. The chosen \(m\) is a mode of the finite sequence \(w_0,\ldots,w_{c-1}\), so \(0<x_n\le1\) throughout that sequence. Anchoring at an extreme index instead can create very large relative weights, particularly for low offered traffic.
Worked Calculations and Checks
Worked Example: Three Servers with Exact Scaled Weights
Take \(c=3\), \(\lambda=4\), and \(\mu=2\). Then \(a=\lambda/\mu=4/2=2\), and \(\rho=a/c=2/3<1\), so the stability condition holds. The finite weights are \(w_0=1\), \(w_1=2\), and \(w_2=2^2/2!=2\). The tail-start weight is \(w_3=2^3/3!=8/6=4/3\). The tail multiplier is \(1/(1-\rho)=1/(1-2/3)=3\), so the total tail weight is \((4/3)3=4\). The finite sum is \(1+2+2=5\), giving
Scaled check. Here \(m=\min(\lfloor2\rfloor,2)=2\), so the anchor weight is \(w_2=2\). The relative finite weights are \(x_0=1/2\), \(x_1=1\), and \(x_2=1\); the next term is \(x_3=x_2(2/3)=2/3\). Thus \(S_3=1/2+1+1=5/2\), and \(x_3/(1-\rho)=(2/3)/(1/3)=2\). The scaled probability is \(2/(5/2+2)=2/(9/2)=4/9\), agreeing with the unscaled calculation.
Normalization and utilization check. The normalization denominator is \(9\), so the probabilities for states \(0,1,2\) are \(1/9,2/9,2/9\), and the probability of \(X_t\ge3\) is \(4/9\). They sum to \(1/9+2/9+2/9+4/9=1\). The expected number of busy servers is \(1(2/9)+2(2/9)+3(4/9)=2/9+4/9+12/9=2\), equal to \(a=2\); dividing by \(c=3\) gives utilization \(2/3=\rho\).
Worked Example: A Four-Server Calculation Near Saturation
Let \(c=4\), \(\lambda=3\), and \(\mu=1\), so \(a=3\) and \(\rho=3/4<1\). The finite weights, computed from \(a^n/n!\), are \(w_0=1\), \(w_1=3\), \(w_2=9/2\), and \(w_3=27/6=9/2\). Their sum is \(1+3+9/2+9/2=13\). The next weight is \(w_4=81/24=27/8\). The tail multiplier is \(1/(1-3/4)=4\), so the full tail weight is \((27/8)4=27/2\). Therefore
Scaled check. The anchor is \(m=\min(\lfloor3\rfloor,3)=3\), with \(w_3=9/2\). The relative finite weights are \(x_0=1/(9/2)=2/9\), \(x_1=3/(9/2)=2/3\), \(x_2=(9/2)/(9/2)=1\), and \(x_3=1\). The next term is \(x_4=x_3(3/4)=3/4\). Hence \(S_4=2/9+2/3+1+1=26/9\), and the scaled tail is \((3/4)/(1/4)=3\). The result is \(3/(26/9+3)=3/(53/9)=27/53\).
Normalization check. The unscaled denominator is \(13+27/2=53/2\). The probability of the tail is \((27/2)/(53/2)=27/53\); the finite-state probabilities sum to \(13/(53/2)=26/53\). Their total is \(27/53+26/53=1\). This check catches errors in either the finite sum or the infinite-tail multiplier.
Worked Example: The One-Server Limit and a Saturation Check
For \(c=1\), take \(\lambda=1/2\) and \(\mu=1\). Then \(a=1/2\), \(\rho=1/2\), the finite sum contains only \(w_0=1\), and \(w_1=1/2\). The tail weight is \((1/2)/(1-1/2)=1\), so \(C(1,1/2)=1/(1+1)=1/2\). This agrees with the direct interpretation: in a stable M/M/1 queue, an arrival waits exactly when the server is busy, whose stationary probability is \(\rho=1/2\).
As a separate saturation check, keep \(c=2\) and set \(a=3/2\), giving \(\rho=3/4<1\). The finite weights are \(w_0=1\) and \(w_1=3/2\), with sum \(5/2\). The next weight is \(w_2=(3/2)^2/2=9/8\); the tail multiplier is \(1/(1-3/4)=4\), so the tail is \(9/2\). Thus \(C(2,3/2)=(9/2)/(5/2+9/2)=9/14\). The normalization check is \(5/2+9/2=7\), so the finite states have total probability \((5/2)/7=5/14\) and the tail has probability \((9/2)/7=9/14\), summing to \(1\). As \(a\) approaches \(c\) from below, the factor \(1/(1-\rho)\) grows without bound while the finite sum remains finite for fixed \(c\); consequently \(C(c,a)\) approaches \(1\). At \(a=c\), however, the queue is not stable and this stationary Erlang C formula does not apply.
Common Numerical Pitfalls
Common mistake: counting only the state \(X_t=c\) as a delay. In the three-server example, using \(w_3=4/3\) as the entire tail gives the incorrect calculation \((4/3)/(5+4/3)=4/19\). A delay occurs in every state \(n\ge3\), not just state \(3\). The correct tail is \(w_3(1+\rho+\rho^2+\cdots)=(4/3)/(1-2/3)=4\), giving \(4/(5+4)=4/9\).
Other checks help distinguish arithmetic errors from a misapplied formula:
- Confirm \(c\ge1\), \(\mu>0\), and \(a<c\) before using a stationary Erlang C probability. If \(\lambda=0\), handle that case separately and return zero.
- Generate each relative term from its neighbor; do not independently evaluate large powers and factorials if they can overflow.
- Use the geometric tail \(w_c/(1-\rho)\), not just its first term \(w_c\).
- Check that the finite-state probabilities and the aggregated tail probability sum to \(1\), and compare expected busy servers with \(a\) when performing a stationary-distribution check.
- Near saturation, retain \(1-\rho=(c-a)/c\) as directly as possible. If the final ratios leave the floating-point range, move the calculation to logarithms rather than trusting an underflowed term.
These checks are especially valuable at large server counts. The recursion changes the calculation from a sum of potentially unmanageable factorial terms into a sequence of local ratios. Scaling preserves the probability exactly in real arithmetic, while normalization, utilization, and limiting cases provide independent tests of the numerical result.
Check Your Understanding
For each question, state the stability condition that applies and verify any computed probability by substituting it into the relevant formula.
- For \(c=2\) and \(a=1\), compute the finite weights, tail weight, and Erlang C probability.
- Explain why the tail weight in the formula is \(w_c/(1-\rho)\), not \(w_c\).
- For \(c=3\) and \(a=2\), identify the anchor index and compute all scaled finite weights and \(x_c\).
- Why does multiplying every finite and tail weight by the same positive constant leave Erlang C unchanged?
- What happens to the stationary Erlang C calculation when \(a=c\), and why is substituting \(\rho=1\) invalid?