Simulación de Eventos Discretos · Tema 11

Reloj de simulación

El tiempo virtual que ordena los eventos — que salta, nunca retrocede y no tiene nada que ver con los segundos de tu PC.

01 · Punto de partida

Dos relojes en tu mesa

Mientras simulás 12 minutos de banco, en tu mesa corren dos tiempos: el de tu PC (medio segundo de CPU) y el del banco (12 minutos virtuales). El segundo es el reloj de simulación: una variable t que empieza en 0 y termina en 12, visitando solo los instantes con eventos, por ejemplo 0 → 2 → 4 → 5 → 8.

Si ese reloj avanzara de a 1 minuto parejo, sería pasos fijos. Como salta al próximo evento —2.0, 3.7, 4.2, da igual— es simulación por eventos. Todo el curso depende de esa variable.

  • ¿Cuánto vale ahora y cuánto valdrá después del próximo evento?
  • ¿Puede quedarse quieto o retroceder?
  • ¿Cuándo termina la simulación: por tiempo o por conteo?
  • ¿Qué pasa con los decimales (2.37) y los empates (dos a las 5)?

02 · Definición

Una variable con tres reglas de oro

0

Nace en cero

t = 0 al inicializar (o en el primer evento si preferís). Todo lo anterior no existe.

⏭

Salta al próximo

t = tiempo(evento) extraído del FEL. El salto puede ser 0.1 o 47: el reloj no es uniforme.

↗

Nunca retrocede

t′ ≥ t siempre; empates permiten =. Retroceder rompería la causalidad.

El reloj forma parte del snapshot para reanudar (Tema 4): (t, estado, FEL). Sin t, los tiempos del FEL quedan huérfanos.

Tiempo simulado frente a tiempo real.
AspectoReloj simulado tReloj de tu PC
Qué mideHistoria del sistema (min virtuales)Ejecución del programa (ms reales)
Cómo avanzaA saltos irregulares (2 → 4 → 5)Continuo y parejo
12 min simulados cuestan12 en tMilisegundos de CPU
Un año simulado cuesta525 600 en tQuizá segundos de CPU

03 · Propiedades

Horizonte, saltos y empates

t = 0→saltar al próximo→llegar al horizonte y parar
  1. 1
    Horizonte T.

    Condición de parada por tiempo: “simular hasta t=100”. Los eventos más allá de T se ignoran (o se truncan). Alternativa: parar por conteo (“1000 clientes”, Tema 19).

  2. 2
    Saltos variables.

    De 2 a 4 hay salto 2; de 4 a 5, salto 1. El salto medio y el máximo describen cuán ralos son los eventos: a saltos grandes, más ahorro vs. pasos fijos.

  3. 3
    Empates (salto cero).

    Dos eventos a las 5.0: el reloj se queda en 5.0 y procesa ambos con regla explícita. Salto cero es legal; salto negativo es bug.

Monotonía
t′ ≥ t siempre. Un assert reloj_nuevo >= reloj en la rutina principal caza FEL desordenados.
Resolución
Los tiempos son floats (2.37): el reloj por eventos es exacto, no redondea a la grilla. Redondear a enteros es pasos fijos.
Reloj + FEL
El reloj dice “dónde estoy”; el FEL dice “qué viene”. Avanzar es copiar el tiempo del evento extraído, nunca t+dt.
Ocio del reloj
Saltos grandes = sistema quieto mucho tiempo (noche sin llegadas). El simulador los cruza gratis; pasos fijos los pagaría uno por uno.

Reloj parejo

t += dt

Fácil de programar, revisa huecos vacíos y redondea eventos a la grilla. Bien si casi todo cambia en cada paso.

Reloj por eventos

t = próximo

Solo visita tiempos útiles con exactitud total. Bien si los eventos son ralos e irregulares (colas).

04 · Ejemplos

El mismo reloj en cuatro mundos

Saltos típicos del reloj por sistema.
SistemaVisitas del relojQué muestra
Banco 12 min0 → 2 → 4 → 5 → 8Saltos 2, 2, 1, 3: irregular
Web 1 s0 → 0.01 → 0.03 → 0.04…Decimales: exactitud sin grilla
Noche sin llegadas22 → 6 (del día siguiente)Salto 8 h gratis
Empate5.0 → 5.0 → 7.0Salto cero legal con regla

05 · Representación en Python

Una variable y un assert guardián

El reloj es un float que solo se asigna desde el FEL, con un assert de monotonía que delata cualquier desorden.

Python en tu navegador. El primer bloque salta y registra visitas; el segundo caza un retroceso. Cambiá tiempos y mirá los saltos.

import heapq
fel = [(2.0, "llegada"), (4.0, "llegada"), (5.0, "salida")]
heapq.heapify(fel)
reloj, visitas = 0.0, [0.0]
while fel:
    t, _ = heapq.heappop(fel)
    assert t >= reloj, "el reloj retrocedió: FEL desordenado"
    reloj = t  # ¡el salto!
    visitas.append(reloj)
