Está en la página 1de 32

INSTITUTO TECNOLÓGICO SUPERIOR DE MISANTLA

INGENIERÍA SISTEMAS COMPUTACIONALES

“SIMULACION DISCRETA DE UNA COLA EN R”

ASIGNATURA
INVESTIGACION DE OPERACIONES

DOCENTE
DR. SIMÓN PEDRO ARGUIJO HERNÁNDEZ

ALUMNOS
JOSÉ MANUEL VIVEROS BARRADAS

SEMESTRE
TERCERO

GRUPO
303A

Simulación de cola discreta

MISANTLA, VER A 07 DE NOVIEMBRE DE 2023


Consideremos un modelo matemático que simula la espera en la fila de un
supermercado con un solo cajero. Este modelo fragmenta el tiempo en pequeños
intervalos, y en cada uno de ellos, ejecuta un procedimiento de generación de
números aleatorios para determinar si ingresa un nuevo cliente o si el cajero
concluye la atención a uno. En caso de que llegue un nuevo cliente, la cola se
incrementa en uno; por otro lado, si el cajero finaliza la atención a un cliente, la
cola disminuye en uno. Este proceso se repite en intervalos de tiempo sucesivos.

Al finalizar la simulación, se obtiene una representación gráfica que muestra la


variación en la longitud de la fila a lo largo del tiempo. Este enfoque posibilita el
análisis y la visualización del comportamiento dinámico de la cola en el
supermercado, brindando información valiosa acerca de la eficiencia del servicio y
la gestión del flujo de clientes.
Los parámetros iniciales del sistema definen su comportamiento durante la
simulación. Estos incluyen la tasa de llegada (λ), que representa el promedio de
clientes que llegan por unidad de tiempo; la tasa de servicio (μ), que indica el
promedio de clientes que el servidor puede atender por unidad de tiempo; la
duración total de la simulación (t.end); y el paso de tiempo (t.step), que define la
subdivisión de la simulación en intervalos específicos. Los valores iniciales para
estos parámetros son λ = 2, μ = 0.5, t.end = 100, t.step = 0.05 y rand.seed = 99.
Para garantizar la reproducibilidad de la simulación, se utiliza la función
set.seed(rand.seed) para fijar la semilla del generador de números aleatorios.

La variable 'queue' se define como un vector que almacena la longitud de la cola


en cada instante de tiempo: queue <- rep(0, t.end/t.step + 1).

La simulación se lleva a cabo en cada paso de tiempo definido por t.step mediante
un bucle for. En cada iteración del bucle, se genera un número aleatorio para
determinar si hay una nueva llegada con probabilidad λt.step. Si hay una llegada,
se incrementa el tamaño de la cola; si hay una salida (con probabilidad μt.step), se
reduce la cola, asegurando que nunca sea negativa. Si no ocurre ninguna de estas
dos situaciones, el tamaño de la cola permanece constante.

El código de simulación de colas proporciona una herramienta flexible para


modelar diversos escenarios donde los clientes o usuarios esperan ser atendidos.
Este enfoque es aplicable a situaciones como supermercados, restaurantes y
hospitales, permitiendo experimentar con diferentes condiciones y evaluar el
rendimiento de sistemas de colas en entornos reales.

Finalmente, la función plot() se utiliza para graficar la evolución del tamaño de la


cola en función del tiempo, proporcionando una representación visual del
comportamiento dinámico del sistema de colas. El título de la gráfica incorpora
información sobre la tasa de llegada (λ) y la tasa de servicio (μ) utilizadas en la
simulación.

Simulación de cola con distribuciones exponenciales


La función a continuación emula la dinámica de una cola de clientes mediante la
generación aleatoria de intervalos entre llegadas y tiempos de atención,
distribuidos de manera exponencial. Sus parámetros incluyen:

 a: el tiempo PROMEDIO entre las llegadas de clientes a la cola. Por


ejemplo, si a = 5, esto indica que, en promedio, los clientes llegan cada 5
minutos.

 p: el tiempo PROMEDIO de atención para cada cliente. Por ejemplo, si p =


3, esto indica que, en promedio, el servidor tarda 3 minutos en atender a
cada cliente.

 numarrivals: la cantidad total de clientes que llegarán durante la


simulación.

