{"id":1453,"date":"2018-06-07T14:09:49","date_gmt":"2018-06-07T19:09:49","guid":{"rendered":"http:\/\/naps.com.mx\/blog\/?p=1453"},"modified":"2018-07-04T11:28:35","modified_gmt":"2018-07-04T16:28:35","slug":"simulacion-en-python-usando-simpy","status":"publish","type":"post","link":"https:\/\/naps.com.mx\/blog\/simulacion-en-python-usando-simpy\/","title":{"rendered":"Simulaci\u00f3n en Python usando Simpy: llegadas y servicio"},"content":{"rendered":"<p><em><strong>Realizaremos una simulaci\u00f3n en Python usando Simpy de eventos discretos (simulaci\u00f3n de llegadas y servicio).<span class=\"Apple-converted-space\">\u00a0<\/span><\/strong><\/em><\/p>\n<p><!--more--><\/p>\n<p>El ejemplo que vamos a utilizar es el siguiente: Una peluquer\u00eda tiene un peluquero que se demora entre 15 y 30 minutos por corte. La peluquer\u00eda recibe en promedio 3 clientes por hora (es decir, uno cada 20 minutos). Se desea simular las llegadas y servicios de 5 clientes.<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<p>Una vez realizado el c\u00f3digo, podremos variar cualquiera de \u00e9stos par\u00e1metros (tener varios peluqueros, cambiar el tiempo que se demoran por corte, simular para n clientes o para un tiempo espec\u00edfico como una cierta cantidad de minutos, etc).<\/p>\n<p>Antes de realizar la simulaci\u00f3n en Python usando Simpy, vamos a hacer los c\u00e1lculos manuales de acuerdo a las t\u00e9cnicas de Simulaci\u00f3n. Esto nos permitir\u00e1 tener un modelo matem\u00e1tico que nos confirme los resultados de nuestra simulaci\u00f3n.<\/p>\n<h2>Simulaci\u00f3n de eventos discretos<\/h2>\n<p>El tiempo entre llegadas de los clientes a la peluquer\u00eda es exponencial con media de 20 minutos.<\/p>\n<p>La peluquer\u00eda es atendida por un solo peluquero y se lleva entre 15 y 30 minutos, distribuidos de una manera uniforme, para realizar un corte de pelo.<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<p>Los clientes son atendidos con base en la disciplina FIFO (primero en llegar, primero en salir).<\/p>\n<p>De modo que <strong>las llegadas son calculadas de acuerdo a la siguiente f\u00f3rmula<\/strong><em>:<\/em><\/p>\n<p><strong>T_LLEGADAS<\/strong> = <strong>&#8211;<span class=\"wp-katex-eq\" data-display=\"false\">\\lambda<\/span> ln(R)<\/strong><\/p>\n<p>Donde:<\/p>\n<p><strong><span class=\"wp-katex-eq\" data-display=\"false\">\\lambda<\/span><\/strong> es el promedio de llegadas, en nuestro ejemplo es de 20 minutos.<\/p>\n<p><strong>Ln<\/strong> es la operaci\u00f3n de Logaritmo Natural<\/p>\n<p><strong>R<\/strong> es un n\u00famero pseudoaleatorio<\/p>\n<h4><strong>Los tiempos de servicio son calculados de acuerdo a la siguiente f\u00f3rmula<\/strong>:<\/h4>\n<p><strong>tiempo_corte = TIEMPO_CORTE_MIN + (TIEMPO_CORTE_MAX &#8211; TIEMPO_CORTE_MIN) R<\/strong><\/p>\n<p>Esto es: el m\u00ednimo de tiempo que se demora el peluquero, en nuestro ejemplo es 15, m\u00e1s la diferencia entre el m\u00e1ximo y el m\u00ednimo, en nuestro ejemplo ser\u00edan 15 minutos (30 menos 15), multiplicado por un n\u00famero pseudoaleatorio. El resultado nos dar\u00e1 un n\u00famero entre 15 y 30.<\/p>\n<p>Los n\u00fameros pseudoaleatorios que utilizaremos ser\u00e1n los siguientes:<\/p>\n<p><strong>0.5391,\u00a00.2892,\u00a00.6536,\u00a00.2573,\u00a00.6416,\u00a00.0300,\u00a00.2100,\u00a00.3972,\u00a00.9888,\u00a00.4615<\/strong><\/p>\n<p>Utilizaremos los primeros 5 para calcular las llegadas y los \u00faltimos 5 para calcular los tiempos de servicio.<\/p>\n<h3>C\u00e1lculo de los tiempos de llegada:<\/h3>\n<p>Cliente 1.<span class=\"Apple-converted-space\">\u00a0 <\/span>-20<span class=\"Apple-converted-space\">\u00a0 <\/span>* ln(0.5391) = 12.36<\/p>\n<p>Cliente 2. -20 * ln(0.2892) = 24.81<span class=\"Apple-converted-space\">\u00a0 <\/span>(le sumamos 12.36 el resultado es: 37.17)<\/p>\n<p>Cliente 3. -20 * ln(0.6536) = 8.50<span class=\"Apple-converted-space\">\u00a0 <\/span>+ 37.17 = 45.67<\/p>\n<p>Cliente 4. -20 * ln (0.2573) = 27.15 + 45.67 = 72.83<\/p>\n<p>Cliente 5. -20 * ln(0.6416) = 8.87 <span class=\"Apple-converted-space\">\u00a0 <\/span>+ 72.83 = 81.70<\/p>\n<p><strong>Breve explicaci\u00f3n<\/strong>: Entre el minuto 0 y la llegada del cliente 1 pasaron 12.36 minutos. Entre el minuto 12.36 y la llegada del cliente 2 pasaron 24.81 minutos, por lo que el cliente 2 lleg\u00f3 en el minuto 37.17.<\/p>\n<h3>C\u00e1lculo de los tiempos de servicio:<\/h3>\n<p>Cliente 1. 15 + (15 * 0.0300) = 15.45<\/p>\n<p>Cliente 2. 15 + (15 * 0.2100) = 18.15<\/p>\n<p>Cliente 3. 15 + (15 * 0.3972) = 20.96<\/p>\n<p>Cliente 4. 15 + (15 * 0.9888) = 29.83<\/p>\n<p>Cliente 5. 15 + (15 * 0.4615) = 21.92<\/p>\n<p><strong>Breve explicaci\u00f3n:<\/strong> El cliente 1 demor\u00f3 15.45 minutos, el cliente 2 demor\u00f3 18.15 minutos. El cliente que m\u00e1s demor\u00f3 fue el 4, demor\u00f3 29.83 minutos. Como puedes notar, el tiempo de servicio va de 15 a 30 minutos.<\/p>\n<p>Con \u00e9stos datos podemos construir la siguiente tabla:<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<div id=\"attachment_1471\" style=\"width: 1206px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1.jpeg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1471\" class=\"wp-image-1471 size-full\" src=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1.jpeg\" alt=\"simulacion en python usando simpy\" width=\"1196\" height=\"450\" srcset=\"https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1.jpeg 1196w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1-300x113.jpeg 300w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1-768x289.jpeg 768w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/tabla-simulacion-en-python-usando-simpy-1-1024x385.jpeg 1024w\" sizes=\"auto, (max-width: 1196px) 100vw, 1196px\" \/><\/a><p id=\"caption-attachment-1471\" class=\"wp-caption-text\">Resultados de la simulaci\u00f3n<\/p><\/div>\n<p>Aplicando las f\u00f3rmulas correspondientes podemos calcular la longitud promedio de la cola, el tiempo de espera promedio, y el uso promedio de la instalaci\u00f3n.<\/p>\n<p><strong>Longitud promedio de la cola<\/strong> = 37.51 \/ 128.03<span class=\"Apple-converted-space\">\u00a0 <\/span>=<span class=\"Apple-converted-space\">\u00a0 <\/span>0.29<\/p>\n<p><strong>Tiempo de espera promedio<\/strong> = 37.51 \/ 5<span class=\"Apple-converted-space\">\u00a0 <\/span>= 7.50 minutos<\/p>\n<p><strong>Uso promedio de la instalaci\u00f3n<\/strong> = 106.31 \/ 128.03<span class=\"Apple-converted-space\">\u00a0 <\/span>= 0.83 (La peluquer\u00eda trabaja un 83% del tiempo total)<\/p>\n<h2>Simulaci\u00f3n en Python usando Simpy de una peluquer\u00eda<\/h2>\n<p>Considero que el lector ya ha utilizado Python, por lo que no incluyo la explicaci\u00f3n de la programaci\u00f3n en Python, y nos centraremos en las instrucciones propias de Simpy y la l\u00f3gica del programa.<\/p>\n<h3>Instalaci\u00f3n de Simpy para Simulaci\u00f3n en Python<\/h3>\n<p>Descarga Simpy desde su sitio web:<\/p>\n<p><a href=\"https:\/\/pypi.python.org\/pypi\/simpy#downloads\" target=\"_blank\" rel=\"noopener\">https:\/\/pypi.python.org\/pypi\/simpy#downloads<\/a><\/p>\n<p>Una vez descargado, guarda el archivo en una ubicaci\u00f3n accesibe.<span class=\"Apple-converted-space\">\u00a0<\/span><\/p>\n<p>Abre esa ubicaci\u00f3n (desde Windows escribe cd<span class=\"Apple-converted-space\">\u00a0 <\/span>seguido de la ruta donde guardaste la carpeta de Simpy).<\/p>\n<p><strong>$ cd donde\/colocaste\/simpy\/<\/strong><\/p>\n<p>Ahora escribe el siguiente comando<\/p>\n<p><strong>$ python setup.py install<\/strong><\/p>\n<h2>Programa de simulaci\u00f3n en Python usando Simpy<\/h2>\n<h3>Primera parte: Bibliotecas a utilizar<\/h3>\n<pre class=\"theme:monokai lang:python decode:true \">import random\r\nimport math\r\nimport simpy<\/pre>\n<p>&nbsp;<\/p>\n<p>Usaremos random para generar n\u00fameros pseudoaleatorios, math para usar la funci\u00f3n de logaritmo natural que utilizaremos para obtener las llegadas de los clientes a la peluquer\u00eda, y simpy para tener un entorno de simulaci\u00f3n.<\/p>\n<h3>Segunda parte: Constantes y variables a utilizar<\/h3>\n<pre class=\"theme:monokai lang:python decode:true \">SEMILLA = 30\r\nNUM_PELUQUEROS = 1\r\nTIEMPO_CORTE_MIN = 15\r\nTIEMPO_CORTE_MAX = 30\r\nT_LLEGADAS = 20\r\nTIEMPO_SIMULACION = 120\r\nTOT_CLIENTES = 5\r\n\r\nte  = 0.0 # tiempo de espera total\r\ndt  = 0.0 # duracion de servicio total\r\nfin = 0.0 # minuto en el que finaliza<\/pre>\n<p>&nbsp;<\/p>\n<p>El valor SEMILLA puede ser cualquiera, el que estamos utilizando nos proporciona los n\u00fameros pseudoaleatorios que utilizamos en la simulaci\u00f3n manual.<\/p>\n<p>La constante TIEMPO_SIMULACION no la utilizaremos en este ejemplo, pero nos puede servir m\u00e1s adelante si, en vez de simular 5 clientes, queremos simular una cantidad de tiempo (120 minutos, por ejemplo).<\/p>\n<h3>Tercera parte: Procedimientos<span class=\"Apple-converted-space\">\u00a0<\/span><\/h3>\n<pre class=\"theme:monokai lang:python decode:true \">def cortar(cliente):\r\n\tglobal dt  #Para poder acceder a la variable dt declarada anteriormente\r\n\tR = random.random()  # Obtiene un numero aleatorio y lo guarda en R\r\n\ttiempo = TIEMPO_CORTE_MAX - TIEMPO_CORTE_MIN  \r\n\ttiempo_corte = TIEMPO_CORTE_MIN + (tiempo*R) # Distribucion uniforme\r\n\tyield env.timeout(tiempo_corte) # deja correr el tiempo n minutos\r\n\tprint(\" \\o\/ Corte listo a %s en %.2f minutos\" % (cliente,tiempo_corte))\r\n\tdt = dt + tiempo_corte # Acumula los tiempos de uso de la i\r\n\r\n\r\ndef cliente (env, name, personal ):\r\n\tglobal te\r\n\tglobal fin\r\n\tllega = env.now # Guarda el minuto de llegada del cliente\r\n\tprint (\"---&gt; %s llego a peluqueria en minuto %.2f\" % (name, llega))\r\n\twith personal.request() as request: # Espera su turno\r\n\t\tyield request # Obtiene turno\r\n\t\tpasa = env.now # Guarda el minuto cuado comienza a ser atendido\r\n\t\tespera = pasa - llega # Calcula el tiempo que espero\r\n\t\tte = te + espera # Acumula los tiempos de espera\r\n\t\tprint (\"**** %s pasa con peluquero en minuto %.2f habiendo esperado %.2f\" % (name, pasa, espera))\r\n\t\tyield env.process(cortar(name)) # Invoca al proceso cortar\r\n\t\tdeja = env.now #Guarda el minuto en que termina el proceso cortar \r\n\t\tprint (\"&lt;--- %s deja peluqueria en minuto %.2f\" % (name, deja))\r\n\t\tfin = deja # Conserva globalmente el ultimo minuto de la simulacion\r\n\t\r\n\r\ndef principal (env, personal):\r\n\tllegada = 0\r\n\ti = 0\r\n\tfor i in range(TOT_CLIENTES): # Para n clientes\r\n\t\tR = random.random()\r\n\t\tllegada = -T_LLEGADAS * math.log(R) # Distribucion exponencial\r\n\t\tyield env.timeout(llegada)  # Deja transcurrir un tiempo entre uno y otro\r\n\t\ti += 1\r\n\t\tenv.process(cliente(env, 'Cliente %d' % i, personal)) \r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>El primer procedimiento invocado es el de principal, que a su vez manda a llamar a cliente, que a su vez manda a llamar a cortar.<\/p>\n<p>El procemiento cortar, calcula el tiempo que se demora con cada cliente. El procedimiento cliente muestra el minuto en que el cliente llega, espera a ser atendido, el minuto en que el cliente comienza a ser atendido, y el minuto en el que termina de ser atendido y deja la instalaci\u00f3n.<\/p>\n<p>El procedimiento principal hace un ciclo para n cantidad de clientes, en nuestro ejemplo, 5, y calcula las llegadas (que siguen una distribuci\u00f3n exponencial).<\/p>\n<h3>Cuarta parte: Programa principal<\/h3>\n<pre class=\"theme:monokai lang:python decode:true\">print (\"------------------- Bienvenido Simulacion Peluqueria ------------------\")\r\nrandom.seed (SEMILLA)  # Cualquier valor\r\nenv = simpy.Environment() # Crea el objeto entorno de simulacion\r\npersonal = simpy.Resource(env, NUM_PELUQUEROS) #Crea los recursos (peluqueros)\r\nenv.process(principal(env, personal)) #Invoca el proceso princial\r\nenv.run() #Inicia la simulacion\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>El programa inicia con un mensaje de Bienvenida, y crea un entorno de simulaci\u00f3n. Crea los recursos, en este caso la cantidad de \u00e9l o los peluqueros, y manda a llamar al proceso principal lo que da inicio a la simulaci\u00f3n<\/p>\n<h2>Quinta parte: Calculo de indicadores<\/h2>\n<pre class=\"theme:monokai lang:python decode:true \">print (\"\\n---------------------------------------------------------------------\")\r\nprint (\"\\nIndicadores obtenidos: \")\r\n\r\nlpc = te \/ fin\r\nprint (\"\\nLongitud promedio de la cola: %.2f\" % lpc)\r\ntep = te \/ TOT_CLIENTES\r\nprint (\"Tiempo de espera promedio = %.2f\" % tep)\r\nupi = (dt \/ fin) \/ NUM_PELUQUEROS\r\nprint (\"Uso promedio de la instalacion = %.2f\" % upi)\r\nprint (\"\\n---------------------------------------------------------------------\")<\/pre>\n<p>&nbsp;<\/p>\n<p>Calculamos los indicadores utilizando las f\u00f3rmulas ya menciondas.<\/p>\n<h2>Resultado de la ejecuci\u00f3n de la simulaci\u00f3n en Python usando simpy<\/h2>\n<div id=\"attachment_1469\" style=\"width: 1150px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy.jpeg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-1469\" class=\"size-full wp-image-1469\" src=\"http:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy.jpeg\" alt=\"Ejecuci\u00f3n de la simulaci\u00f3n en Python usando Simpy\" width=\"1140\" height=\"928\" srcset=\"https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy.jpeg 1140w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy-300x244.jpeg 300w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy-768x625.jpeg 768w, https:\/\/naps.com.mx\/blog\/wp-content\/uploads\/2018\/06\/ejecuci\u00f3n-simulaci\u00f3n-en-python-usando-simpy-1024x834.jpeg 1024w\" sizes=\"auto, (max-width: 1140px) 100vw, 1140px\" \/><\/a><p id=\"caption-attachment-1469\" class=\"wp-caption-text\">Ejecuci\u00f3n de la simulaci\u00f3n en Python usando Simpy<\/p><\/div>\n<p>Como podemos observar, debido a que se utilizaron los mismos n\u00fameros pseudoaleatorios obtenemos los mismos resultados. Podemos ahora variar a nuestro gusto los par\u00e1metros proporcionados al programa,<span class=\"Apple-converted-space\">\u00a0 <\/span>por ejemplo, la cantidad de clientes, la cantidad de Peluqueros, o cu\u00e1nto se demoran \u00e9stos en un corte de cabello.<\/p>\n<h2>Otras instrucciones<\/h2>\n<p>Si deseamos que la simulaci\u00f3n se ejecute durante un tiempo espec\u00edfico, en vez de hacerlo para <em><strong>n<\/strong><\/em> clientes:<\/p>\n<p>En la l\u00ednea <strong>env.run<\/strong> cambiamos la llamada a la siguiente orden:<\/p>\n<p><em><strong>env.run(until = TIEMPO_SIMULACION)<\/strong><\/em><\/p>\n<p>Donde <strong>TIEMPO_SIMULACION<\/strong> es una variable declarada al principio del programa.<\/p>\n<p>Tambien debemos ajustar el procedimiento llamado principal. En vez de: <em><strong>for i in range(TOT_CLIENTES):<span class=\"Apple-converted-space\">\u00a0<\/span><\/strong><\/em><\/p>\n<p>Usaremos un ciclo infinito:<\/p>\n<p><em><strong>While True:<\/strong><\/em><\/p>\n<p>Si deseamos que el programa se ejecute en tiempo real, cambiemos la l\u00ednea env = simpy.Environment() por<\/p>\n<p><em><strong>env = simpy.rt.RealtimeEnvironment(factor=1.0)<\/strong><\/em><\/p>\n<h2>Conclusi\u00f3n<\/h2>\n<p>La presente simulaci\u00f3n en Python usando Simpy es una herramienta para obtener resultados en forma m\u00e1s sencilla que si lo hici\u00e9ramos manualmente. Es tambien muy flexible y se pueden crear programas m\u00e1s elaborados que si utiliz\u00e1ramos una hoja de c\u00e1lculo.<\/p>\n<p>Python es un lenguaje multiplataforma, ademas, es software libre, por lo que es ideal para cualquier proyecto acad\u00e9mico.<\/p>\n<p>Es posible adaptar este programa para resolver diferentes problemas y se puede ampliar para obtener m\u00e1s datos cuya soluci\u00f3n manual o matem\u00e1tica pudiera ser complicada.<\/p>\n<p>Deja tus dudas y comentarios, y califica este art\u00edculo. Si est\u00e1s utilizando este art\u00edculo para alg\u00fan trabajo acad\u00e9mico no olvides citarnos:<\/p>\n<p><strong>Garc\u00eda (2018). Simulaci\u00f3n en Python usando Simpy: llegadas y servicio. Disponible en [http:\/\/naps.com.mx\/blog\/simulacion-en-python-usando-simpy\/] consultado el [7-Jun-2018].<\/strong><\/p>\n<p>En twitter hablamos de muchos otros temas: <a href=\"http:\/\/www.twitter.com\/napsmx\" class=\"broken_link\">@napsmx<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Realizaremos una simulaci\u00f3n en Python usando Simpy de eventos discretos (simulaci\u00f3n de llegadas y servicio).\u00a0<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"amp_status":"","footnotes":""},"categories":[216],"tags":[230,231,232],"class_list":["post-1453","post","type-post","status-publish","format-standard","hentry","category-simulacion-ciencias","tag-python","tag-simpy","tag-simulacion-de-colas"],"_links":{"self":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/comments?post=1453"}],"version-history":[{"count":17,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1453\/revisions"}],"predecessor-version":[{"id":1483,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/posts\/1453\/revisions\/1483"}],"wp:attachment":[{"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/media?parent=1453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/categories?post=1453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/naps.com.mx\/blog\/wp-json\/wp\/v2\/tags?post=1453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}