Simulación de Eventos Discretos · Tema 15

Inicialización del modelo

Reloj en cero, estado inicial, primeros futuros y estadísticas en cero — el despegue que condiciona todo el vuelo.

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

0

Reloj y fin

t=0 y condición de parada (hasta T=100 o 1000 salidas). Sin fin, la rutina no sabe parar.

s₀

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.

∑0

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á).

Arranques típicos y qué pregunta responden.
Arranques₀ y FEL₀Responde a
VacíoN=0, libre; primera llegada sorteada“¿Cómo amanece el sistema?” (apertura)
CargadoN=5, ocupado; llegada + salida en curso“¿Cómo sigue el pico?” (continuidad)
RégimenEstado típico + warmup descartado“¿Cómo rinde en equilibrio?” (Temas 41–43)

03 · Decisiones

Cuatro elecciones que sesgan en silencio

fijar params y seed→estado + reloj + stats→primeros futuros
  1. 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. 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. 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. 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

Tupla inicial por sistema.
Sistemas₀FEL₀
Banco que abreN=0, librePrimera llegada sorteada
Banco en picoN=5, ocupadoLlegada + salida en S
Web en hora picoCola=10, ocupados=cArribo + c fines en curso
InventarioStock inicial, sin pedidoPró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.

EXPERIMENTO 15

Luna de miel vs. herencia

sesgo = media(0–6) − media(6–12)

Los resultados numéricos aparecen debajo.
Media 0–60
Media 6–120
Sesgo arranque0
N final0

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

  1. Con A=2, S=3 y N₀=0, ¿la primera media es menor o mayor que la segunda? ¿Por qué?
  2. Subí a N₀=5: ¿qué media se mueve más? ¿El sistema “recuerda” el despegue hasta el final?
  3. ¿Qué harías para medir el régimen sin el sesgo del despegue?
Ver respuestas sugeridas
  1. Menor: arranca vacío y tarda en llenarse (luna de miel). La segunda ya vive el ritmo real.
  2. La primera se dispara; la segunda apenas: el recuerdo se diluye pero contamina la media global. Con horizonte corto manda el despegue.
  3. 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.