La función utiliza estos promedios para simular el proceso completo de la cola y


devuelve datos que indican la eficiencia del sistema:

queue1 <- function(a, p, numarrivals) {


wait_time <- 0; # tiempo de espera excluyendo el tiempo de servicio
total_wait_time <- 0; # tiempo total de espera
total_idle_time <- 0; # tiempo total de inactividad
total_arrival_time <- 0; # hora total de llegada
# Se asume que la cola ya tiene un cliente que llegó a la hora
total_arrival_time = 0
# Este cliente es el cliente 0
for (i in 1:numarrivals) {
service_time <- rexp(1, 1/p); # Tiempo de servicio de la cliente i-1
interarival_time <- rexp(1, 1/a); #Tiempo de llegada entre la cliente i-1 e i
total_arrival_time <- total_arrival_time + interarival_time;
# avanza el reloj desde la hora de llegada total
# hasta la hora de llegada total+tiempo de intercalación
wait_time <- wait_time - interarival_time + service_time;
# tiempo_de_espera+tiempo_de_servicio es el tiempo de espera del
cliente i
# dado que el cliente (i-1) esperó el tiempo de espera
# if wait_time>=0, es el trabajo encontrado por el cliente
# if wait_time<0, es el tiempo de inactividad del servidor entre la
finalización del cliente i-1
# y el inicio del cliente i
if (wait_time >= 0)
total_wait_time <- total_wait_time + wait_time
# total_wait_time incluye el tiempo de espera del (i) ésimo cliente
else {
total_idle_time <- total_idle_time - wait_time;
# el n-ésimo cliente no experimenta una espera pero encuentra el
# servicio inactivo
wait_time <- 0; # la cola está inactiva, el tiempo de espera del cliente i
es cero
}
}
result <- data.frame(Utilization=1-total_idle_time/total_arrival_time,
TimeInQueue=total_wait_time/numarrivals,
TimeInSystem=total_wait_time/numarrivals+p,
NumberInQueue=total_wait_time/total_arrival_time,
NumberInSystem=total_wait_time/total_arrival_time+p/a);
return(result)
}
Simule diferentes tiempos de llegada, diferentes tiempos y diferentes
clientes.

Qué ocurre si ejecuta dos veces seguida la misma instrucción.

La función queue1() emplea números aleatorios para generar los tiempos de


llegada y los tiempos de servicio de los clientes. Esto implica que los resultados de
la simulación pueden diferir entre ejecuciones, incluso cuando se proporcionan los
mismos parámetros iniciales.

Actualmente, queue1() no incorpora una semilla aleatoria fija, lo que significa que
en cada ejecución se generan nuevos números aleatorios. Esta falta de
consistencia en los tiempos de llegada y servicio puede resultar en variaciones en
las métricas estadísticas del sistema simulado. Por ejemplo, el tiempo promedio
de espera o el número promedio de clientes en la cola pueden fluctuar de una
ejecución a otra.
Para mitigar esta variabilidad en los resultados de la simulación, se sugiere
establecer una semilla aleatoria fija para queue1(). Este enfoque garantizará que
se generen los mismos números aleatorios en cada ejecución de la función,
brindando así resultados más consistentes y comparables entre simulaciones.

Sus resultados son los mismos que los de su compañero considerando los
mismos parámetros.

La función queue1() utiliza números aleatorios para generar los tiempos de llegada
y los tiempos de servicio de los clientes. Esto significa que los resultados de la
simulación pueden variar, incluso cuando se utilizan los mismos parámetros de
entrada.

Una manera de entender cómo esto es posible es imaginar que estamos


simulando una cola de clientes en un supermercado. Los tiempos de llegada de
los clientes se pueden modelar mediante una distribución exponencial, que es una
distribución de probabilidad que se utiliza para modelar la ocurrencia de eventos
aleatorios que ocurren a intervalos irregulares.

Por ejemplo, si la tasa de llegada de clientes al supermercado es de 10 clientes


por hora, esto significa que, en promedio, se espera que un cliente llegue cada 6
minutos. Sin embargo, no es posible predecir exactamente cuándo llegará un
cliente. Podría llegar en 5 minutos, 7 minutos, o incluso 10 minutos.

Debido a que los tiempos de llegada son aleatorios, los resultados de la