print("visitas:", visitas)  # [0.0, 2.0, 4.0, 5.0]
print("saltos:", [round(b - a, 1) for a, b in zip(visitas[:-1], visitas[1:])])
# Horizonte T: lo que pase después se ignora
T = 4.5
visitas = [0.0, 2.0, 4.0, 5.0]
dentro = [t for t in visitas if t <= T]
print("dentro del horizonte:", dentro, "| truncado:", [t for t in visitas if t > T])

06 · Exploración

Laboratorio: los saltos del reloj

Llega uno cada A, atención S, 1 caja, horizonte 12. La recta numérica muestra en verde los instantes que el reloj visita (eventos ≤12) y en gris los que saltea; los arcos son los saltos. Subí A y mirá cómo los saltos se agrandan y el ahorro se dispara.

EXPERIMENTO 11

Tiempo virtual a saltos

t ← próximo evento · salto = t′ − t ≥ 0

Los resultados numéricos aparecen debajo.
Visitas (eventos)0
Salto medio0
Salto máximo0
Ahorro vs. fijo (13)0%

Mueve A para agrandar los saltos.

Puntos verdes: el reloj los visita. Grises: los saltea. Arcos: tamaño del salto. Más verde junto = reloj casi parejo; verde ralo = grandes saltos gratis.

Preguntas para explorar

  1. Con A=2, S=3, ¿qué instantes visita el reloj? ¿Cuál es el salto máximo?
  2. Con A=4, S=4, ¿cuántas visitas hay? ¿Qué ahorro lográs?
  3. Con A=1, S=1, ¿el reloj se parece a pasos fijos? ¿Cuándo conviene cada uno?
Ver respuestas sugeridas
  1. Visita llegadas (2,4,6,8,10,12) y salidas efectivas (5,8,11…): ~8 visitas con saltos 1–3. El máximo suele estar al inicio o al final.
  2. 3–4 visitas → ahorro ≈70%. Los saltos grandes (4) cruzan de noche simulada sin costo.
  3. Sí: visita casi todo (12–13) y el ahorro se evapora. Con cambios en cada paso, pasos fijos empatan; con ralos, eventos gana.

07 · Comprensión

Confusiones frecuentes

«El reloj mide cuánto tarda mi programa»

Ese es el tiempo real (CPU). El reloj simulado puede avanzar 1 año en 2 segundos de CPU: son universos distintos.

«El reloj avanza de a uno»

Solo en pasos fijos. En eventos avanza al próximo tiempo del FEL, sea +0.01 o +100.

«Puede retroceder un poco para corregir»

No: retroceder invalida estados y estadísticas ya acumulados. Si algo salió mal, se reinicia, no se rebobina.

«Los decimales se redondean a enteros»

Redondear rompe el orden de eventos cercanos (2.37 vs 2.41). El reloj por eventos conserva los floats exactos.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: registrar visitas y saltos

Con FEL en 2, 4 y 5 desde 0, listá visitas y saltos.

visitas = [0.0, 2.0, 4.0, 5.0]
saltos = [b - a for a, b in zip(visitas[:-1], visitas[1:])]
print("visitas:", visitas, "| saltos:", saltos, "| medio:", sum(saltos) / len(saltos))
Ver solución razonada

Saltos [2, 2, 1], medio ≈1.67. El reloj no es parejo: dos saltos largos y uno corto según la densidad de eventos.

Ejercicio 2: cazar un retroceso

¿Qué hace este código con un FEL desordenado?

reloj = 5.0
t_nuevo = 4.0  # llegó desordenado
assert t_nuevo >= reloj, "retroceso"
print("avanza a", t_nuevo)
Ver solución

Falla con “retroceso”: el assert delata que el FEL no entregó el mínimo. Sin guardián, el reloj viajaría al pasado en silencio.

Ejercicio 3: horizonte que trunca

Con T=4.5 y visitas hasta 8, ¿qué se procesa y qué se ignora?

Ver una posible respuesta
T, visitas = 4.5, [0.0, 2.0, 4.0, 5.0, 8.0]
print("proceso:", [t for t in visitas if t <= T])
print("ignoro:", [t for t in visitas if t > T])

Proceso hasta 4.0; 5.0 y 8.0 quedan fuera (o truncan la última actividad según la regla de cierre del Tema 19).

09 · Síntesis

Ideas para recordar

  • Reloj = tiempo virtual: nace en 0, salta al próximo evento, nunca retrocede.
  • Saltos variables e incluso cero (empates); negativos = bug.
  • Tiempo simulado ≠ tiempo CPU: un año virtual puede costar segundos reales.
  • En Python: reloj = t desde el FEL + assert t >= reloj.
  • Horizonte T trunca: lo posterior se ignora o se cierra con regla.

En el próximo tema compararemos el avance del tiempo: incremento fijo frente a salto al próximo evento.