01 · Punto de partida
El hermano mayor del M/M/1
Un call center con 8 agentes no es “ocho M/M/1”: es un M/M/c con fila única y pooling. Con la misma oferta a = λ/μ = 6, ocho separados colapsan (ρ = 0.75 cada uno con su racha) y ocho unidos respiran. La diferencia la escribe Erlang C: la probabilidad de esperar.
Este modelo es la calculadora de dimensionamiento: dado λ y μ, dice el c mínimo estable y el c que cumple el SLA (“80% en 20 s”).
- ¿Qué supuestos definen al M/M/c y qué es la oferta a?
- ¿Cómo se calcula C(c,a) y qué significa?
- ¿Cómo paso de C a Lq, Wq, W y L?
- ¿Qué c elijo para un SLA dado?
02 · Definición
Oferta, Erlang C y medidas
Oferta a
a = λ/μ (erlangs): servidores ocupados equivalentes. c mínimo estable = ⌊a⌋ + 1.
Erlang C
P(esperar) = P(N ≥ c). Cae fuerte con cada servidor extra hasta aplanarse.
Medidas
Lq = C·ρ/(1−ρ), Wq = Lq/λ, W = Wq + 1/μ, L = λW. Todo cuelga de C.
| c | ρ | C(c,a) P(espera) | Wq |
|---|---|---|---|
| 2 | 0.90 | ≈ 0.85 | ≈ 8.5 min (rescate) |
| 3 | 0.60 | ≈ 0.35 | ≈ 0.9 min (pulido) |
| 4 | 0.45 | ≈ 0.12 | ≈ 0.2 min (lujo) |
03 · El corazón: muerte por tramos
Nacer a λ, morir a min(n,c)μ
Con n < c hay n servidores activos (muerte nμ); con n ≥ c todos trabajan (muerte cμ fija) y la cola crece. El balance por tramos da P0, de P0 sale C(c,a) y de C salen todas las medias. Es el mismo nacimiento-muerte del M/M/1 con techo de servicio.
- 1Calcular a y ρ.
a = λ/μ; ρ = a/c < 1 o no hay régimen. c mínimo = ⌊a⌋ + 1.
- 2Erlang C.
C = [ac/(c!(1−ρ))] / [Σk<c ak/k! + ac/(c!(1−ρ))].
- 3Medias.
Lq = Cρ/(1−ρ); Wq = Lq/λ; W = Wq + 1/μ; L = Lq + a.
- 4Dimensionar por SLA.
P(Wq > t) = C·e−(cμ−λ)t: subir c hasta cumplir “X% en t segundos”.
- P0
- P(sistema vacío) = 1/[Σk<c ak/k! + ac/(c!(1−ρ))]. El denominador de Erlang C.
- Economías de escala
- A igual ρ, más c ⇒ menos espera: un M/M/8 con ρ=0.9 espera menos que un M/M/2 con ρ=0.9. Lo grande absorbe rachas.
- SLA “X% en t”
- P(Wq ≤ t) = 1 − C·e−(cμ−λ)t. La fórmula con la que se venden call centers (80/20).
- M/M/1 como caso
- Con c = 1, C(1,a) = ρ y Wq = ρ/(μ−λ): se recupera el Tema 31. Un solo modelo, dos capítulos.
M/M/1
Base exacta
Sin pooling: cada racha la sufre uno. Sirve para validar el motor antes de paralelizar.
M/M/c y M/M/c/c
Pooling y pérdida
M/M/c espera unida (Erlang C); M/M/c/c pierde sin espera (Erlang B). Misma a, distinto destino del pico.
04 · Ejemplos
Tres M/M/c con placa
- 1Call center 8 agentes.
λ = 6/min, S = 1 min (a = 6, ρ = 0.75): C ≈ 0.4, Wq ≈ 0.2 min. SLA 80/20 holgado.
- 2Banco 3 cajas.
λ = 0.9/min, S = 2 (a = 1.8, ρ = 0.6): Wq ≈ 0.9 min. La tercera caja humaniza.
- 3Web 4 workers.
λ = 100/s, S = 0.03 s (a = 3, ρ = 0.75): Wq ≈ 0.02 s. Pooling perfecto a microescala.
05 · Representación en Python
mmc() que dimensiona y simula
Erlang C con factoriales para predecir; libre_en[c] para validar con fila única.
Python en tu navegador. El primero calcula C y todo M/M/c; el segundo simula y compara. Barré c y buscá tu SLA.
# Todo M/M/c en una función (Erlang C)
import math
def mmc(lam, mu, c):
a = lam / mu
rho = a / c
assert rho < 1, "sin régimen"
s = sum(a**k / math.factorial(k) for k in range(c))
C = (a**c / (math.factorial(c)*(1-rho))) / (s + a**c/(math.factorial(c)*(1-rho)))
Wq = C / (c*mu - lam)
return {"a": a, "rho": rho, "C": C, "Wq": Wq, "W": Wq + 1/mu}
for c in [2, 3, 4]:
print(c, {k: round(v, 2) for k, v in mmc(0.9, 0.5, c).items()})
# Validar M/M/c simulando fila única (T largo)
import random
random.seed(32)
lam, mu, c, T = 0.9, 0.5, 3, 300.0
t, libres, wqs = 0.0, [0.0]*c, []
while t < T:
t += random.expovariate(lam)
k = min(range(c), key=lambda j: libres[j])
ini = max(t, libres[k])
libres[k] = ini + random.expovariate(mu)
wqs.append(ini - t)
print(f"Wq sim={sum(wqs)/len(wqs):.2f} vs {mmc(lam, mu, c)['Wq']:.2f}")
06 · Exploración
Laboratorio: la curva de Erlang C
Fijamos S = 2 (μ = 0.5) y elegís la oferta a (vía λ) y c. Las barras muestran la Wq teórica M/M/c para c = 1…5 (capada donde no hay régimen); la barra marcada es tu c y el punto teal es tu simulación (T = 150, réplica). Subí a y mirá cómo la curva exige más c.
Erlang C manda, la sim obedece
Wq(c) = C(c,a)/(cμ−λ) · S=2
Mové la oferta para exigir más c.
Barras lima = Wq teórica por c (roja donde ρ ≥ 1); borde grueso = tu c; punto teal = Wq simulada en tu c. S = 2 fijo.
Preguntas para explorar
- Con a=1.8: ¿Wq con c=2 vs c=3? ¿C en cada uno? ¿Qué compró el tercero?
- Subí a=2.4: ¿c mínimo? ¿Wq con ese c vs uno más?
- ¿Por qué la sim con c al límite (ρ≈0.95) tiembla entre réplicas?
Ver respuestas sugeridas
- c=2: C≈0.85, Wq≈8.5; c=3: C≈0.35, Wq≈0.9. El tercero compró −90% de espera y −60% de probabilidad de esperar.
- a=2.4 ⇒ c mínimo 3 (ρ=0.8); con 4 la Wq se parte (~1/3). Dimensionar es elegir en esa curva.
- Al límite el régimen tarda y la varianza de Wq explota: T=150 no alcanza; se alarga T o se promedia réplicas (Temas 41–43).
07 · Comprensión
Confusiones frecuentes
«C(c,a) es la utilización»
No: ρ = a/c es utilización; C es P(esperar), siempre mayor que ρ y mucho mayor al límite. Con ρ=0.9 y c=2, C≈0.95.
«M/M/3 es tres M/M/1»
Tres M/M/1 con λ/3 esperan más que un M/M/3 con λ (pooling). El código con una sola barra es otra cosa.
«P0 = 1 − ρ también con c > 1»
Solo con c = 1. Con c > 1, P0 lleva la suma de Erlang: 1/[Σak/k! + ac/(c!(1−ρ))].
«Erlang C vale con prioridades»
No: supone FIFO sin clases. Con prioridades la espera por clase se calcula aparte (Tema 39) o se simula.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: mínimo estable
λ=6/min, S=1 min. ¿a, c mínimo y ρ con ese c?
lam, S = 6.0, 1.0
a = lam * S
c = int(a) + 1
print(f"a={a} c_min={c} rho={a/c:.2f}") # a=6 c=7 rho=0.86
Ver solución razonada
a = 6 erlangs ⇒ c = 7 mínimo (ρ≈0.86, tenso); c = 8 lo deja en 0.75 (SLA viable).
Ejercicio 2: leer la curva
Con λ=0.9, μ=0.5: ¿Wq con c=2, 3, 4? Usá tu mmc().
# Esperado: ~8.5, ~0.9, ~0.2 (ver tabla de la sección 02)
print("El salto 2->3 vale 10x; el 3->4 vale 4x: retornos decrecientes")
Ver solución
8.5 → 0.9 → 0.2: el rescate lo hace el 3º; el 4º es lujo salvo SLA estricto.
Ejercicio 3: SLA 80/20
λ=6/min, S=1, c=8: ¿P(Wq ≤ 1/3 min)? Fórmula P(Wq>t) = C·e−(cμ−λ)t.
Ver una posible respuesta
import math
# con mmc(6, 1, 8): C≈0.36, cμ−λ=2
C, resto, t = 0.36, 2.0, 1/3
print(f"P(Wq<={t:.2f})={1 - C*math.exp(-resto*t):.0%}") # ~82%
~82%: cumple el 80/20 por poco. Con c=7 caería bajo el SLA.
09 · Síntesis
Ideas para recordar
- M/M/c = Poisson + Exp + c + ∞ + FIFO; a = λ/μ, ρ = a/c.
- Erlang C = P(esperar); de ella cuelgan Lq, Wq, W, L y el SLA.
- c mínimo = ⌊a⌋+1 estabiliza; el siguiente humaniza; el resto decora.
- En Python:
mmc()con factoriales +libre_en[c]para validar. - A igual ρ, más c espera menos: lo grande absorbe rachas.
En el próximo tema miraremos el otro extremo del mostrador: la utilización del servidor.