simulación también serán aleatorios. Si ejecutamos la simulación varias veces,
obtendremos resultados diferentes cada vez.

service_time <- rexp(1, 1/p);

La función rexp() genera números aleatorios con una distribución exponencial.


Esto significa que los tiempos de servicio generados por la función rexp() varían
de una ejecución a otra, incluso si se utilizan los mismos parámetros de entrada.

Una forma de entender cómo esto es posible es imaginar que estamos simulando
una cola de clientes en un supermercado. Los tiempos de servicio de los clientes
se pueden modelar mediante una distribución exponencial, que es una distribución
de probabilidad que se utiliza para modelar la ocurrencia de eventos aleatorios
que ocurren a intervalos irregulares.

Por ejemplo, si establecemos el valor de p en 3, esto significa que, en promedio,


los clientes tardarán 3 minutos en ser atendidos. Sin embargo, no es posible
predecir exactamente cuánto tiempo tardará un cliente en ser atendido. Podría
tardar 2 minutos, 4 minutos, o incluso más.

Debido a que los tiempos de servicio son aleatorios, los resultados de la


simulación también serán aleatorios. Si ejecutamos la simulación varias veces,
obtendremos resultados diferentes cada vez.

Esta variabilidad en los tiempos de servicio puede provocar variaciones en las


métricas estadísticas calculadas al final de la simulación.

Ejemplo 1 con Simmer

Este código en R hace uso del paquete simmer para llevar a cabo una simulación
de eventos discretos, representando así un proceso médico elemental. El modelo
simula de manera detallada las distintas fases que experimentan los pacientes,
desde su admisión hasta la consulta con el médico y la planificación
administrativa.

library(simmer)

set.seed(42)

env <- simmer("SuperDuperSim")


env

patient <- trajectory("patients' path") %>%


## add an intake activity
seize("nurse", 1) %>%
timeout(function() rnorm(1, 15)) %>%
release("nurse", 1) %>%
## add a consultation activity
seize("doctor", 1) %>%
timeout(function() rnorm(1, 20)) %>%
release("doctor", 1) %>%
## add a planning activity
seize("administration", 1) %>%
timeout(function() rnorm(1, 5)) %>%
release("administration", 1)

env %>%
add_resource("nurse", 1) %>%
add_resource("doctor", 2) %>%
add_resource("administration", 1) %>%
add_generator("patient", patient, function() rnorm(1, 10, 2))

env %>%
run(80) %>%
now()
env %>% peek(3)

env %>%
stepn() %>% # 1 step
print() %>%
stepn(3) # 3 steps
env %>% peek(Inf, verbose=TRUE)

env %>%
run(120) %>%
now()

env %>%
reset() %>%
run(80) %>%
now()

Explicación del código:

 Configuración del entorno:

library(simmer)
set.seed(42)
env <- simmer("SuperDuperSim")

En primer lugar, se carga la biblioteca simmer, la cual suministra las funciones


esenciales para llevar a cabo simulaciones de sistemas de eventos discretos.
Posteriormente, se establece una semilla con el fin de garantizar la consistencia
en los resultados de la simulación. Por último, se instancia un entorno de
simulación bajo el nombre de "SuperDuperSim".

 Definición de la trayectoria del paciente:

patient <- trajectory("patients' path") %>%


seize("nurse", 1) %>%
timeout(function() rnorm(1, 15)) %>%
release("nurse", 1) %>%
seize("doctor", 1) %>%
timeout(function() rnorm(1, 20)) %>%
release("doctor", 1) %>%
seize("administration", 1) %>%
timeout(function() rnorm(1, 5)) %>%
release("administration", 1)

La trayectoria del paciente engloba una serie de tareas que deben llevarse a cabo
para culminar el proceso médico. Estas tareas abarcan desde la asignación de
recursos hasta los intervalos de espera y la liberación de dichos recursos.

 Configuración de recursos y generador:

env %>%
add_resource("nurse", 1) %>%
add_resource("doctor", 2) %>%
add_resource("administration", 1) %>%
add_generator("patient", patient, function() rnorm(1, 10, 2))

Los elementos del sistema, como el personal de enfermería, los médicos y el


equipo administrativo, se instancian y se incorporan al entorno de simulación.
Además, se establece un generador de pacientes que imita el ingreso de
pacientes al sistema.

 Ejecución de la simulación:

