Simulación de Eventos Discretos · Tema 06

Atributos

Los datos que lleva cada entidad —prioridad, tipo, horarios— y cómo esos campos deciden quién pasa primero y cuánto esperó cada uno.

01 · Punto de partida

Sin atributos, todos son iguales

En el Tema 5 seguimos a C5, C6 y C7 por su ID. Pero si solo sabemos el ID, la fila solo puede ser FIFO: pasa el que llegó primero. En la realidad hay embarazadas que pasan primero, requests premium, piezas urgentes y pacientes graves.

¿Cómo sabe el simulador quién es quién? Por los atributos: campos pegados a cada entidad que se fijan al nacer (prioridad, tipo), se completan al vivir (inicio, fin) y se leen al decidir y al medir (espera = inicio − llegada).

  • ¿Qué dato decide el orden cuando hay dos esperando?
  • ¿Qué dato permite medir la espera individual?
  • ¿Qué dato nunca cambia y cuál se completa con el tiempo?
  • ¿Dónde vive el atributo: en la entidad, en el estado o en la estadística?

02 · Definición

Tres familias de atributos

◈

Fijos al nacer

Se sortean en la llegada y no cambian: tipo (normal/premium), prioridad (1/2), operación requerida.

◔

De historia

Se completan al vivir: llegada, inicio, fin. Con ellos se deriva todo lo medible.

∑

Derivados

Se calculan, no se guardan: espera = inicio − llegada, vida = fin − llegada. Guardarlos duplica y desincroniza.

Regla de oro: los fijos y la historia se guardan en la entidad; los derivados se calculan con funciones. Si guardás espera como campo y después cambiás inicio, la espera vieja miente.

Atributos típicos: qué se guarda y qué se calcula.
AtributoFamiliaEjemploSe usa para
idFijoC7Trazabilidad
prioridadFijo1 = alta, 2 = bajaOrdenar la cola
tipoFijojubilado / premium / normalReglas y estadísticas por clase
llegada, inicio, finHistoria8.0, 10.0, 13.0Medir esperas y vidas
espera, vidaDerivado2.0, 5.0Reportar (calcular, no guardar)

03 · Para qué sirven

Decidir y medir: los dos trabajos

  1. 1
    Ordenar.

    La disciplina deja de ser “el primero que llegó” y pasa a ser “el de menor (prioridad, llegada)”. Dos campos deciden todo el orden. (Temas 25–26 y 39)

  2. 2
    Enrutar.

    El tipo elige servidor o cola: premium → caja 1, piezas tipo B → máquina B, request de video → worker GPU.

  3. 3
    Medir por clase.

    Con atributos podés reportar “espera media de prioritarios vs. normales” y descubrir que el promedio global esconde una injusticia.

  4. 4
    Auditar.

    Con llegada/inicio/fin reconstruís la película de C7 ante un reclamo: cuándo llegó, cuánto esperó, quién lo atendió.

Atributo de entidad
Vive en la entidad (C7.prioridad). Viaja con ella si migra de cola en cola.
Atributo de recurso
Vive en la estática (caja.velocidad, worker.GPU). Decide quién atiende qué.
Variable global
Vive en el sistema (reloj, contador). No es atributo aunque lo lea todo el mundo.
Etiqueta vs. medida
La etiqueta clasifica (tipo, prioridad); la medida cuantifica (espera, vida). Ambas son atributos, distinto uso.

Sin atributos

FIFO ciego

Todos iguales, un solo promedio, cero políticas. Simple pero injusto e inútil para dimensionar por clases.

Con atributos

Políticas y lupa

Prioridades, tipos, esperas por clase y auditoría. Cuesta un dict por entidad y una regla de orden.

04 · Ejemplos

Los mismos clientes, distintas etiquetas

atributos (prio, llegada)→regla min(prio, llegada)→quién sigue
  1. 1
    Banco.

    C7 (jubilado, prio 1, llegó 8) pasa antes que C5 (normal, prio 2, llegó 4). Sin prioridad, C5 pasaba primero.

  2. 2
    Web.

    R12 (premium) salta la cola de 20 normales. El atributo plan decide latencias por clase.

  3. 3
    Guardia.

    Triage rojo/amarillo/verde: el color es un atributo fijo que ordena vidas, no llegadas.

  4. 4
    Fábrica.

    Pieza tipo B requiere máquina B: el atributo operación enruta, no el orden.

05 · Representación en Python

Dict con campos + orden por tupla

La entidad es un dict; el orden es ordenar por (prioridad, llegada). Los derivados son funciones puras.

Python en tu navegador. El primer bloque ordena por atributos; el segundo mide por clase. Cambiá prioridades y re-ejecutá.

cola = [
    {"id": "C5", "prio": 2, "llegada": 4.0},
    {"id": "C6", "prio": 2, "llegada": 6.0},
    {"id": "C7", "prio": 1, "llegada": 8.0},  # prioritario aunque llegó último
]
cola_ordenada = sorted(cola, key=lambda e: (e["prio"], e["llegada"]))
print([e["id"] for e in cola_ordenada])  # ['C7', 'C5', 'C6']
def espera(e):
    return e["inicio"] - e["llegada"]  # derivado: no se guarda

