01 · Punto de partida
Empezar vacío no es neutral
Dos simulaciones del mismo banco con igual llegadas: una arranca vacía a las 9:00 (N=0, caja libre) y otra arranca con 5 en fila (heredados del pico anterior). La primera mide esperas bajas al principio; la segunda, altas. Ambas son correctas… pero responden preguntas distintas.
Inicializar es declarar desde dónde despega la historia. Un olvido —no agendar la primera llegada, dejar basura en las estadísticas, no fijar la semilla— contamina toda la corrida aunque la rutina principal sea perfecta.
- ¿Vacío, cargado o a mitad de un día real?
- ¿La primera llegada es a las 0 o se sortea?
- ¿Las estadísticas empiezan en cero limpio?
- ¿Puedo repetir exacto la corrida mañana?
02 · Definición
El checklist de seis casillas
Reloj y fin
t=0 y condición de parada (hasta T=100 o 1000 salidas). Sin fin, la rutina no sabe parar.
Estado inicial
N₀, ocupados₀: vacío (0, libre) o cargado (5, ocupado). Define la pregunta.
Primeros futuros
Primera llegada (en 0 o sorteada) y, si arranca ocupado, su salida en S. Sin ellos el FEL nace vacío y muere al nacer.
Estadísticas en cero
Espera total, atendidos, máximos: todo a cero. Reutilizar acumuladores mezcla corridas.
Parámetros
c, A, S, capacidad, disciplina: fijos en la corrida. Cambiarlos a mitad es otro experimento.
Semilla
random.seed(…) para repetir exacto. Sin semilla no hay reproducibilidad (Tema 41 la retomará).
| Arranque | s₀ y FEL₀ | Responde a |
|---|---|---|
| Vacío | N=0, libre; primera llegada sorteada | “¿Cómo amanece el sistema?” (apertura) |
| Cargado | N=5, ocupado; llegada + salida en curso | “¿Cómo sigue el pico?” (continuidad) |
| Régimen | Estado típico + warmup descartado | “¿Cómo rinde en equilibrio?” (Temas 41–43) |
03 · Decisiones
Cuatro elecciones que sesgan en silencio
- 1¿Primera llegada en 0 o sorteada?
En 0 simplifica la mano; sorteada es más real (nadie garantiza cliente a las 9:00:00). Documentalo: cambia las primeras esperas.
- 2¿Servidor ocupado inicial?
Si N₀>0, hay que agendar su salida en S. Arrancar “con gente pero sin salida agendada” deja ocupados fantasma que nunca se liberan.
- 3¿Vacío o cargado?
Vacío subestima al principio (luna de miel); cargado sobrestima si el pico era atípico. Para régimen, se calienta y se descarta (warmup, Tema 41).
- 4¿Semilla fija?
Sí para depurar y publicar; varias semillas para reportar (réplicas). Cambiar código sin fijar semilla mezcla efectos.
- Sesgo de arranque
- La primera mitad mide distinto que la segunda por culpa de s₀. Se ve comparando medias por mitades (laboratorio).
- Warmup
- Período inicial que se simula pero no se mide, para olvidar s₀. Se decide mirando la estabilización (Tema 41).
- FEL₀ mínima
- Al menos la primera llegada; más la salida en curso si N₀>0. FEL₀ vacía = fin instantáneo.
- Reproducibilidad
- Misma seed + mismos params = misma historia. Sin ella no hay depuración ni ciencia.
Arranque vacío
Luna de miel
Primeros sin espera: la media sale baja. Ideal para aperturas, pésimo para picos.
Arranque cargado
Herencia del pico
Primeros con espera alta: la media sale alta. Ideal para continuidad, pésimo para amaneceres.
04 · Ejemplos
Inicializar cada mundo
| Sistema | s₀ | FEL₀ |
|---|---|---|
| Banco que abre | N=0, libre | Primera llegada sorteada |
| Banco en pico | N=5, ocupado | Llegada + salida en S |
| Web en hora pico | Cola=10, ocupados=c | Arribo + c fines en curso |
| Inventario | Stock inicial, sin pedido | Próxima demanda |
05 · Representación en Python
Una función que devuelve todo limpio
Toda la inicialización cabe en inicializar(): sin prints, sin globales, con semilla. El segundo bloque muestra el pecado capital (stats heredadas).
Python en tu navegador. El primero inicializa vacío; el segundo cargado con salida en curso. Cambiá N₀ y mirá la FEL₀.
import heapq, itertools, random
def inicializar(N0=0, A=2.0, S=3.0, T=12.0, seed=7):
random.seed(seed)
reloj = 0.0
estado = {"N": N0, "ocupado": 1 if N0 > 0 else 0}
stats = {"espera_total": 0.0, "atendidos": 0, "max_N": N0}
fel, seq = [], itertools.count()
heapq.heappush(fel, (A, 1, next(seq), "llegada", {})) # primera llegada
if N0 > 0:
heapq.heappush(fel, (S, 0, next(seq), "salida", {})) # la en curso
return reloj, estado, fel, stats, {"T": T}
reloj, estado, fel, stats, fin = inicializar(N0=0)
print(reloj, estado, [e[:2] for e in fel], stats)
# Pecado: reutilizar stats entre réplicas mezcla historias
stats = {"espera_total": 0.0, "atendidos": 0}
stats["espera_total"] += 6.0 # corrida 1...
# Réplica 2 SIN reset: hereda 6.0 -> media contaminada
print("sin reset:", stats)
stats = {"espera_total": 0.0, "atendidos": 0} # así sí
print("con reset:", stats)
06 · Exploración
Laboratorio: el arranque sesga la media
Mismo sistema (llega cada A, S, 1 caja), distinto despegue N₀. La curva muestra N(t); el fondo sombrea primera mitad (0–6) y segunda (6–12) con sus medias. Subí N₀ de 0 a 5 y mirá cómo la primera media se dispara mientras la segunda apenas se mueve: eso es sesgo de arranque.
Luna de miel vs. herencia
sesgo = media(0–6) − media(6–12)
Mueve N₀ para sesgar.
Mitad izquierda sombreada clara, derecha oscura, cada una con su media punteada. Si las medias difieren mucho, el arranque manda: hay que calentar y descartar (Tema 41).
Preguntas para explorar
- Con A=2, S=3 y N₀=0, ¿la primera media es menor o mayor que la segunda? ¿Por qué?
- Subí a N₀=5: ¿qué media se mueve más? ¿El sistema “recuerda” el despegue hasta el final?
- ¿Qué harías para medir el régimen sin el sesgo del despegue?
Ver respuestas sugeridas
- Menor: arranca vacío y tarda en llenarse (luna de miel). La segunda ya vive el ritmo real.
- La primera se dispara; la segunda apenas: el recuerdo se diluye pero contamina la media global. Con horizonte corto manda el despegue.
- Calentar: simular de más y descartar la primera parte (warmup), o arrancar cargado cerca del régimen. Tema 41 lo sistematiza.
07 · Comprensión
Confusiones frecuentes
«Vacío es neutral»
Vacío es una decisión que subestima el inicio. Neutral no existe: todo s₀ sesga; se elige el que responde la pregunta o se descarta el arranque.
«La FEL se llena sola»
La FEL nace de tu mano con los primeros futuros. Vacía al inicio = fin en t=0 sin error visible.
«Las stats pueden vivir entre réplicas»
Cada réplica nace en cero. Reutilizar acumuladores promedia historias distintas en un número sin sentido.
«Ocupado inicial sin salida agendada alcanza»
Sin su fin agendado, ese ocupado es eterno: la caja nunca se libera y la cola crece por un fantasma.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: FEL₀ completa
Con N₀=2, A=2 y S=3, ¿qué lleva la FEL₀? Construila.
import heapq
fel = []
heapq.heappush(fel, (2.0, "llegada"))
heapq.heappush(fel, (3.0, "salida")) # la en curso por N0>0
print(sorted(fel)) # [(2.0...), (3.0...)]
Ver solución razonada
Primera llegada (2.0) + salida en curso (3.0). Sin la segunda, los 2 iniciales serían eternos.
Ejercicio 2: sesgo a ojo
Serie desde vacío [0,1,2,3,3,4] vs. desde cargado [5,5,4,4,3,4]: compará medias.
vacio = [0, 1, 2, 3, 3, 4]
cargado = [5, 5, 4, 4, 3, 4]
print("vacío:", sum(vacio) / len(vacio), "| cargado:", sum(cargado) / len(cargado))
Ver solución
2.17 vs. 4.17: mismo sistema, distinta historia por s₀. Promediar sin declarar el arranque es contar dos películas como una.
Ejercicio 3: réplicas limpias
Escribí nueva_stats() y usala en cada réplica.
Ver una posible respuesta
def nueva_stats():
return {"espera_total": 0.0, "atendidos": 0, "max_N": 0}
for replica in range(2):
stats = nueva_stats() # nace limpia
stats["atendidos"] += 1
print(replica, stats)
Cada réplica parte de cero: las medias finales son comparables y el warmup se puede aplicar por réplica.
09 · Síntesis
Ideas para recordar
- Inicializar = (t=0, s₀, FEL₀, stats=0, params, seed, fin): seis casillas o hay bug.
- s₀ responde la pregunta: vacío (apertura), cargado (pico), régimen (con warmup).
- FEL₀ mínima: primera llegada + salida en curso si N₀>0.
- Stats y seed por réplica: sin reset no hay ciencia.
- El arranque sesga: se detecta por mitades y se cura con warmup (Tema 41).
En el próximo tema ensamblaremos la rutina principal de simulación: el bucle que une reloj, FEL y eventos.