env %>%
run(80) %>%
now()

Se ejecuta la simulación durante 80 unidades de tiempo


 Inspección de la simulación:

env %>% peek(3)

Dejará visualizar las 3 próximas actividades planificadas en el entorno

 Ejecución y visualización paso a paso:

env %>%
stepn() %>%
print() %>%
stepn(3)

Te deja visualizar una simulación, mostrando la información de cada paso.

 Inspección continua de la simulación:

env %>% peek(Inf, verbose=TRUE)


 Ejecución de una segunda fase de la simulación:

env %>%
run(120) %>%
now()

 Reinicio de la simulación y ejecución final:

env %>%
reset() %>%
run(80) %>%
now()
Por lo tanto, el código imprime:
Este script emplea el paquete simmer para representar y ejecutar la simulación de
un proceso médico elemental, donde los pacientes atraviesan diversas actividades
y utilizan recursos específicos en un contexto de eventos discretos. La simulación
progresa a través de múltiples etapas, permitiendo la inspección detallada y la
visualización de la dinámica del sistema en diferentes momentos.
Ejemplo 2 con Simmer

El código usa la librería conocida como “simmer” de R para pretender tener un


sistema de eventos discretos. Pero, en esta instancia, la simulación se realiza de
forma paralela utilizando el paquete parallel.

library(parallel)

envs <- mclapply(1:100, function(i) {


simmer("SuperDuperSim") %>%
add_resource("nurse", 1) %>%
add_resource("doctor", 2) %>%
add_resource("administration", 1) %>%
add_generator("patient", patient, function() rnorm(1, 10, 2)) %>%
run(80) %>%
wrap()
})
envs[[1]] %>% get_n_generated("patient")

envs[[1]] %>% get_queue_count("doctor")

envs[[1]] %>% get_queue_size("doctor")

envs %>%
get_mon_resources() %>%
head()

envs %>%
get_mon_arrivals() %>%
head()

1. Carga de la biblioteca y configuración paralela:

library(parallel)

2. Creación de múltiples entornos de simulación en paralelo:

envs <- mclapply(1:100, function(i) { simmer("SuperDuperSim") %>%


add_resource("nurse", 1) %>% add_resource("doctor", 2) %>%
add_resource("administration", 1) %>% add_generator("patient", patient,
function() rnorm(1, 10, 2)) %>% run(80) %>% wrap() })
3. Consulta de estadísticas del primer entorno de simulación:

envs[[1]] %>% get_n_generated("patient")

Número de pacientes que se generaron el primer entorno de simulación.

envs[[1]] %>% get_queue_count("doctor")

Cantidad de pacientes que actualmente esperan para ver al médico en el primer


entorno de simulación.

envs[[1]] %>% get_queue_size("doctor")

Determina el tamaño máximo de la cola de pacientes esperando para ver al


médico en el primer entorno de simulación.

4. Consulta de estadísticas agregadas de todos los entornos de


simulación:

Para comparar las estadísticas de los recursos en todos los escenarios de


simulación, podemos utilizar el siguiente código:

envs %>% get_mon_resources() %>% head()

Obtén las estadísticas agregadas de llegadas de todos los entornos de simulación.

envs %>% get_mon_arrivals() %>% head()


El resultado de la ejecución del código es la siguiente:
Ejemplo 3 con Simmer

Este script en R hace uso de la librería simmer para crear una trayectoria simple
de simulación y luego presenta los métodos que están disponibles para los objetos
pertenecientes a la clase "trayectoria".

library(simmer)
traj <- trajectory() %>%
seize(resource = "doctor", amount = 1) %>%
timeout(task = 3) %>%
release(resource = "doctor", amount = 1)
methods(class="trajectory")

1. Carga de la biblioteca:

library(simmer)

2. Definición de una trayectoria de simulación:

traj <- trajectory() %>% seize(resource = "doctor", amount = 1) %>%


timeout(task = 3) %>% release(resource = "doctor", amount = 1)

3. Imprimir los métodos de la clase "trajectory":

methods(class="trajectory")
El resultado de la ejecución del código es la siguiente:
Ejemplo 4 con Simmer

En el siguiente script de R, se emplea la librería simmer para establecer un


