01 · Punto de partida
Del modelo al motor
El modelo dice qué evoluciona; el simulador resuelve cómo: quién lleva el reloj, dónde esperan los eventos, cómo se sortea y dónde se registra todo. Implementar es construir ese motor sin traicionar el modelo.
- ¿Reloj por pasos o agenda de eventos?
- ¿Dónde vive el estado y quién lo toca?
- ¿Cómo se fija y registra la semilla?
- ¿Cada corrida deja traza reproducible?
02 · Las piezas del motor
Cinco órganos vitales
Reloj
Avanza el tiempo: pasos fijos o saltos al próximo evento.
Agenda
Cola de eventos futuros ordenada por tiempo: lo próximo primero.
Azar
Generador con semilla fijada y registrada por corrida.
| Pieza | En Python | Rol |
|---|---|---|
| Reloj | for t in range(pasos) | Avanza paso a paso |
| Agenda | heapq de (tiempo, evento) | Saca lo próximo primero |
| Azar | random.Random(semilla) | Sortea reproducible |
| Registro | dict con parámetros y semilla | Hace repetible la corrida |
03 · Dos motores
Paso fijo o eventos
- 1Paso fijo.
Itera todos los instantes: simple, parejo, con pasos vacíos cuando no pasa nada.
- 2Eventos.
Salta a lo próximo de la agenda: eficiente con actividad rala.
- 3Ambos deben empatar.
Mismo escenario, mismos resultados: el motor no cambia el modelo.
Paso fijo
Siempre trabaja
24 pasos = 24 operaciones, haya o no eventos. Simple de escribir y verificar.
Eventos
Solo cuando pasa algo
2 operaciones por cliente (llegada + salida). Gana con poco tráfico.
- Reloj
- Mecanismo que avanza el tiempo simulado.
- Agenda
- Eventos futuros ordenados por instante.
- Traza
- Registro de corrida: parámetros, semilla, versión, resultados.
- Motor
- Bucle que procesa tiempo y eventos según el modelo.
04 · Trazas
Corridas que se pueden repetir
Toda corrida devuelve su historia junto a su traza. El Tema 41 convertirá esto en reproducibilidad total del proyecto.
05 · En Python
Agenda con heapq y motor con traza
Python en tu navegador. La agenda ordenada y la traza son stdlib puro.
import heapq
agenda = []
heapq.heappush(agenda, (2, "llegada"))
heapq.heappush(agenda, (1, "cierre"))
print([heapq.heappop(agenda) for _ in range(2)])
Motor con traza incluida
def simular(n0, entradas, salida, semilla="v1"):
n = n0
historia = []
for e in entradas:
n = max(n + e - salida, 0)
historia.append(n)
return {"semilla": semilla, "historia": historia}
print(simular(10, [3, 3, 3], 2))
06 · Exploración
Laboratorio: contar operaciones
Llegadas al azar en 24 pasos con un servidor. El motor fijo procesa los 24 pasos; el de eventos, solo llegadas y salidas. ¿Cuándo gana cada uno?
Fijo contra eventos
24 pasos · 1 servidor
Con 6 llegadas, eventos usa 12 operaciones contra 24 del fijo.
Puntos: llegadas. Barras: servidor ocupado. Ambos motores describen lo mismo con distinto costo.
Preguntas para explorar
- Subí las llegadas a 12. ¿Sigue ganando eventos? ¿Por qué?
- ¿Los motores empatan en clientes en sistema? ¿Deberían?
- ¿Qué motor elegirías para una autopista vacía de noche?
Ver respuestas sugeridas
- Empatan (24 vs 24): con tráfico denso los pasos vacíos desaparecen y el fijo deja de perder.
- Sí: mismo escenario, mismo resultado. El motor cambia el costo, nunca el modelo.
- Eventos: casi todo son pasos vacíos y el fijo trabaja de balde.
07 · Comprensión
Confusiones frecuentes
«Motor rápido es motor fiel»
La velocidad no prueba fidelidad: ambos motores deben empatar en resultados ante los testigos.
«La semilla es detalle menor»
Sin semilla registrada no hay réplica posible. Es parte del resultado, no del código.
«La agenda ordena sola»
Solo si se usa estructura ordenada (heapq) y empates definidos. La lista desordenada inventa futuros.
«Registrar enlentece»
Cuesta una fracción y salva auditorías: lo no registrado se rehace entero.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: agenda ordenada
Encolá una llegada en t = 2 y un cierre en t = 1. ¿Qué sale primero?
import heapq
agenda = []
heapq.heappush(agenda, (2, "llegada"))
heapq.heappush(agenda, (1, "cierre"))
print([heapq.heappop(agenda) for _ in range(2)])
Ver solución razonada
Salen [(1, 'cierre'), (2, 'llegada')]: el heap ordena por tiempo aunque entren desordenados.
Ejercicio 2: motor con traza
Corré 3 entradas y verificá historia y semilla en la traza.
def simular(n0, entradas, salida, semilla="v1"):
n = n0
historia = []
for e in entradas:
n = max(n + e - salida, 0)
historia.append(n)
return {"semilla": semilla, "historia": historia}
print(simular(10, [3, 3, 3], 2))
Ver solución
Da {'semilla': 'v1', 'historia': [11, 12, 13]}: datos y metadatos viajan juntos.
Ejercicio 3: contar operaciones
Con 6 llegadas en 24 pasos, ¿cuántas operaciones usa cada motor?
Ver una posible respuesta
print("fijo:", 24)
print("eventos:", 2 * 6)
El fijo usa 24 y eventos 12: con tráfico ralo, eventos gana por goleada.
09 · Síntesis
Ideas para recordar
- Simulador = reloj + estado + agenda + azar + registro.
- Paso fijo simple; eventos eficiente con tráfico ralo.
- Ambos motores deben empatar ante los mismos testigos.
- Toda corrida deja traza: parámetros, semilla y versión.
- En Python:
heapqpara agendas y dicts para trazas.
En el próximo tema diseñaremos qué correr: el diseño de experimentos.