Tutorials › Stochastic Processes › Stable Numerical Evaluation of Erlang C

Multi-Server and Finite Queues · Tutorial 655 of 994

Stable Numerical Evaluation of Erlang C

Use recursively computed, scaled queue-state weights to evaluate Erlang C without forming large factorials or unscaled sums.

Advanced 11 min read

What You'll Learn

  • State the stability condition and Erlang C formula for an M/M/c queue.
  • Generate relative stationary weights by upward and downward recursions.
  • Aggregate the infinite queue tail using its geometric ratio.
  • Compute the delay probability from scaled sums and verify normalization.
  • Check results against utilization, the one-server case, and saturation limits.
  • Recognize when floating-point cancellation or overflow calls for log-scale calculations.

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.

Definition: For \(0<a<c\), the Erlang C delay probability is the stationary probability of \(X_t\ge c\). With \(w_n=a^n/n!\) for \(0\le n\le c\), it is $$ C(c,a)=\frac{w_c/(1-\rho)}{\displaystyle\sum_{n=0}^{c-1}w_n+w_c/(1-\rho)},\qquad \rho=\frac ac. $$ The numerator is the total stationary weight of states with all servers busy, including every state above \(c\).

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:

$$ x_{n+1}=x_n\frac{a}{n+1}\quad(m\le n<c-1),\qquad x_{n-1}=x_n\frac{n}{a}\quad(1\le n\le m). $$

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:

$$ C(c,a)=\frac{1}{1+S_c(1-\rho)/x_c}. $$

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.

Why the scaling works: Multiplying every term in a ratio by the same positive constant does not change the ratio. The recursion gives \(x_n=w_n/w_m\) exactly in real arithmetic, and the geometric tail has the same scale factor \(w_m\). Hence the scaled numerator and denominator are the original ones divided by \(w_m\).

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

$$ C(3,2)=\frac{4}{5+4}=\frac49. $$

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

$$ C(4,3)=\frac{27/2}{13+27/2} =\frac{27/2}{53/2} =\frac{27}{53}. $$

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.

  1. For \(c=2\) and \(a=1\), compute the finite weights, tail weight, and Erlang C probability.
  2. Explain why the tail weight in the formula is \(w_c/(1-\rho)\), not \(w_c\).
  3. For \(c=3\) and \(a=2\), identify the anchor index and compute all scaled finite weights and \(x_c\).
  4. Why does multiplying every finite and tail weight by the same positive constant leave Erlang C unchanged?
  5. What happens to the stationary Erlang C calculation when \(a=c\), and why is substituting \(\rho=1\) invalid?