entorno de simulación elemental, construir una trayectoria y llevar a cabo una
breve simulación.

library(simmer)

# first, instantiate the environment


env <- simmer()

# here I'm using it


traj <- trajectory() %>%
log_(function() as.character(now(env)))

# and finally, run it


env %>%
add_generator("dummy", traj, function() 1) %>%
run(4) %>% invisible

1. Carga de la biblioteca:

library(simmer)

2. Creación del entorno de simulación:


env <- simmer()

3. Definición de una trayectoria de simulación:

traj <- trajectory() %>% log_(function() as.character(now(env)))

4. Agregar un generador al entorno de simulación:

env %>% add_generator("dummy", traj, function() 1)

5. Ejecución de la simulación:

env %>% run(4) %>% invisible

Resultado de la ejecución del código:

Ejemplo 5 con Simmer

Se importa la librería simmer en R, la cual brinda las funcionalidades necesarias


para llevar a cabo simulaciones de eventos discretos.

library(simmer)
customer <-
trajectory("Customer's path") %>%
log_("Here I am") %>%
timeout(10) %>%
log_("I must leave")

bank <-
simmer("bank") %>%
add_generator("Customer", customer, at(5))

bank %>% run(until = 100)


bank %>% get_mon_arrivals()

1. Carga de la biblioteca:

library(simmer)

2. Definición de la trayectoria del cliente:

customer <- trajectory("Customer's path") %>% log_("Here I am") %>%


timeout(10) %>% log_("I must leave")

3. Creación del entorno del banco y configuración del generador:

bank <- simmer("bank") %>% add_generator("Customer", customer, at(5))

4. Ejecución de la simulación hasta el tiempo 100:

bank %>% run(until = 100)

5. Obtención de estadísticas de llegadas (arrivals):

bank %>% get_mon_arrivals()


Resultado de la ejecución:

Ejemplo 6 con Simmer

Se carga la biblioteca simmer en R, la cual ofrece funcionalidades esenciales para


la simulación de sistemas basados en eventos discretos.

library(simmer)

# Function to specify a series of waiting times in a loop


loop <- function(...) {
time_diffs <- c(...)
i <- 0
function() {
if (i < length(time_diffs)) {
i <<- i+1
} else {
i <<- 1
}
return(time_diffs[i])
}
}

customer <-
trajectory("Customer's path") %>%
log_("Here I am") %>%
timeout(loop(7, 10, 20)) %>%
log_("I must leave")

bank <-
simmer("bank") %>%
add_generator("Customer", customer, at(2, 5, 12))

bank %>% run(until = 400)


bank %>% get_mon_arrivals()

1. Carga de la biblioteca:

library(simmer)

2. Definición de la función loop:

loop <- function(...) {


time_diffs <- c(...)
i <- 0
function() {
if (i < length(time_diffs)) {
i <<- i+1
} else {
i <<- 1
}
return(time_diffs[i])
}
}
3. Definición de la trayectoria del cliente con tiempos de espera en bucle:

customer <-
trajectory("Customer's path") %>%
log_("Here I am") %>%
timeout(loop(7, 10, 20)) %>%
log_("I must leave")

4. Creación del entorno del banco y configuración del generador:

bank <-
simmer("bank") %>%
add_generator("Customer", customer, at(2, 5, 12))

5. Ejecución de la simulación hasta el tiempo 400:

bank %>% run(until = 400)

6. Obtención de estadísticas de llegadas (arrivals)

bank %>% get_mon_arrivals()

El resultado de la ejecución de código:


Ejemplo 7 con Simmer
El siguiente código en R emplea la librería simmer para llevar a cabo una
simulación de un servicio de lavado de automóviles.

library(simmer)

NUM_MACHINES <- 2 # Number of machines in the carwash


WASHTIME <- 5 # Minutes it takes to clean a car
T_INTER <- 7 # Create a car every ~7 minutes
SIM_TIME <- 20 # Simulation time in minutes

# setup
set.seed(42)
env <- simmer()

car <- trajectory() %>%


log_("arrives at the carwash") %>%
seize("wash", 1) %>%
log_("enters the carwash") %>%
timeout(WASHTIME) %>%
set_attribute("dirt_removed", function() sample(50:99, 1)) %>%
log_(function()
paste0(get_attribute(env, "dirt_removed"), "% of dirt was removed")) %>%
release("wash", 1) %>%
log_("leaves the carwash")