atendidas = [
    {"id": "C5", "prio": 2, "llegada": 4.0, "inicio": 10.0},
    {"id": "C7", "prio": 1, "llegada": 8.0, "inicio": 9.0},
]
for e in atendidas:
    print(e["id"], "esperó", espera(e))

06 · Exploración

Laboratorio: la misma fila, dos justicias

Cinco clientes con llegadas fijas (1, 2, 4, 6, 8). Con el interruptor elegí FIFO (ordena por llegada) o Prioridades (ordena por (prio, llegada)). El control prioridad de C5 lo convierte en normal (2) o VIP (1). Mirá cómo cambia quién sigue y la espera media por clase.

EXPERIMENTO 06

El campo que cambia el orden

FIFO: min(llegada) · Prioridades: min(prio, llegada)

Los resultados numéricos aparecen debajo.
Orden de atención—
Espera media VIP (prio 1)—
Espera media normal—
El último espera—

Cambiá la disciplina para comparar.

Barras: espera de cada cliente en orden de atención (lima VIP, naranja normal). La etiqueta muestra id + espera. Con FIFO manda la llegada; con prioridades manda el campo prio.

Preguntas para explorar

  1. Con FIFO, ¿quién es primero y quién último? ¿Importa la prioridad de C5?
  2. Con Prioridades y C5 normal, ¿quién salta al frente? ¿Cuánto espera C5?
  3. Convertí a C5 en VIP (prio 1, llegó primero): ¿qué pasa con las medias por clase?
Ver respuestas sugeridas
  1. FIFO atiende por llegada: C5 primero aunque sea normal; la prioridad se ignora por diseño.
  2. Con C5 normal, el VIP (C7, prio 1) salta al frente aunque llegó 4º; C5, primero en llegar, termina esperando lo que dure vaciar a los VIP.
  3. Si C5 también es VIP, los VIP copan el frente y los normales esperan más: la media global puede igualarse pero la brecha por clase se invierte. Los atributos revelan la injusticia que el promedio esconde.

07 · Comprensión

Confusiones frecuentes

«El ID es un atributo más»

El ID identifica, no clasifica ni mide. Prioridad y tipo deciden; llegada/inicio/fin miden. Mezclarlos lleva a ordenar por ID, que es arbitrario.

«La espera se guarda en la entidad»

La espera se deriva de inicio − llegada. Guardarla congela un valor que se desactualiza al reprogramar el inicio.

«Más atributos = mejor modelo»

Cada atributo debe decidir o medir algo. El color favorito del cliente no ordena ni mide: es ruido que cuesta memoria y confunde.

«Cambiar atributos en la fila está bien»

Cambiar prioridad a mano para “desempatar” falsea la política. La regla se escribe una vez y se aplica; los atributos se leen.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: ordenar por dos claves

Ordená esta cola por (prio, llegada) y decí quién sigue.

cola = [
    {"id": "A", "prio": 2, "llegada": 1.0},
    {"id": "B", "prio": 1, "llegada": 5.0},
    {"id": "C", "prio": 1, "llegada": 3.0},
]
orden = sorted(cola, key=lambda e: (e["prio"], e["llegada"]))
print([e["id"] for e in orden])
Ver solución razonada

['C', 'B', 'A']: primero los prio 1 entre sí por llegada (C antes que B), y al final los prio 2. La tupla decide sin if enredados.

Ejercicio 2: espera por clase

Calculá la espera media de VIP y de normales con estos atendidos.

ats = [
    {"id": "C5", "prio": 2, "llegada": 4.0, "inicio": 10.0},
    {"id": "C7", "prio": 1, "llegada": 8.0, "inicio": 9.0},
    {"id": "C8", "prio": 1, "llegada": 9.0, "inicio": 12.0},
]
for p in [1, 2]:
    esp = [a["inicio"] - a["llegada"] for a in ats if a["prio"] == p]
    print("prio", p, "->", sum(esp) / len(esp))
Ver solución

VIP: (1.0 + 3.0)/2 = 2.0; normal: 6.0. El promedio global (10/3 ≈ 3.3) esconde que los normales esperan el triple: por eso se mide por clase.

Ejercicio 3: constructor único

Escribí nueva_entidad() que fije claves estables y evitá el caos de nombres.

Ver una posible respuesta
contador = 0
def nueva_entidad(llegada, prio=2, tipo="normal"):
    global contador
    contador += 1
    return {"id": f"C{contador}", "llegada": llegada,
            "prio": prio, "tipo": tipo, "inicio": None, "fin": None}

print(nueva_entidad(8.0, prio=1, tipo="vip"))

Todas nacen con las mismas claves; inicio/fin en None hasta vivir. Ninguna rutina falla por clave inexistente.

09 · Síntesis

Ideas para recordar

  • Atributo = dato de la entidad que distingue, decide y mide; sin ellos solo hay FIFO.
  • Fijos (prio, tipo) se sortean al nacer; historia (llegada, inicio, fin) se completa; derivados (espera, vida) se calculan.
  • Ordenar es min(prio, llegada); enrutar es leer tipo; medir es agrupar por clase.
  • En Python: dict con claves estables + sorted(key=…) + funciones para derivados.
  • Los atributos se leen, no se reescriben para forzar el orden.

En el próximo tema veremos los recursos: las entidades estáticas con capacidad limitada por las que compiten las entidades.