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
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.
| Aspecto | Reloj simulado t | Reloj de tu PC |
|---|---|---|
| Qué mide | Historia del sistema (min virtuales) | Ejecución del programa (ms reales) |
| Cómo avanza | A saltos irregulares (2 → 4 → 5) | Continuo y parejo |
| 12 min simulados cuestan | 12 en t | Milisegundos de CPU |
| Un año simulado cuesta | 525 600 en t | Quizá segundos de CPU |
03 · Propiedades
Horizonte, saltos y empates
- 1Horizonte 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).
- 2Saltos 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.
- 3Empates (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 >= relojen 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
| Sistema | Visitas del reloj | Qué muestra |
|---|---|---|
| Banco 12 min | 0 → 2 → 4 → 5 → 8 | Saltos 2, 2, 1, 3: irregular |
| Web 1 s | 0 → 0.01 → 0.03 → 0.04… | Decimales: exactitud sin grilla |
| Noche sin llegadas | 22 → 6 (del día siguiente) | Salto 8 h gratis |
| Empate | 5.0 → 5.0 → 7.0 | Salto 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.
Tiempo virtual a saltos
t ← próximo evento · salto = t′ − t ≥ 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
- Con A=2, S=3, ¿qué instantes visita el reloj? ¿Cuál es el salto máximo?
- Con A=4, S=4, ¿cuántas visitas hay? ¿Qué ahorro lográs?
- Con A=1, S=1, ¿el reloj se parece a pasos fijos? ¿Cuándo conviene cada uno?
Ver respuestas sugeridas
- 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.
- 3–4 visitas → ahorro ≈70%. Los saltos grandes (4) cruzan de noche simulada sin costo.
- 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 = tdesde 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.