env %>%
add_resource("wash", NUM_MACHINES) %>%
# feed the trajectory with 4 initial cars
add_generator("car_initial", car, at(rep(0, 4))) %>%
# new cars approx. every T_INTER minutes
add_generator("car", car, function() sample((T_INTER-2):(T_INTER+2), 1)) %>%
# start the simulation
run(SIM_TIME)

1. Definición de parámetros:

NUM_MACHINES <- 2 # Número de máquinas en el lavado de autos


WASHTIME <- 5 # Minutos que tarda en limpiar un auto
T_INTER <- 7 # Crear un auto cada ~7 minutos
SIM_TIME <- 20 # Tiempo de simulación en minutos

2. Configuración del entorno de simulación:

set.seed(42)

3. Definición de la trayectoria del auto:

car <- trajectory() %>%


log_("arrives at the carwash") %>%
seize("wash", 1) %>%
log_("enters the carwash") %>%
timeout(WASHTIME) %>%
set_attribute("dirt_removed", function() sample(50:99, 1)) %>%
log_(function()
paste0(get_attribute(env, "dirt_removed"), "% of dirt was removed")) %>%
release("wash", 1) %>%
log_("leaves the carwash")

4. Configuración de recursos y generadores:

env %>%
add_resource("wash", NUM_MACHINES) %>%
add_generator("car_initial", car, at(rep(0, 4))) %>%
add_generator("car", car, function() sample((T_INTER-2):(T_INTER+2), 1))

5. Ejecución de la simulación:
env %>% run(SIM_TIME)

El resultado de la ejecución del código es la siguiente:

Resultados

En este documento, se presentan dos enfoques para la simulación de sistemas de


colas con un solo servidor, representativos de situaciones como una caja en un
supermercado o un mesero en un restaurante.
El primer enfoque es una simulación paso a paso que registra eventos de llegada
y salida, y grafica dinámicamente el tamaño de la cola. Este enfoque permite la
variación de parámetros como la tasa de llegada (λ) y la tasa de servicio (μ) para
comparar diferentes comportamientos.

El segundo enfoque simula tiempos de llegada y servicio utilizando distribuciones


exponenciales. En lugar de ofrecer detalles paso a paso, calcula métricas resumen
en un solo experimento de simulación. Proporciona estadísticas promedio del
sistema, como el tiempo promedio en cola, la utilización del servidor y la cantidad
promedio de clientes en el sistema.

Ambos enfoques ofrecen ventajas y desventajas.

El enfoque paso a paso es más detallado y permite una mejor comprensión del
comportamiento del sistema. Sin embargo, también es más complejo y requiere
más tiempo de ejecución.

El enfoque basado en distribuciones exponenciales es más simple y rápido, pero


proporciona menos información sobre el comportamiento del sistema.

En conjunto, los dos enfoques ofrecen una herramienta integral para modelar,
analizar y optimizar sistemas prácticos de colas de clientes.

El paquete simmer de R para la simulación de eventos discretos

El paquete simmer de R se utiliza para la simulación de eventos discretos. En este


documento, se presentan varios códigos que ilustran su versatilidad:

 Simulación de proceso médico: Modela un proceso médico simple con


actividades de admisión, consulta médica y planificación administrativa.

 Simulación paralela de proceso médico: Ejecuta múltiples simulaciones en


paralelo, explorando diferentes escenarios de manera eficiente.

 Simulación con trayectoria simple: Simula un proceso sencillo donde un


objeto sigue una trayectoria específica en el tiempo.
 Simulación de lavado de autos: Modela un lavado de autos con múltiples
máquinas y tiempos de lavado variables.

Estos códigos demuestran cómo usar el paquete simmer para modelar y simular
procesos complejos, asignar recursos y ejecutar simulaciones en entornos
controlados. Las operaciones de inspección son cruciales para comprender el
comportamiento de los sistemas modelados.

En resumen, el paquete simmer proporciona una herramienta poderosa para la


simulación de eventos discretos en R.

Anexos
simmer. (s.f.). Obtenido de simmer: https://r-simmer.org/articles/

También podría gustarte