01 · Punto de partida
Revisar cada minuto o poner el despertador
En el Tema 1 vimos que la fila del banco solo cambia cuando alguien llega o termina. Entre esos instantes, el estado está congelado. Entonces hay dos formas de simular 12 minutos:
Opción A (pasos fijos): mirar el reloj en t=0, 1, 2, …, 12 y preguntar “¿pasó algo?”. La mayoría de las veces la respuesta es “no”, pero igual gastaste 13 revisiones.
Opción B (orientada a eventos): anotar que lo próximo ocurre en t=2, poner el “despertador” allí y dormir hasta ese momento. Solo despertás cuando hay trabajo real.
La Opción B es la simulación orientada a eventos: el reloj salta al próximo evento en vez de avanzar de a un paso.
- ¿Cuántas revisiones inútiles hace el método de pasos fijos?
- ¿Cómo sabe el simulador cuál es “lo próximo” que va a ocurrir?
- ¿Qué pasa si dos eventos caen en el mismo instante?
- ¿Cuándo conviene cada método?
02 · Definición
Los seis ingredientes inseparables
Toda simulación por eventos, desde una fila de banco hasta un data center, usa las mismas seis piezas. Si falta una, el simulador no funciona:
Reloj t
Tiempo virtual. No es el tiempo real de tu PC: es el tiempo del mundo simulado, que salta de evento en evento.
Estado s
Lo mínimo para continuar: cuántos hay, si el servidor está ocupado, a qué hora termina lo actual.
Calendario FEL
FEL (Future Event List, Lista de Eventos Futuros) ordenada por tiempo: [(2, llegada), (5, salida), …]. Siempre sabemos “lo próximo”.
Rutinas de evento
Un if por tipo: si es llegada suma uno y agenda la próxima; si es salida libera y atiende al siguiente.
Estadísticas
Contadores que se actualizan en cada evento: esperas acumuladas, máximos, utilización. Sin esto no hay medidas.
Rutina principal
El bucle: saca lo próximo del FEL → avanza el reloj → ejecuta la rutina → agenda futuro → repite hasta el fin.
| Aspecto | Pasos fijos (dt = 1) | Orientada a eventos |
|---|---|---|
| Avance del reloj | t = 0, 1, 2, …, 12 (13 pasos) | t salta solo a instantes con eventos (ej. 2, 4, 5…) |
| Trabajo inútil | Revisa instantes “nada” | Cero: cada iteración procesa un evento real |
| Exactitud | Aproximada si el evento cae entre pasos | Exacta: usa el tiempo real del evento (2.37, no 2) |
| Costo | Crece con T/dt (horizonte / resolución) | Crece con nº de eventos (ideal si son ralos) |
| Cuándo conviene | Cambios en casi cada paso o flujo continuo | Eventos ralos e irregulares: colas, redes, logística |
03 · La máquina
La rutina principal, línea por línea
Este es el esqueleto que repetiremos en todo el curso (Temas 15–18 lo desarmarán pieza por pieza). Leelo como una receta:
- 0Inicializar.
Reloj en 0, estado inicial, FEL con los primeros eventos (ej. primera llegada), estadísticas en cero, condición de fin (ej. simular hasta t=100 o 1000 clientes).
- 1Sacar lo próximo.
Extraer del FEL el evento con menor tiempo. Si el FEL se vacía, no hay futuro: la simulación termina.
- 2Avanzar el reloj.
t = tiempo_evento. El salto puede ser de 0.1 o de 50: el reloj no es uniforme. - 3Procesar y agendar.
Actualizar estado y estadísticas; según el tipo, agendar nuevos eventos futuros (una llegada agenda la siguiente; una salida puede agendar la próxima salida).
- 4Chequear fin.
Si
t >= T_fino se atendieron N clientes, cerrar y reportar. Si no, volver al paso 1.
- Evento incondicionado
- Ocurrirá sí o sí a su hora: la próxima llegada ya sorteada. Va directo al FEL.
- Evento condicionado
- Solo ocurre si se cumple una condición: “empezar servicio” requiere servidor libre y alguien en cola. Se intenta al procesar cada evento.
- Empate de eventos
- Dos eventos a la misma hora: se necesita regla explícita (ej. primero salidas, luego llegadas) para que la simulación sea reproducible.
- Reloj vs. CPU
- El reloj simulado puede saltar 100 unidades en un milisegundo real. No confundas tiempo simulado con tiempo de ejecución.
04 · Paso a paso
Una mini-cola simulada a mano
Sistema: un cajero, llega uno en t=2 y otro en t=4, cada atención dura 3. Estado inicial vacío. Seguí la rutina principal como lo haría el programa:
| Reloj t | Evento extraído | Estado después | FEL después |
|---|---|---|---|
| 0 | — (inicialización) | N=0, libre | [(2, llegada), (4, llegada)] |
| 2 | llegada | N=1, ocupado | [(4, llegada), (5, salida)] |
| 4 | llegada | N=2 (1 en cola + 1 en servicio) | [(5, salida)] |
| 5 | salida | N=1, sigue ocupado | [(8, salida)] |
| 8 | salida | N=0, libre | [] → fin |
Con pasos fijos dt=1 habrías revisado t=0…8 (9 veces) para solo 4 eventos útiles. Con eventos saltaste directo a 2, 4, 5 y 8. Esa diferencia se vuelve abismal en un año simulado con eventos cada horas.
Analogía del despertador
Pasos fijos = mirar cada minuto
Te despertás 480 veces para tomar un colectivo que pasa 3 veces. Funciona, pero no dormís.
Analogía del despertador
Eventos = 3 alarmas
Programás alarmas a las horas exactas y dormís entre ellas. Cada despertada es productiva.
05 · Representación en Python
De revisar todo a atender solo eventos
Primero la versión ingenua por pasos fijos: recorre cada instante aunque no pase nada. Después la versión por eventos con heapq, la cola de prioridad que Python usa como FEL (siempre entrega lo de menor tiempo).
Python en tu navegador. Ejecutá ambos bloques y compará: dan el mismo estado final, pero uno revisa 13 instantes y el otro solo los eventos. Podés editar intervalos y re-ejecutar.
# Versión A: pasos fijos dt=1 hasta T=12 (13 revisiones)
T = 12
N = 2
llega_cada, sirve_cada = 2, 3
revisiones = 0
for t in range(T + 1):
revisiones += 1
llega = (t % llega_cada == 0 and t != 0)
sale = (t % sirve_cada == 0 and t != 0 and N > 0)
N = N + (1 if llega else 0) - (1 if sale else 0)
print("final:", N, "| revisiones:", revisiones)
import heapq
# Versión B: por eventos. El reloj salta solo a tiempos con eventos.
reloj = 0
N = 2
fel = [(2, "llegada"), (3, "salida"), (4, "llegada"), (6, "llegada")]
heapq.heapify(fel)
procesados = 0
while fel:
t, e = heapq.heappop(fel) # lo próximo
reloj = t # ¡el salto!
if e == "llegada":
N += 1
else:
N = max(0, N - 1)
procesados += 1
print("final:", N, "| reloj:", reloj, "| eventos procesados:", procesados)
06 · Exploración
Laboratorio: cuántas revisiones te ahorrás
Mismo sistema del Tema 1 (llega uno cada A, se atiende uno cada S, 12 pasos), pero ahora contado de dos formas: pasos fijos (siempre 13 revisiones) frente a eventos (solo instantes que cambian el estado). Movete los controles y mirá el ahorro.
Despertar solo cuando hay trabajo
relojes: t ← próximo evento del FEL
Mueve los controles para comparar.
La escalera lima es el estado. Las marcas verticales verdes son instantes con evento (el reloj los visita); las grises son instantes “nada” que pasos fijos revisa en vano y eventos saltea.
Preguntas para explorar
- Con llega cada 4 y sirve cada 4, ¿cuántos eventos hay en 12 pasos? ¿Qué ahorro lográs?
- Con llega cada 1 y sirve cada 1, ¿sigue conveniendo eventos? ¿Cuándo se achica la ventaja?
- Si el horizonte fuera 365 días con 20 eventos, ¿cuántas revisiones haría cada método con dt=1 día?
Ver respuestas sugeridas
- Llegadas en 4, 8, 12 (3) y salidas efectivas en 4, 8, 12 (3, compensadas): pocos instantes útiles → ahorro grande (≈50–60%). El reloj salta, por ejemplo, 0→4→8→12.
- Hay evento en casi cada instante → el ahorro se achica. Cuando todo cambia en cada paso, pasos fijos y eventos empatan; la ventaja de eventos es máxima con eventos ralos.
- Pasos fijos: 366 revisiones. Eventos: 20 (+1 inicial). Ahorro ≈95%. En simulaciones reales (años con miles de eventos ralos) la diferencia es de horas a segundos.
07 · Comprensión
Confusiones frecuentes
«El reloj avanza de a uno»
En simulación por eventos el reloj toma el tiempo del próximo evento, sea +1 o +47. Avanzar de a uno es pasos fijos, no eventos.
«Procesar en cualquier orden da igual»
No: procesar una salida antes que una llegada simultánea puede cambiar quién espera. El FEL debe estar ordenado y los empates resueltos con regla explícita.
«Más eventos = simulación más lenta que pasos fijos»
Depende. Si hay evento en cada paso, empatan. Si los eventos son ralos (lo típico en colas), eventos gana por lejos porque saltea los huecos.
«El FEL es una lista cualquiera»
El FEL es una cola de prioridad ordenada por tiempo (heap). Si insertás al final sin ordenar, “lo próximo” deja de ser lo próximo y la causalidad se rompe.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: contar el ahorro
Ejecutá y explicá por qué ambos dan el mismo final con distinto costo.
T = 12
fijos = T + 1 # t = 0..12
# eventos que cambian el estado: llegadas en 2,4,6,8,10,12 y salidas en 3,6,9,12
eventos = [2, 3, 4, 6, 8, 9, 10, 12]
print("fijos:", fijos, "| eventos:", len(eventos))
print("ahorro:", round(100 * (1 - len(eventos) / fijos)), "%")
Ver solución razonada
13 revisiones fijas frente a 8 eventos → ahorro ≈38%. El estado final coincide porque los instantes “nada” (1, 5, 7, 11) no cambian nada; revisarlos es trabajo inútil.
Ejercicio 2: el salto del reloj
Sin ejecutar primero: ¿qué tiempos toma el reloj si el FEL inicial es [(2, llegada), (4, llegada), (5, salida)]? Comprobalo.
import heapq
fel = [(2, "llegada"), (4, "llegada"), (5, "salida")]
heapq.heapify(fel)
reloj = 0
visitas = [reloj]
while fel:
t, e = heapq.heappop(fel)
reloj = t
visitas.append(reloj)
print(visitas)
Ver solución
[0, 2, 4, 5]: el reloj nunca vale 1 ni 3. “Dormir” entre 2 y 4 es legal porque por definición nada puede cambiar allí.
Ejercicio 3: empate con regla explícita
Una llegada y una salida empatan en t=5 con N=1. ¿Qué pasa si procesás salida primero vs. llegada primero? Codificá ambas y decidí una regla.
Ver una posible respuesta
# Regla A: salidas primero (típica en colas: libera antes de ocupar)
N = 1
N = max(0, N - 1) # salida
N = N + 1 # llegada
print("salida primero:", N) # 1
# Regla B: llegadas primero
N = 1
N = N + 1 # llegada
N = max(0, N - 1) # salida
print("llegada primero:", N) # 1
# Aquí empatan, pero con capacidad finita o estadísticas de espera difieren:
# por eso la regla debe escribirse. Recomendada: salidas (0) antes que llegadas (1).
Con cola infinita el N final coincide, pero la espera medida y los rechazos con capacidad finita no. Conclusión: los empates se resuelven con prioridad explícita, ej. (t, 0, "salida") antes que (t, 1, "llegada").
09 · Síntesis
Ideas para recordar
- Orientada a eventos = reloj que salta al próximo evento del FEL, no pasos fijos.
- Componentes: estado, reloj, FEL (heap), rutinas por tipo, estadísticas y rutina principal.
- Cada iteración útil procesa un evento; los huecos “nada” se saltean sin perder exactitud.
- La ventaja es máxima con eventos ralos; con cambios en cada paso, empatan.
- El FEL debe estar ordenado y los empates resueltos con regla explícita y reproducible.
En el próximo tema definiremos el concepto de evento: qué es, qué tipos hay y cómo diseñarlos para que el simulador sea correcto.