Simulación de Eventos Discretos · Tema 12

Avance del tiempo

Incremento fijo frente a salto al próximo evento — cuándo revisar cada minuto y cuándo poner el despertador exacto.

01 · Punto de partida

El evento que cae entre dos revisiones

Una llegada real ocurre a las 10:02:22 (t=2.37). Con revisiones cada 1 minuto la ves a las 10:03 (t=3): llegás 0.63 tarde. Con despertador exacto la ves a las 10:02:22: llegás justo.

Ese retraso no es gratis: altera esperas, ordena mal eventos cercanos y, si achicás dt a 0.01 para disimularlo, pasás de 13 a 1201 revisiones. La elección del avance cuesta CPU o cuesta exactitud: hay que elegir con criterio.

  • ¿Cuántas revisiones hace cada método en el horizonte T?
  • ¿Qué error de tiempo introduce redondear a la grilla?
  • ¿Qué pasa si dos eventos caen en el mismo intervalo dt?
  • ¿Cuándo empatan y cuándo gana por lejos el próximo evento?

02 · Definición

Las dos máquinas, pieza por pieza

dt

Fijo: t += dt

Grilla 0, dt, 2dt… Se chequea cada casilla aunque esté vacía. Simple de programar, ciego entre casillas.

⏭

Eventos: t = próximo

Saltos a tiempos exactos del FEL (2.37, 4.10…). Solo trabaja donde hay eventos. Exacto y ralo-eficiente.

≈

Error de grilla

Redondear 2.37 a 3.0 atrasa 0.63. Achicar dt lo reduce pero multiplica revisiones: no hay almuerzo gratis.

Comparación completa con T=12.
AspectoIncremento fijoPróximo evento
RevisionesT/dt + 1 (13 con dt=1, 121 con dt=0.1)E = nº eventos (ej. 8)
ExactitudAproximada: redondea a la grillaExacta: floats del FEL
Orden en el intervaloPierde el orden dentro de dtConserva el orden total
Mejor casoCambios en casi cada pasoEventos ralos o irregulares
Riesgodt grande pierde eventos; dt chico explota CPUFEL desordenado rompe causalidad

03 · Detalle fino

Tres trampas del incremento fijo

dt grande (pierde)→dt chico (cuesta)→eventos (exacto)
  1. 1
    Cuantización.

    Todo evento se ve en el próximo múltiplo de dt: atraso medio ≈ dt/2. Con dt=1 y llegadas en .37, el reloj vive 0.63 atrasado.

  2. 2
    Colisión en el intervalo.

    Dos eventos en (2, 3] se ven “juntos” en t=3 y su orden se pierde. Con llegadas y salidas cercanas, el error cambia quién esperó.

  3. 3
    Dilema dt.

    Achicar dt de 1 a 0.1 divide el error por 10 pero multiplica las revisiones por 10. En un año simulado, dt=1 min son 525 601 revisiones por un puñado de eventos.

dt (paso)
Resolución de la grilla fija. Parámetro del método, no del sistema: no figura en ninguna fórmula de colas.
E (nº eventos)
Costo del método por eventos. No depende de T sino de la actividad: noche quieta cuesta casi nada.
Denso vs. ralo
Denso: evento en casi cada dt → empatan. Ralo: huecos largos → eventos gana por lejos.
Híbrido
Pasos fijos para lo continuo + eventos para lo discreto (heladera con termostato). Cada motor en lo suyo. (Tema 1)

04 · Ejemplos

Los números del dilema

Costo y error con T=12 y 8 eventos.
MétodoRevisionesError máx.Cuándo
Fijo dt=272.0 (pierde orden)Solo si nada fino importa
Fijo dt=1131.0Juguete didáctico
Fijo dt=0.11210.1Caro y aún aproximado
Próximo evento80Colas reales

05 · Representación en Python

Dos bucles frente a frente

El fijo recorre la grilla y redondea; el de eventos salta exacto. Mismo sistema, distinto costo y exactitud.

Python en tu navegador. Ejecutá ambos: el fijo revisa 13 y atrasa; el de eventos visita 3 exactos. Cambiá dt y mirá el costo.

# Fijo dt=1: el evento de 2.37 se ve en 3.0 (atraso 0.63)
dt, T = 1.0, 12.0
reloj, revisiones = 0.0, 0
while reloj <= T:
    revisiones += 1
    reloj += dt
print("revisiones fijas:", revisiones)  # 13
print("2.37 se ve en:", 3.0, "| atraso:", round(3.0 - 2.37, 2))
import heapq
fel = [(2.37, "llegada"), (4.10, "llegada"), (5.02, "salida")]
heapq.heapify(fel)
reloj, visitas = 0.0, [0.0]
while fel:
    t, _ = heapq.heappop(fel)
    reloj = t
    visitas.append(round(reloj, 2))
print("visitas exactas:", visitas, "| revisiones:", len(visitas) - 1)

06 · Exploración

Laboratorio: la grilla atrasa, el evento clava

Llegadas cada A (permitimos decimales como 2.5) y servicio S en 1 caja. Verde: tiempo exacto del evento. Gris: cuándo lo vería la grilla fija con tu dt. El segmento rojo es el atraso. Achicá dt y mirá cómo el error baja pero las revisiones explotan.

EXPERIMENTO 12

Exactitud vs. revisiones

vista = techo(real / dt) × dt · atraso = vista − real

Los resultados numéricos aparecen debajo.
Revisiones fijas13
Eventos exactos0
Atraso medio0
Atraso máximo0

Mueve dt para ver el dilema.

Cada par vertical: punto verde (real) y gris (visto). Rojo: atraso. Con decimales la grilla siempre atrasa; con enteros y dt=1 el error se esconde.

Preguntas para explorar

  1. Con A=2.5, S=2 y dt=1, ¿cuánto atrasa en promedio? ¿Y el máximo?
  2. Pasá a dt=0.5: ¿el error cae a la mitad? ¿Y las revisiones?
  3. Poné A entero (2) con dt=1: ¿el error desaparece? ¿Significa que fijo es exacto o que el ejemplo lo favorece?
Ver respuestas sugeridas
  1. Atraso medio ≈0.4–0.5 y máximo ≈1.0: cada evento con decimal se redondea al entero siguiente.
  2. Sí, el error se parte; las revisiones pasan de 13 a 25. En un año, de 366 a 731: el costo escala con 1/dt.
  3. Se esconde porque los eventos caen justo en la grilla. Es un caso amañado: en colas reales los tiempos son 2.37, no 2. El método por eventos es exacto siempre.

07 · Comprensión

Confusiones frecuentes

«dt chico = exacto»

dt chico = menos error pero nunca cero, y cuesta 1/dt. Exacto solo es el salto al tiempo real del FEL.

«Si los tiempos son enteros, da igual»

Con enteros y dt=1 el error se esconde, pero basta un sorteo exponencial (2.37) para que aparezca. Lo entero es un juguete.

«Más revisiones = mejor simulación»

Más revisiones fijas = más CPU mirando huecos vacíos. Mejor es revisar menos veces en los instantes justos.

«El orden dentro de dt no importa»

Llegada en 2.1 y salida en 2.9 vistas “juntas” en 3.0 pierden quién fue primero: con capacidad finita cambia quién es rechazado.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: costo de la grilla

Con T=12, compará revisiones con dt=1, 0.5 y 0.1.

T = 12
for dt in [1, 0.5, 0.1]:
    print("dt =", dt, "->", int(T / dt) + 1, "revisiones")
Ver solución razonada

13, 25 y 121. Bajar el error de 1.0 a 0.1 cuesta 9× CPU. El método por eventos visita solo los ~8 reales con error 0.

Ejercicio 2: atraso de un evento

Evento real en 2.37 con dt=1: ¿cuándo se ve y cuánto atrasa?

import math
real, dt = 2.37, 1.0
vista = math.ceil(real / dt) * dt
print("vista:", vista, "| atraso:", round(vista - real, 2))
Ver solución

Vista 3.0, atraso 0.63. Ese atraso se suma a la espera medida y contamina Wq sin que ningún assert lo atrape.

Ejercicio 3: colisión en el intervalo

Llegada en 2.1 y salida en 2.9 con dt=1: ¿qué ve la grilla? ¿Y eventos?

Ver una posible respuesta
import math
for real in [2.1, 2.9]:
    print(real, "-> grilla:", math.ceil(real / 1.0), "| eventos:", real)

Grilla: ambos en 3.0 (orden perdido). Eventos: 2.1 antes que 2.9 (orden total). Con cola llena, el orden decide el rechazo.

09 · Síntesis

Ideas para recordar

  • Fijo: t+=dt, costo T/dt+1, error ≤ dt y pierde orden intra-dt.
  • Eventos: t=próximo, costo E, error 0 y orden total.
  • Achicar dt reduce error pero multiplica CPU: no hay almuerzo gratis.
  • En colas ralas e irregulares, eventos gana en costo y exactitud.
  • Lo entero con dt=1 esconde el error; lo decimal lo revela.

En el próximo tema veremos el calendario de eventos: la agenda ordenada que hace posibles esos saltos.