<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Investigación de Operaciones archivos &#187; Ingenieria Industrial Online</title>
	<atom:link href="https://ingenieriaindustrialonline.com/tag/investigacion-de-operaciones-2/feed/" rel="self" type="application/rss+xml" />
	<link>https://ingenieriaindustrialonline.com/tag/investigacion-de-operaciones-2/</link>
	<description>ingenieriaindustriaonline.com</description>
	<lastBuildDate>Thu, 09 Feb 2023 15:12:10 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>

<image>
	<url>https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/cropped-faVicon-32x32.png</url>
	<title>Investigación de Operaciones archivos &#187; Ingenieria Industrial Online</title>
	<link>https://ingenieriaindustrialonline.com/tag/investigacion-de-operaciones-2/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>¿Una hora perdida en el cuello de botella es una hora perdida en el sistema?</title>
		<link>https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/una-hora-perdida-en-el-cuello-de-botella-es-una-hora-perdida-en-el-sistema/</link>
					<comments>https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/una-hora-perdida-en-el-cuello-de-botella-es-una-hora-perdida-en-el-sistema/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Fri, 27 Jan 2023 23:30:34 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Teoría de restricciones (TOC)]]></category>
		<category><![CDATA[Eliyahu Goldratt]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Restricciones]]></category>
		<category><![CDATA[TOC]]></category>
		<guid isPermaLink="false">https://www.ingenieriaindustrialonline.com/?p=33065</guid>

					<description><![CDATA[<p>Hace poco leí una publicación sobre una conversación con Javier Arévalo, experto en Teoría de Restricciones. Durante esta conversación, Javier mencionó algo que me llamó la atención: “¡Nada es más práctico que una buena teoría y una buena teoría es un conjunto de relaciones de causa y efecto que explican el funcionamiento y describen las &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/una-hora-perdida-en-el-cuello-de-botella-es-una-hora-perdida-en-el-sistema/">¿Una hora perdida en el cuello de botella es una hora perdida en el sistema?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Hace poco leí una publicación sobre una conversación con Javier Arévalo, experto en <a href="https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/que-es-teoria-de-restricciones-toc/"><strong>Teoría de Restricciones</strong></a>. Durante esta conversación, Javier mencionó algo que me llamó la atención: <em><strong>“¡Nada es más práctico que una buena teoría y una buena teoría es un conjunto de relaciones de causa y efecto que explican el funcionamiento y describen las decisiones que deben tomarse a medida que la realidad cambia para tener el máximo éxito!”</strong></em>.</p>
<p>Aunque la publicación me llamó la atención, con el tiempo se me fue olvidando, hasta que en una clase de Ingeniería Industrial formulé una pregunta que consideré diagnóstica para la introducción del curso de Investigación de Operaciones:</p>
<blockquote class=" quote-simple "><p>«Considere las siguientes operaciones interdependientes y sin variabilidad (una línea de ensamble). En color rojo, debajo de cada operación se encuentran los tiempos por unidad. Si se producen 100 unidades desde 0, ¿En cuántos minutos terminaría la producción?”</p></blockquote>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/interdependencia.png" alt="interdependencia" width="540" height="165" class="size-full wp-image-33066 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/interdependencia.png 540w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/interdependencia-300x92.png 300w" sizes="(max-width: 540px) 100vw, 540px" /></p>
<p>La pregunta plantea un problema de producción «<a href="https://ingenieriaindustrialonline.com/produccion/sistema-de-produccion-flow-shop/"><strong>flow shop</strong></a>«, en el que sus tareas pasan a través de todos sus procesos en el mismo orden, en el que varias operaciones pueden ocurrir simultáneamente en diferentes máquinas, pero con secuencialidad. La interdependencia indica, al menos, lo siguiente: la operación D no puede comenzar antes de que C lo proporcione, y este a su vez no puede comenzar antes de que B lo proporcione, y este a su vez no puede comenzar antes de que A lo proporcione.</p>
<p>Me sorprendió no obtener respuestas acertadas cuando pregunté a los estudiantes sobre esto, basándome en los principios del flujo creí que la respuesta estaba a la vista. Por lo tanto, me dirigí a la herramienta de moda por estos días, la herramienta de inteligencia artificial optimizada para el diálogo, llamada <em><strong>ChatGPT</strong></em>, para obtener una respuesta. Esta herramienta, que condensa el conocimiento humano disponible en digital hasta el 2021, o al menos ha sido entrenada con gran parte de él, me proporcionó múltiples respuestas incorrectas (entre ellas caracterizó el sistema como «<a href="https://ingenieriaindustrialonline.com/produccion/sistema-de-produccion-job-shop/"><strong>job shop</strong></a>«) y solo después de interactuar un buen tiempo, logró llegar al resultado acertado.</p>

		<div class="post-content-slideshow-outer">
			<div class="post-content-slideshow">

			<div class="loader-overlay"><div class="spinner-circle"></div></div>

				<div class="tie-slick-slider">

			<div class="slide post-content-slide">
				 Interacción Inicial | <img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow.png" alt="ChatGPT, restricciones" width="653" height="442" class="alignnone wp-image-33068 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow.png 653w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow-300x203.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow-220x150.png 220w" sizes="(max-width: 653px) 100vw, 653px" /> 
			</div><!-- post-content-slide -->
		

			<div class="slide post-content-slide">
				 Retroalimentación | <img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow2.png" alt="ChatGPT" width="653" height="442" class="alignnone size-full wp-image-33069" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow2.png 653w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow2-300x203.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/flow2-220x150.png 220w" sizes="(max-width: 653px) 100vw, 653px" /> 
			</div><!-- post-content-slide -->
		


					<div class="slider-nav-wrapper">
						<ul class="tie-slider-nav"></ul>
					</div>
				</div><!-- tie-slick-slider -->
			</div><!-- post-content-slideshow -->
		</div><!-- post-content-slideshow-outer -->
	
<p>&nbsp;</p>
<p>Para aclarar mis dudas, decidí consultar con varios profesionales egresados de programas de Ingeniería Industrial en Latinoamérica, algunos con experiencia en la gestión de operaciones secuenciales. Sin embargo, las respuestas que obtuve no fueron satisfactorias.</p>
<p>De repente recordé aquel post de Javier Arévalo y pensé en la pertinencia de sus palabras: ¡Nada es más práctico que una buena teoría! Esto me llevó a reflexionar sobre los principios y teorías que siguen los profesionales de Ingeniería Industrial, y cómo toman decisiones.</p>
<p>Como docente, consideré la responsabilidad de explicar el resultado del problema planteado, lo hice acudiendo a una sabia frase de Eliyahu Goldratt:</p>
<blockquote class=" quote-simple "><p>“Una hora perdida en la restricción es una hora perdida en todo el sistema, y una hora ganada en cualquier otro recurso es un espejismo”. <cite>Eliyahu Goldratt</cite></p></blockquote>
<p>Si partimos de este principio, la respuesta del problema es clara: 405 minutos.</p>
<h2>¿Creer o reventar? No, no es un acto de fe</h2>
<p>La Investigación de Operaciones es una disciplina que a menudo se asocia con el modelamiento matemático, las ecuaciones y los algoritmos. Por lo menos así la recuerdan los estudiantes; pero en realidad se trata de un enfoque sistémico que se centra en la comprensión de las restricciones que impiden alcanzar los objetivos de un sistema. También se enfatiza el contraste de paradigmas, como el mundo de los costos y los óptimos locales, los principios del flujo, y el papel del gestor de operaciones en la toma de decisiones. Todo esto abordado desde las herramientas de análisis cuantitativo, o como diría Javier: ¡Una buena teoría!</p>
<p>En clase, utilizamos herramientas cuantitativas y un enfoque teórico para demostrar cómo se puede llegar a la solución de un problema, utilizando un diagrama de Gantt en Excel. En resumen, 405 minutos es el resultado.</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/gantt.png" alt="Gantt, restricciones" width="616" height="348" class="aligncenter wp-image-33070 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/gantt.png 616w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/gantt-300x169.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/gantt-390x220.png 390w" sizes="(max-width: 616px) 100vw, 616px" /></p>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 50%;">Primera unidad</td>
<td style="width: 50%;">1 unidad * (1 m/unidad + 2 m/unidad + 4 m/unidad + 2 m/unidad)</td>
</tr>
<tr>
<td style="width: 50%;">Unidades restantes</td>
<td style="width: 50%;">99 unidades * (4 m/unidad)</td>
</tr>
<tr>
<td style="width: 50%;">Tiempo total</td>
<td style="width: 50%;">9 minutos + 396 minutos</td>
</tr>
<tr>
<td style="width: 50%;">Tiempo total (100 unidades)</td>
<td style="width: 50%;">405 minutos</td>
</tr>
</tbody>
</table>
<p>El tiempo de procesamiento de las unidades, excepto la primera, está determinado por la operación que tenga el mayor tiempo de procesamiento por unidad, conocido como la restricción activa. Este principio es esencial para comprender el flujo y las variables que lo controlan. Con el objetivo de ilustrar este principio, utilicé la simulación determinística en Bizagi. Como resultado, se demostró que el tiempo total para completar las 100 unidades fue de 6 horas y 45 minutos, es decir, 405 minutos.</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi1.png" alt="bizagi, restricciones" width="602" height="453" class="aligncenter wp-image-33072 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi1.png 602w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi1-300x226.png 300w" sizes="(max-width: 602px) 100vw, 602px" />Además, para seguir explorando este tema, también se aumentó el tiempo de procesamiento de la operación restricción (C), pasando de 4 minutos por unidad a 5 minutos por unidad; con esto quise abordar mediante simulación un aspecto de la frase de Goldratt: <strong>«Una hora perdida en la restricción es una hora perdida en el sistema»</strong>.</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi2.png" alt="" width="600" height="450" class="size-full wp-image-33073 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi2.png 600w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi2-300x225.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>Desde luego, de acuerdo con los principios que rigen el flujo y las restricciones del proceso, ese minuto adicional sobre la restricción activa tuvo un impacto en el desempeño general del sistema.</p>
<p>Ahora evaluemos el postulado restante de Goldratt: <strong>«Una hora ganada en cualquier otro recurso es un espejismo»</strong>. Para eso mejoramos la «eficiencia» local de una operación cualquiera (no una restricción activa), por ejemplo, la operación B. Una mejora local puede ser pasar de un tiempo de procesamiento de 2 m/unidad a 1 m/unidad. Veamos los resultados de la simulación:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi3.png" alt="bizagi3" width="600" height="450" class="size-full wp-image-33074 aligncenter" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi3.png 600w, https://ingenieriaindustrialonline.com/wp-content/uploads/2023/01/bizagi3-300x225.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p><em><strong>¡Un espejismo!</strong></em></p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<p>En palabras de Javier, <em>«la práctica debe guiarse de una buena teoría».</em> En el caso de los ingenieros industriales y/o gestores de operaciones, el ideal es el de fortalecer los bases que le permitan entender las restricciones, los principios del flujo y el óptimo global. No es un asunto exclusivo de un cuerpo de conocimiento, como lo es la Teoría de las Restricciones de Goldratt, también <a href="https://ingenieriaindustrialonline.com/columnas/lecciones-de-gerencia-de-taiichi-ohno-parte-vii/"><strong>Ohno destacaba que el componente transversal de los procesos lo tenían quienes tenían la visión del flujo</strong></a>.</p>
<p>Y para todos aquellos que se debaten entre la teoría y la práctica, Javier nos deja un mensaje claro: <em>«Si no tienes una buena teoría, mi mejor deseo para ti es: ¡Buena suerte!»</em>.</p>
<blockquote class=" quote-simple "><p>«Si no tienes una buena teoría, mi mejor deseo para ti es: ¡Buena suerte!» <cite>Javier Arévalo</cite></p></blockquote>
<p>&nbsp;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/una-hora-perdida-en-el-cuello-de-botella-es-una-hora-perdida-en-el-sistema/">¿Una hora perdida en el cuello de botella es una hora perdida en el sistema?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/teoria-de-restricciones-toc/una-hora-perdida-en-el-cuello-de-botella-es-una-hora-perdida-en-el-sistema/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Casos especiales de programación lineal (Método Gráfico)</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/casos-especiales-de-programacion-lineal-metodo-grafico/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/casos-especiales-de-programacion-lineal-metodo-grafico/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Thu, 08 Dec 2022 19:53:51 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Análisis de sensibilidad]]></category>
		<category><![CDATA[casos especiales]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[modelamiento de problemas]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[restricciones inactivas]]></category>
		<category><![CDATA[solución infactible]]></category>
		<category><![CDATA[solución óptima acotada]]></category>
		<category><![CDATA[solución óptima no acotada]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=32764</guid>

					<description><![CDATA[<p>Para empezar, es importante recordar que en un modelo de programación lineal, se busca encontrar la solución óptima que maximiza o minimiza una función objetivo, sujeta a un conjunto de restricciones. En el método gráfico, se representa gráficamente la función objetivo y las restricciones en un plano cartesiano, formando una región factible que contiene todas &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/casos-especiales-de-programacion-lineal-metodo-grafico/">Casos especiales de programación lineal (Método Gráfico)</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Para empezar, es importante recordar que en un modelo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal/"><strong>programación lineal</strong></a>, se busca encontrar la solución óptima que maximiza o minimiza una función objetivo, sujeta a un conjunto de restricciones. En el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico/"><strong>método gráfico</strong></a>, se representa gráficamente la función objetivo y las restricciones en un plano cartesiano, formando una región factible que contiene todas las soluciones posibles. Sin embargo, en algunos casos, la solución óptima no siempre se encuentra en uno de los vértices de la región factible. Estos son los denominados casos especiales de programación lineal.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Recordemos que en el área de solución se encuentran todas las posibles soluciones que satisfacen el conjunto de restricciones, lo que se denomina factibilidad. La optimalidad es diferente, ya que dentro de las soluciones factibles, una o algunas de ellas serán las que minimicen o maximicen la función objetivo. Esto es fundamental, tanto en la enseñanza de la programación lineal como en el aprendizaje.
			</div>
		</div>
	

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<h2>
		<div id="casos-especiales" data-title="Casos especiales" class="index-title"></div>
	Casos especiales</h2>
<h3>
		<div id="soluciones-optimas-multiples" data-title="Soluciones óptimas múltiples" class="index-title"></div>
	Soluciones óptimas múltiples</h3>
<p>En ocasiones, pueden existir múltiples soluciones que maximizan o minimizan la función objetivo de un modelo de programación lineal, es decir, múltiples soluciones óptimas. La elección de la solución en un contexto de aplicación práctico dependerá tanto de la sensibilidad de las restricciones activas como de los factores propios del sistema que no se consideran en un modelo matemático.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>En el caso en el que los vectores solución sean consecutivos, esta cantidad de soluciones óptimas puede tender a infinito.
			</div>
		</div>
	
<p>Por ejemplo, supongamos que tenemos el siguiente modelo de programación lineal:</p>
<blockquote class=" quote-simple "><p>La ebanistería «López S.A.S» ha recibido una gran cantidad de partes prefabricadas para la elaboración de mesas, pero no ha podido iniciar un plan de producción enfocado en ellas debido a la alta demanda de sus otros productos. Las mesas que se pueden elaborar con las partes prefabricadas son de dos modelos: A y B. Estas mesas solo requieren ser ensambladas y pintadas.</p>
<p>Esta semana se ha determinado dedicar 10 horas a ensamblar y 8 horas a pintar para elaborar la mayor cantidad de mesas posibles, teniendo en cuenta que cada mesa modelo A requiere 2 horas de ensamble y 1 hora de pintura, mientras que cada mesa modelo B requiere 1 hora de ensamble y 2 horas de pintura. Si el margen de utilidad es de $20000 por cada mesa modelo A y $10000 por cada mesa modelo B, determine el modelo de producción adecuado para esta semana.</p></blockquote>
<p><em><strong>Variables</strong></em></p>
<p>x = Mesas modelo A a fabricar esta semana</p>
<p>y = Mesas modelo B a fabricar esta semana</p>
<p><em><strong>Restricciones</strong></em></p>
<p>2x + y &lt;= 10        «Horas de ensamble»</p>
<p>x + 2y &lt;= 8          «Horas de pintura»</p>
<p>x &gt;= 0                  «No negatividad»</p>
<p>y &gt;= 0                  «No negatividad»</p>
<p><em><strong>Función objetivo</strong></em></p>
<p>Zmax = 20000x + 10000y</p>
<p><em><strong>Solución gráfica:</strong></em></p>
<p><iframe loading="lazy" width="800" height="600" style="border: 1px solid #e4e4e4; border-radius: 4px;" src="https://www.geogebra.org/graphing/b9squs3s?embed" allowfullscreen="allowfullscreen" frameborder="0"></iframe></p>
<p>Como se puede observar, la región factible tiene dos vértices que maximizan la función Z: <strong>C</strong>(4,2) y <strong>D</strong>(6,0). Ambas soluciones son óptimas, ya que maximizan la función Z dentro de las restricciones del modelo. En este caso, la elección de la solución dependerá de factores propios del sistema que no se consideran en el modelo matemático, como por ejemplo la disponibilidad de recursos o el costo de producción; del mismo modo, sería conveniente evaluar la sensibilidad de las restricciones activas en cada vértice.</p>
<table style="border-collapse: collapse; width: 100%; height: 111px;">
<tbody>
<tr style="height: 37px;">
<td style="width: 33.3333%; height: 37px; background-color: #487acf;"><span style="color: #ffffff;"><strong>Solución óptimas</strong></span></td>
<td style="width: 21.0833%; text-align: center; height: 37px; background-color: #487acf;"><span style="color: #ffffff;">20000(<strong>x</strong>)</span></td>
<td style="width: 23.9167%; text-align: center; height: 37px; background-color: #487acf;"><span style="color: #ffffff;">10000(<strong>y</strong>)</span></td>
<td style="width: 21.6667%; text-align: center; height: 37px; background-color: #487acf;"><span style="color: #ffffff;"><strong>Z</strong></span></td>
</tr>
<tr style="height: 37px;">
<td style="width: 33.3333%; height: 37px;">Vértice C</td>
<td style="width: 21.0833%; text-align: center; height: 37px;">20000(<strong>4</strong>)</td>
<td style="width: 23.9167%; text-align: center; height: 37px;">10000(<strong>2</strong>)</td>
<td style="width: 21.6667%; height: 37px; text-align: center;">100000</td>
</tr>
<tr style="height: 37px;">
<td style="width: 33.3333%; height: 37px;">Vértice D</td>
<td style="width: 21.0833%; text-align: center; height: 37px;">20000(<strong>5</strong>)</td>
<td style="width: 23.9167%; text-align: center; height: 37px;">10000(<strong>0</strong>)</td>
<td style="width: 21.6667%; height: 37px; text-align: center;">100000</td>
</tr>
</tbody>
</table>
<p>En este caso se da una condición particular, ya que los vértices son consecutivos. Esto implica que cada punto en el tramo que une a C y a D son soluciones óptimas.</p>
<p>Con la ayuda de GeoGebra, se puede mover la barra de desplazamiento que representa la utilidad y evaluar que la función objetivo alcanza el máximo de 100000 tanto en C como en D, así como en cualquier punto del tramo CD. Esto demuestra que en este caso existen múltiples soluciones óptimas y que cualquier punto en el tramo CD es una solución válida.</p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-dashed">
	
<h3>
		<div id="solucion-optima-no-acotada" data-title="Solución óptima no acotada" class="index-title"></div>
	Solución óptima no acotada</h3>
<p>Otra de las variantes que presentan los modelos de programación lineal es la solución óptima no acotada, es decir, problemas con infinitas soluciones óptimas. Dada la naturaleza finita de las restricciones en los contextos reales, estos problemas suelen deberse a un mal planteamiento de las restricciones, pero en el mundo académico es común evaluar este tipo de problemas.</p>
<p>Por ejemplo, supongamos que tenemos el siguiente modelo de programación lineal:</p>
<blockquote class=" quote-simple "><p>La compañía comercializadora de bebidas energéticas «WILD» está promocionando dos nuevas bebidas: la tipo A y la tipo B. Dado que se encuentran en promoción, se puede asegurar el cubrimiento de cualquier cantidad de demanda. Sin embargo, la empresa debe tener en cuenta dos políticas:</p>
<p>1. La cantidad de bebidas tipo A que se vendan no puede ser menor que la de tipo B.</p>
<p>2. Se deben vender por lo menos 1500 bebidas de cualquier tipo.</p>
<p>Estas políticas pueden ser representadas mediante restricciones en un modelo de programación lineal, que nos permitirá determinar la cantidad óptima de bebidas tipo A y B a vender en promoción.</p>
<p>El precio de venta de ambas bebidas es de $1800 por unidad</p></blockquote>

		<div class="box warning  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Antes de pasar al modelo matemático, es importante evaluar críticamente el problema planteado. El primer supuesto considera la capacidad de atender cualquier demanda, es decir, una demanda infinita. En la realidad, esto implicaría la existencia de recursos de producción infinitos, lo cual es un supuesto poco realista. De hecho, es una apelación al absurdo que se utiliza para definir la gestión de las restricciones en la programación lineal.</p>
<p>Es importante tener en cuenta estos supuestos y ajustar el modelo matemático en consecuencia, para obtener soluciones realistas y viables en un contexto de aplicación práctico.
			</div>
		</div>
	
<p><em><strong>Variables</strong></em></p>
<p>x = Bebidas tipo A a vender en promoción</p>
<p>y = Bebidas tipo B a vender en promoción</p>
<p><em><strong>Restricciones</strong></em></p>
<p>x &gt;= y                     «Políticas de ventas»</p>
<p>x + y &gt;= 1500          «Horas de pintura»</p>
<p>x &gt;= 0                      «No negatividad»</p>
<p>y &gt;= 0                      «No negatividad»</p>
<p><em><strong>Función objetivo</strong></em></p>
<p>Zmax = 1800x + 1800y</p>
<p><em><strong>Solución gráfica:</strong></em></p>
<p><iframe loading="lazy" width="800" height="600" style="border: 1px solid #e4e4e4; border-radius: 4px;" src="https://www.geogebra.org/graphing/r2rkwssf?embed" allowfullscreen="allowfullscreen" frameborder="0"></iframe></p>
<p>Como se puede observar, la región factible no está acotada, lo que significa que el conjunto de posibles soluciones factibles es infinito. En consecuencia, la solución óptima tiende a infinito, lo que hace que el modelo no tenga solución.</p>
<p>En este caso, el problema planteado tiene un mal planteamiento de las restricciones, lo que ha llevado a una solución no acotada. Para solucionar este problema, es necesario ajustar las restricciones del modelo para que la región factible sea acotada y tenga una solución óptima finita.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>En mi experiencia docente, este es uno de los casos más frecuentes que se dan en el modelamiento por parte de los estudiantes. Es importante que los estudiantes desarrollen habilidades para comprender si las restricciones presentes acotan suficientemente el modelo o no. Esto es fundamental para obtener soluciones realistas y viables en un contexto de aplicación práctico. 
			</div>
		</div>
	

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-dashed">
	
<h3>
		<div id="solucion-inviable-o-infactible" data-title="Solución inviable o infactible" class="index-title"></div>
	Solución inviable o infactible</h3>
<p>La solución infactible es un caso común en la programación lineal, y corresponde a aquellos casos en los que no existen soluciones que cumplan con todas las restricciones del modelo. Las restricciones acotan el sistema y establecen límites para alcanzar el objetivo del modelo. Cuando estas restricciones no se pueden cumplir en su totalidad, la solución es infactible.</p>
<p>En contexto de aplicación práctica, este caso es más común de lo que parece, y se da en escenarios en los que se plantean proporciones de oferta y demanda inviables en el modelo.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span><span>En mi experiencia, el síndrome del trabajador quemado es una de las principales consecuencias de una mala gestión de recursos en un plan de trabajo (un típico escenario de soluciones no acotadas en un contexto real). Cuando un trabajador se encuentra a cargo de cumplir con un plan que no cuenta con los recursos necesarios, se ve obligado a realizar un esfuerzo excesivo para lograr cumplir con las metas establecidas, lo que puede llevar a un agotamiento laboral y una disminución en su rendimiento y calidad de vida.</span>
			</div>
		</div>
	
<p>Por ejemplo, supongamos que tenemos el siguiente modelo de programación lineal:</p>
<blockquote class=" quote-simple "><p>La compañía de galletas «CAROLA» desea planificar la producción de galletas que deberá entregar a su cliente en dos semanas. Según el contrato, la empresa se compromete a entregar al menos 300 cajas de galletas, cualquiera sea su tipo (presentación D, presentación N o una combinación de ambas).</p>
<p>La producción de cada caja de galletas presentación D requiere 2 horas de elaboración y 3 horas de horneado, mientras que la producción de cada caja de presentación N requiere 3 horas de elaboración y 1 hora de horneado. La empresa cuenta con 550 horas para elaboración y 480 horas de horneado en las próximas dos semanas.</p>
<p>El margen de utilidad de cada caja de galletas presentación D es de $8500, y el margen de utilidad de cada caja de presentación N es de $8100. Para maximizar las utilidades, la empresa debe utilizar un modelo de programación lineal que le permita determinar el plan de producción adecuado.</p></blockquote>
<p><em><strong>Variables</strong></em></p>
<p>x = Cajas de galletas de presentación D a producir en dos semanas</p>
<p>y = Cajas de galletas de presentación N a producir en dos semanas</p>
<p><em><strong>Restricciones</strong></em></p>
<p>2x + 3y &lt;= 550               «Horas de elaboración»</p>
<p>3x + y &lt;= 480                 «Horas de horneado»</p>
<p>x + y &gt;= 300                   «Demanda según el contrato»</p>
<p>x &gt;= 0                             «No negatividad»</p>
<p>y &gt;= 0                             «No negatividad»</p>
<p><em><strong>Función objetivo</strong></em></p>
<p>Zmax = 8500x + 8100y</p>
<p><em><strong>Solución gráfica:</strong></em></p>
<p><iframe loading="lazy" width="800" height="600" style="border: 1px solid #e4e4e4; border-radius: 4px;" src="https://www.geogebra.org/graphing/bk7tfxmp?embed" allowfullscreen="allowfullscreen" frameborder="0"></iframe></p>
<p>La gráfica muestra el área sombreada que se delimita a partir de la representación de las restricciones que corresponden a los recursos de producción. Se puede apreciar cómo no existe una intersección entre las restricciones de capacidad y la restricción de demanda (sombreada de azul), lo que indica que no existen soluciones que cumplan con todas las restricciones del modelo. En este caso, se trata de una solución infactible.</p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-dashed">
	
<h3>
		<div id="restricciones-redundantes" data-title="Restricciones redundantes" class="index-title"></div>
	Restricciones redundantes</h3>
<p>Existen en los modelos de programación lineal un tipo de restricciones que no tienen ningún efecto en la determinación del conjunto solución (ni en la solución óptima). Estas restricciones se conocen como redundantes, ya que no afectan la solución del modelo.</p>
<p>En algunos casos, las restricciones redundantes pueden ser útiles para el análisis de sensibilidad, ya que nos permiten evaluar cómo cambia la solución óptima ante cambios en los valores de las restricciones. En lugar de eliminar estas restricciones, se pueden dejar en el modelo y marcar como inactivas. De esta manera, no afectan la solución óptima del modelo, pero se pueden utilizar para el análisis de sensibilidad.</p>

		<div class="box error  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span><span>Es importante tener en cuenta que, aunque algunos autores recomiendan eliminar las restricciones redundantes para mejorar la precisión del modelo, esta práctica no siempre es recomendable, de hecho, definitivamente desaconsejo esta práctica. </span></p>
<p><span>En contextos de aplicación real, el profesional debe gestionar las restricciones del sistema, lo que implica que la restricción activa puede cambiar con el tiempo y las acciones tomadas (la restricción de un sistema se mueve, las restricciones se activan ante cambios en los parámetros). Por lo tanto, es importante considerar todas las restricciones del modelo y utilizarlas en el análisis de sensibilidad. </span>
			</div>
		</div>
	
<p>Por ejemplo, supongamos que tenemos el siguiente modelo de programación lineal:</p>
<blockquote class=" quote-simple "><p>La compañía «CONGELADORES MAJO» se encuentra en la necesidad de planificar su producción semanal de congeladores tipo A y B. Cada uno de ellos requiere pasar por tres operaciones: Ensamblaje, pintura y control de calidad. Los congeladores tipo A requieren 2 horas de ensamblaje, 3 kg de pintura y 4 horas de control de calidad; los congeladores tipo B requieren 3 horas de ensamblaje, 6 kg de pintura y 5 horas de control de calidad. La compañía cuenta con un margen contributivo de $102000 y $98000 por cada congelador tipo A y B respectivamente. La disponibilidad semanal de recursos se encuentra limitada a 300 horas de ensamblaje, 840 kg de pintura y 450 horas de control de calidad. A partir de esta información, se debe determinar la cantidad de unidades a producir semanalmente de cada referencia, con el fin de maximizar las utilidades.</p></blockquote>
<p><em><strong>Variables</strong></em></p>
<p>x = Congeladores tipo A a producir semanalmente</p>
<p>y = Congeladores tipo B a producir semanalmente</p>
<p><em><strong>Restricciones</strong></em></p>
<p>2x + 3y &lt;= 300               «Horas de ensamblaje»</p>
<p>3x + 5y &lt;= 840                «Kg de pintura»</p>
<p>4x + 5y &lt;= 450                «Horas de control de calidad»</p>
<p>x &gt;= 0                             «No negatividad»</p>
<p>y &gt;= 0                             «No negatividad»</p>
<p><em><strong>Función objetivo</strong></em></p>
<p>Zmax = 102000x + 98000y</p>
<p><em><strong>Solución gráfica:</strong></em></p>
<p><iframe loading="lazy" width="800" height="600" style="border: 1px solid #e4e4e4; border-radius: 4px;" src="https://www.geogebra.org/graphing/guesnehs?embed" allowfullscreen="allowfullscreen" frameborder="0"></iframe></p>
<p>La gráfica muestra cómo las restricciones 1 y 2 (Horas de ensamblaje y Kg de pintura) no determinan el conjunto solución. La solución se encuentra en el vértice C(112.5, 0), con una utilidad de $11475000.</p>
<p>Sin embargo, denominar las restricciones que no determinan el área de factibilidad ni el punto óptimo, como redundantes o sobrantes (o excluirlas del modelo), quizá no sea la manera correcta; preferiblemente denominar estas restricciones como inactivas. Supongamos que nos encontramos en un escenario en el cual evaluamos la posibilidad de aumentar la capacidad de la restricción 3 (horas de control de calidad); es fundamental reconocer los límites en los cuales dicho incremento de capacidad activaría las restricciones 1 y 2.</p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<p>En resumen, los casos especiales en programación lineal pueden darse por una mala formulación de las restricciones, la presencia de soluciones infactibles o infinitas. Es importante considerar siempre todas las restricciones del modelo y utilizar el análisis de sensibilidad para evaluar su efecto en la solución óptima.</p>
<p><em><strong>¡Si te ha gustado este artículo sobre casos especiales en programación lineal, ¡compartelo con tus contactos!</strong></em></p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Recomendamos que lean: <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/"><strong>Método gráfico mediante Python</strong></a>
			</div>
		</div>
	
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/casos-especiales-de-programacion-lineal-metodo-grafico/">Casos especiales de programación lineal (Método Gráfico)</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/casos-especiales-de-programacion-lineal-metodo-grafico/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Análisis de sensibilidad gráfica mediante el uso de Python (Caso 1)</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Sun, 18 Jul 2021 18:57:10 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Análisis de sensibilidad]]></category>
		<category><![CDATA[Intervalos de factibilidad]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Método gráfico]]></category>
		<category><![CDATA[Optimización]]></category>
		<category><![CDATA[Precio dual]]></category>
		<category><![CDATA[Precio sombra]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Solución gráfica]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=26239</guid>

					<description><![CDATA[<p>¿Qué es el análisis de sensibilidad en programación lineal? Quienes se adentren en los conceptos de la investigación de operaciones, propiamente en los conceptos de la programación lineal, deben considerar que existe algo más allá de la solución óptima. En investigación de operaciones, la solución de un modelo matemático establece una base para la toma &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/">Análisis de sensibilidad gráfica mediante el uso de Python (Caso 1)</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>¿Qué es el análisis de sensibilidad en programación lineal?</h2>

		<div id="que-es-el-analisis-de-sensibilidad-en-programacion-lineal" data-title="¿Qué es el análisis de sensibilidad en programación lineal?" class="index-title"></div>
	
<p>Quienes se adentren en los conceptos de la investigación de operaciones, propiamente en los conceptos de la <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal/"><em><strong>programación lineal</strong></em></a>, deben considerar que existe algo más allá de la solución óptima. En investigación de operaciones, la solución de un modelo matemático establece una base para la toma de decisiones; sin embargo, puede considerarse como esencial el <strong>análisis de los resultados obtenidos</strong>.</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/modelamiento.png" alt="modelamiento" width="565" height="61" class="aligncenter wp-image-26253 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/modelamiento.png 565w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/modelamiento-300x32.png 300w" sizes="(max-width: 565px) 100vw, 565px" /></p>
<p>Los resultados de un modelo de programación lineal pueden ofrecer mucha información adicional, <em>inputs </em>del análisis de resultados. Existen a grandes rasgos, dos tipos de análisis complementarios en PL:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><strong>Análisis de sensibilidad</strong>: El cual determina las condiciones que mantendrán la solución actual sin cambios.</li>
<li><strong>Análisis postóptimo</strong>: El cual determina la nueva solución óptima cuando cambian los datos del modelo</li>
</ul>

		</div>
	
<p>En este artículo abordaremos el <strong>análisis de sensibilidad</strong>.</p>
<p>El análisis de sensibilidad en programación lineal corresponde al examen detallado de los límites dentro de los cuales los parámetros del modelo (recursos, utilidad o costo), pueden cambiar sin que esto afecte a la solución óptima y a la capacidad de calcular el impacto que tienen dichos cambios sobre la misma. Es decir, a partir de los resultados obtenidos de un modelo, podemos establecer una solución óptima y tenemos un rango en el cual podemos medir el impacto de los cambios en la disponibilidad de los recursos y los cambios en los coeficientes de la función objetivo. El análisis de dichas variaciones y el cálculo de los límites dentro de los cuales estas son válidas, es lo que se conoce como análisis de sensibilidad. Todo el análisis que se encuentre fuera de ese rango requeriría recalcular el modelo.</p>
<p>Como tal, constituye una herramienta importante en el análisis de los resultados obtenidos, sobre todo, en dos casos en particular:</p>
<ol>
<li><strong>La sensibilidad de la solución óptima ante los cambios de la disponibilidad de los recursos (lado derecho de las restricciones)</strong>. Es decir, cuáles serían los límites dentro de los cuáles los resultados del modelo me permiten calcular el impacto que tiene sobre la solución óptima, el aumento o la disminución de la disponibilidad de un recurso.</li>
<li><strong>La sensibilidad de la solución óptima ante los cambios en la utilidad unitaria o el costo unitario (coeficientes de las variables de la función objetivo)</strong>. Es decir, cuáles serían los límites dentro de los cuáles los resultados del modelo me permiten calcular el impacto que tiene sobre la solución óptima, el aumento o la disminución de la utilidad unitaria o el costo unitario asociada a las variables que forman parte de la función objetivo.</li>
</ol>
<h3>Análisis de sensibilidad gráfica, caso 1: Cambios en el lado derecho (Disponibilidad de recursos)</h3>

		<div id="analisis-de-sensibilidad-grafica-caso-1-cambios-en-el-lado-derecho" data-title="Análisis de sensibilidad gráfica, caso 1: Cambios en el lado derecho" class="index-title"></div>
	
<p>Con el propósito de evaluar los resultados obtenidos a través del tratamiento de un problema técnicamente formulado y abordado, utilizaremos un caso descrito en el libro Investigación de Operaciones (9na edición), de Hamdy A. Taha (University of Arkansas, Fayetteville), (Ejemplo 3.6-1):</p>
<blockquote class=" quote-simple "><p>JOBCO fabrica dos productos en dos máquinas. Una unidad del producto 1 requiere 2 horas en la máquina 1, y 1 hora en la máquina 2. Una unidad del producto 2 requiere 1 hora en la máquina 1, y 3 horas en la máquina 2. Los ingresos por unidad de los productos 1 y 2 son de $30 y $20, respectivamente. El tiempo de procesamiento diario total disponible en cada máquina es de 8 horas.</p></blockquote>
<p>Si <em><strong>x1</strong></em> y <em><strong>x2</strong></em> son las cantidades diarias de productos 1 y 2, respectivamente, el modelo se da como:</p>
<p style="text-align: center;"><strong>Zmax</strong> = 30<strong>x1</strong> + 20<strong>x2</strong></p>
<p>Sujeto a:</p>
<p style="text-align: center;">2<strong>x1</strong> + <strong>x2</strong> &lt;= 8 (Máquina 1)</p>
<p style="text-align: center;"><strong>x1</strong> + 3<strong>x2</strong> &lt;= 8 (Máquina 2)</p>
<p style="text-align: center;"><strong>x1</strong>, <strong>x2</strong> &gt;= 0 (No negatividad)</p>
<p>La forma en la que abordamos la <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/"><strong>solución gráfica de un modelo de programación lineal mediante <em>Python</em></strong></a>, está ampliamente documentada. El código en <em>Python </em>que resuelve mediante método gráfico el problema anterior, lo presentamos a continuación:</p>
<hr />
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Caso JOBCO: Investigación de Operaciones (9na edición), de Hamdy A. Taha 
# (University of Arkansas, Fayetteville), (Ejemplo 3.6-1)
# Autor del código en Python: Bryan Salazar López, Ing. M.Sc. (2021)

#Librerías necesarias
import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import LineString

#Ecuaciones e intervalos (Para tabular)
x = np.arange(-20, 20, 5)
y = np.arange(-20, 20, 5)
y1 = 8 - (2 * x)
y2 = (8 - x) / 3
y3 = 0 * x
x1 = 0 * y
z = (-30 * x) / 20

#Identificadores para las líneas
primera_linea = LineString(np.column_stack((x, y1)))
segunda_linea = LineString(np.column_stack((x, y2)))
tercera_linea = LineString(np.column_stack((x, y3)))
cuarta_linea = LineString(np.column_stack((x1, y)))
quinta_linea = LineString(np.column_stack((x, z)))

#Graficando las líneas
plt.plot(x, y1, '-', linewidth=2, color='b')
plt.plot(x, y2, '-', linewidth=2, color='g')
plt.plot(x, y3, '-', linewidth=2, color='r')
plt.plot(x1, y, '-', linewidth=2, color='y')
plt.plot(x, z, ':', linewidth=1, color='k')


#Generando las intersecciones (vértices)
primera_interseccion = cuarta_linea.intersection(segunda_linea)
segunda_interseccion = primera_linea.intersection(segunda_linea)
tercera_interseccion = primera_linea.intersection(tercera_linea)
cuarta_interseccion = tercera_linea.intersection(cuarta_linea)
quinta_interseccion = cuarta_linea.intersection(primera_linea)
sexta_interseccion = segunda_linea.intersection(tercera_linea)

#Graficando los vértices
plt.plot(*primera_interseccion.xy, 'o', color='k')
plt.plot(*segunda_interseccion.xy, 'o', color='k')
plt.plot(*tercera_interseccion.xy, 'o', color='k')
plt.plot(*cuarta_interseccion.xy, 'o', color='k')
plt.plot(*quinta_interseccion.xy, 'o', color='silver')
plt.plot(*sexta_interseccion.xy, 'o', color='silver')

#Identificando los valores de las coordenadas x y y de cada vértice
xi1m, yi1m = primera_interseccion.xy
xi2m, yi2m = segunda_interseccion.xy
xi3m, yi3m = tercera_interseccion.xy
xi4m, yi4m = cuarta_interseccion.xy
xi5m, yi5m = quinta_interseccion.xy
xi6m, yi6m = sexta_interseccion.xy

#Cambiamos el formato de matriz a float
xi1 = np.float64(np.array(xi1m))
xi2 = np.float64(np.array(xi2m))
xi3 = np.float64(np.array(xi3m))
xi4 = np.float64(np.array(xi4m))
xi5 = np.float64(np.array(xi5m))
xi6 = np.float64(np.array(xi6m))
yi1 = np.float64(np.array(yi1m))
yi2 = np.float64(np.array(yi2m))
yi3 = np.float64(np.array(yi3m))
yi4 = np.float64(np.array(yi4m))
yi5 = np.float64(np.array(yi5m))
yi6 = np.float64(np.array(yi6m))

#literales de las intersecciones (nombres en el gráfico)
plt.annotate(' A', (xi4, yi4))
plt.annotate(' B', (xi1, yi1))
plt.annotate(' C', (xi2, yi2))
plt.annotate(' D', (xi3, yi3))
plt.annotate(' E', (xi5, yi5))
plt.annotate(' F', (xi6, yi6))

#Evaluando la función objetivo en cada vértice
FOi1 = (xi1 * 30) + (yi1 * 20)
FOi2 = (xi2 * 30) + (yi2 * 20)
FOi3 = (xi3 * 30) + (yi3 * 20)
FOi4 = (xi4 * 30) + (yi4 * 20)

#Calculando el mejor resultado (Maximizar)
ZMAX = max(FOi1, FOi2, FOi3, FOi4)

#Imprimiendo la solución óptima en la consola
print('\n SOLUCIÓN ÓPTIMA')
print('Solución óptima: {} '.format(ZMAX))

#Ordenando las coordenadas de los vértices (Las coordenadas x en m y las coordenadas y en n)
m = [xi1, xi2, xi3, xi4]
n = [yi1, yi2, yi3, yi4]

#Graficando el polígono solución a partir de las coordenadas de los vértices (importante el orden según las manecillas)
plt.fill(m, n, color='silver')

#Identificando el índice del vértice de la mejor solución
dict1 = {0:FOi1, 1:FOi2, 2:FOi3, 3:FOi4}
posicion = max(dict1, key=dict1.get)

#Obteniendo las coordenadas del vértice de la mejor solución de acuerdo al índice del paso anterior
XMAX = m[posicion]
YMAX = n[posicion]

#Imprimiendo las coordenadas del vértice de la mejor solución (variables de decisión)
print('\n VARIABLES DE DECISIÓN')
print('Cantidad de producto X1 a producir: {} unidades'.format(XMAX))
print('Cantidad de producto X2 a producir: {} unidades'.format(YMAX))

#Configuraciones adicionales del gráfico
plt.grid()
plt.xlabel('X1')
plt.ylabel('X2')
plt.title('JOBCO')

plt.show()
</code></pre>
</div>
<p>Al ejecutar este código, de acuerdo a las instrucciones que pueden encontrar en el artículo de<a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/"><strong> introducción al método gráfico mediante Python</strong></a>, obtendrán:</p>
<figure id="attachment_26264" aria-describedby="caption-attachment-26264" style="width: 640px" class="wp-caption aligncenter"><a href="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_metodo_grafico.webp"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_metodo_grafico.webp" alt="jobco_metodo_grafico" width="640" height="480" class="wp-image-26264 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_metodo_grafico.webp 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_metodo_grafico-300x225.webp 300w" sizes="(max-width: 640px) 100vw, 640px" /></a><figcaption id="caption-attachment-26264" class="wp-caption-text">Figura 2: Solución gráfica del caso JOBCO obtenida mediante Python</figcaption></figure>
<p>Así mismo, el siguiente resultado en la consola:</p>
<figure id="attachment_26265" aria-describedby="caption-attachment-26265" style="width: 593px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_optima.png" alt="jobco_solucion_optima" width="593" height="213" class="wp-image-26265 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_optima.png 593w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_optima-300x108.png 300w" sizes="(max-width: 593px) 100vw, 593px" /><figcaption id="caption-attachment-26265" class="wp-caption-text">Figura 3: Resultados del caso JOBCO en la consola de Windows (Solución óptima)</figcaption></figure>
<p>&nbsp;</p>
<p>Así entonces, tenemos establecido el código base sobre el cual abordaremos este primer caso de <em>análisis de sensibilidad: cambios en el lado derecho (disponibilidad de recursos)</em>.</p>
<p>La pregunta que pretende responder este caso de análisis de sensibilidad es <em>¿Qué pasaría con la función objetivo si cambia la disponibilidad de alguno de los recursos del problema?</em> El cual corresponde a un planteamiento muy lógico de análisis, puesto que nos ampliaría la información relevante para la toma de decisiones.</p>
<p>Acercando este interrogante a nuestro problema de ejemplo, la cuestión podría ser: <em>¿Qué pasaría con los ingresos totales si la máquina 1 cambia su capacidad?</em></p>
<p>Pensemos por un momento cómo podemos abordar este planteamiento de manera gráfica, pensemos por un momento cómo podemos utilizar nuestro código base para abordar este caso.</p>
<p>Y bien, se nos puede ocurrir en primer lugar, graficar la nueva línea que represente la nueva función de disponibilidad de la máquina 1. Recordemos que si la restricción base es la siguiente:</p>
<p style="text-align: center;">2<strong>x1</strong> + <strong>x2</strong> &lt;= 8 (Máquina 1)</p>
<p>Un incremento en la capacidad (variación), por ejemplo, se representaría mediante la siguiente inecuación:</p>
<p style="text-align: center;">2<strong>x1</strong> + <strong>x2</strong> &lt;= 8 + 1</p>
<p>Esto quiere decir que, la nueva disponibilidad de la <em><strong>máquina 1</strong></em> pasará de 8 horas a 9 horas. Y nuestro razonamiento nos indica que esta función, tendrá una representación distinta a la anterior, por ende sería de mucha utilidad graficarla. Veamos el código para adicionar en Python:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Ecuaciones e intervalos (Para tabular)
x = np.arange(-20, 20, 5)
y = np.arange(-20, 20, 5)
y1 = 8 - (2 * x)
y2 = (8 - x) / 3
y3 = 0 * x
x1 = 0 * y
z = (-30 * x) / 20
y1v = (8 + 1) - (2 * x) #Variación en la máquina 1 (8 + 1)

#Identificadores para las líneas
primera_linea = LineString(np.column_stack((x, y1)))
segunda_linea = LineString(np.column_stack((x, y2)))
tercera_linea = LineString(np.column_stack((x, y3)))
cuarta_linea = LineString(np.column_stack((x1, y)))
quinta_linea = LineString(np.column_stack((x, z)))
sexta_linea = LineString(np.column_stack((x, y1v))) #Nueva línea

#Graficando las líneas
plt.plot(x, y1, '-', linewidth=2, color='b')
plt.plot(x, y2, '-', linewidth=2, color='g')
plt.plot(x, y3, '-', linewidth=2, color='r')
plt.plot(x1, y, '-', linewidth=2, color='y')
#plt.plot(x, z, ':', linewidth=1, color='k') #Podemos ocultar esta línea (FO)
plt.plot(x, y1v, ':', linewidth=1, color='b') #Nueva línea
</code></pre>
</div>
<p>Podemos observar cómo agregamos tan solo 3 líneas de código, mediante la inclusión de la variable <em><strong>y1v </strong></em>(variación de la ecuación de la máquina 1).</p>
<p>La ecuación con la cual se representa la restricción de la máquina 1 corresponde a la siguiente:</p>
<p style="text-align: center;"><strong>y1</strong> = 8 &#8211; (2 * <strong>x</strong>)</p>
<p>Por ende, lo que hacemos es aumentar la capacidad de la máquina 1 (8) a una nueva capacidad (9 = 8 + 1):</p>
<p style="text-align: center;"><strong>y1v</strong> = (8 + 1) &#8211; (2 * <strong>x</strong>)</p>
<p>Las dos líneas de código adicionales graficarán esta nueva ecuación.</p>
<p>Al ejecutar nuestro código tendremos:</p>
<figure id="attachment_26266" aria-describedby="caption-attachment-26266" style="width: 640px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad.png" alt="jobco_analisis_sensibilidad" width="640" height="480" class="wp-image-26266 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad.png 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad-300x225.png 300w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption id="caption-attachment-26266" class="wp-caption-text">Figura 3: Representación gráfica de un cambio en la disponibilidad del recurso 1</figcaption></figure>
<p>En este caso, podemos observar cómo una variación en la disponibilidad del recurso 1 (máquina 1), se representa con una línea recta (línea azul punteada) que conserva la misma pendiente de la restricción 1 (línea azul continua), que se mueve paralela a ella, y que en consecuencia, genera una nueva intersección solución (intersección con la restricción 2 &#8211; línea verde).</p>
<p>En este nuevo vértice solución se encuentra un nuevo punto óptimo, es decir, una coordenada diferente en la cual al evaluar la función objetivo, tendremos la respuesta al interrogante planteado: <em>¿Qué pasaría con los ingresos totales si la máquina 1 cambia su capacidad?</em></p>
<p>En consecuencia, utilizaremos nuestro código para:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Establecer gráficamente el nuevo vértice</li>
<li>Nombrarlo gráficamente (Vértice <em>G</em>)</li>
<li>Determinar las coordenadas de este vértice (<em>G</em>)</li>
<li>Evaluar la función objetivo en este nuevo vértice (<em>G</em>)</li>
</ul>

		</div>
	
<p>El siguiente fragmento muestra detalladamente las adiciones al código base:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Generando las intersecciones (vértices)
primera_interseccion = cuarta_linea.intersection(segunda_linea)
segunda_interseccion = primera_linea.intersection(segunda_linea)
tercera_interseccion = primera_linea.intersection(tercera_linea)
cuarta_interseccion = tercera_linea.intersection(cuarta_linea)
quinta_interseccion = cuarta_linea.intersection(primera_linea)
sexta_interseccion = segunda_linea.intersection(tercera_linea)
septima_interseccion = sexta_linea.intersection(segunda_linea) #Nuevo vértice

#Graficando los vértices
plt.plot(*primera_interseccion.xy, 'o', color='k')
plt.plot(*segunda_interseccion.xy, 'o', color='k')
plt.plot(*tercera_interseccion.xy, 'o', color='k')
plt.plot(*cuarta_interseccion.xy, 'o', color='k')
plt.plot(*quinta_interseccion.xy, 'o', color='silver')
plt.plot(*sexta_interseccion.xy, 'o', color='silver')
plt.plot(*septima_interseccion.xy, 'o', color='k') #Graficar nuevo vértice

#Identificando los valores de las coordenadas x y y de cada vértice
xi1m, yi1m = primera_interseccion.xy
xi2m, yi2m = segunda_interseccion.xy
xi3m, yi3m = tercera_interseccion.xy
xi4m, yi4m = cuarta_interseccion.xy
xi5m, yi5m = quinta_interseccion.xy
xi6m, yi6m = sexta_interseccion.xy
xi7m, yi7m = septima_interseccion.xy #Coordenadas del nuevo vértice

#Cambiamos el formato de matriz a float
xi1 = np.float64(np.array(xi1m))
xi2 = np.float64(np.array(xi2m))
xi3 = np.float64(np.array(xi3m))
xi4 = np.float64(np.array(xi4m))
xi5 = np.float64(np.array(xi5m))
xi6 = np.float64(np.array(xi6m))
xi7 = np.float64(np.array(xi7m)) #Nueva coordenada en x
yi1 = np.float64(np.array(yi1m))
yi2 = np.float64(np.array(yi2m))
yi3 = np.float64(np.array(yi3m))
yi4 = np.float64(np.array(yi4m))
yi5 = np.float64(np.array(yi5m))
yi6 = np.float64(np.array(yi6m))
yi7 = np.float64(np.array(yi7m)) #Nueva coordenada en y

#literales de las intersecciones (nombres en el gráfico)
plt.annotate(' A', (xi4, yi4))
plt.annotate(' B', (xi1, yi1))
plt.annotate(' C', (xi2, yi2))
plt.annotate(' D', (xi3, yi3))
plt.annotate(' E', (xi5, yi5))
plt.annotate(' F', (xi6, yi6)) 
plt.annotate(' G', (xi7, yi7)) #Nombrar el nuevo vértice como G

#Evaluando la función objetivo en cada vértice
FOi1 = (xi1 * 30) + (yi1 * 20)
FOi2 = (xi2 * 30) + (yi2 * 20)
FOi3 = (xi3 * 30) + (yi3 * 20)
FOi4 = (xi4 * 30) + (yi4 * 20)
FOi4 = (xi7 * 30) + (yi7 * 20) #Calcular la FO en el nuevo vértice

#Imprimiendo la solución óptima en la consola
print('\n SOLUCIÓN ÓPTIMA')
print('Solución óptima: {} '.format(ZMAX))
print('Función objetivo en punto G: {} '.format(FOi7)) #Mostrar la FO en el punto G
</code></pre>
</div>
<p>Lo que realizamos es muy sencillo, es decir, ya que tenemos una nueva línea, realizamos los mismos procedimientos para identificar y graficar el nuevo vértice generado. Al ejecutar el código obtendremos lo siguiente:</p>
<figure id="attachment_26267" aria-describedby="caption-attachment-26267" style="width: 640px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1.png" alt="jobco_analisis_sensibilidad1" width="640" height="480" class="wp-image-26267 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1.png 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1-300x225.png 300w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption id="caption-attachment-26267" class="wp-caption-text">Figura 4: Representación gráfica de un cambio en la disponibilidad del recurso 1 (Nuevo vértice)</figcaption></figure>
<p>&nbsp;</p>
<figure id="attachment_26268" aria-describedby="caption-attachment-26268" style="width: 597px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_sensibilidad.png" alt="jobco_solucion_sensibilidad" width="597" height="226" class="wp-image-26268 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_sensibilidad.png 597w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_solucion_sensibilidad-300x114.png 300w" sizes="(max-width: 597px) 100vw, 597px" /><figcaption id="caption-attachment-26268" class="wp-caption-text">Figura 5: Resultados del caso JOBCO en la consola de Windows (Función objetivo en punto G)</figcaption></figure>
<p>&nbsp;</p>
<p>La <em>figura 5</em> ilustra el cambio de la solución óptima cuando se cambia la capacidad de la máquina 1. Si la capacidad diaria se incrementa de 8 a 9 horas, el nuevo óptimo se moverá al punto <strong><em>G</em></strong>. Los resultados que se pueden apreciar en la consola, nos permiten conocer que el valor de la función objetivo evaluada en el punto <em><strong>G </strong>(<strong>z</strong> en <strong>G</strong>)</em> equivale a 142,0. Es decir, un valor superior a 128,0 (<em><strong>z</strong></em> en <em><strong>C</strong></em>).</p>
<p>Con los datos obtenidos podemos establecer en este caso la tasa de cambio en la función objetivo a consecuencia del cambio de la capacidad de la máquina 1 de 8 a 9 horas. Esta tasa de cambio se denomina de diversas formas: <em><strong>valor unitario de un recurso </strong></em>(Taha); <em><strong>precio dual </strong></em>(múltiples solucionadores); <em><strong>shadow price </strong></em>(precio sombra).</p>
<p>Esta tasa representa el impacto del incremento unitario en la capacidad de un recurso en la función objetivo; se calcula de la siguiente manera:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual.png" alt="precio_dual" width="517" height="59" class="aligncenter size-full wp-image-26269" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual.png 517w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual-300x34.png 300w" sizes="(max-width: 517px) 100vw, 517px" /></p>
<p>En nuestro ejemplo se calculará así:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/shadow_price.png" alt="shadow_price" width="346" height="232" class="aligncenter size-full wp-image-26270" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/shadow_price.png 346w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/shadow_price-300x201.png 300w" sizes="(max-width: 346px) 100vw, 346px" /></p>
<p>Esto indica que: <em>Un incremento unitario en la capacidad de la máquina 1, aumentará el ingreso en $14,0</em>. O lo que es igual: <em>Una reducción unitaria en la capacidad de la máquina 1, reducirá el ingreso en $14,0.</em></p>
<p>En lo que concierne a nuestro código e Python, es sencillo, resulta que nosotros ya tenemos la función objetivo evaluada en cada uno de los vértices del gráfico. Así entonces, será cuestión de crear una nueva variable que represente el <em><strong>precio dual </strong></em>y la operación correspondiente:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Precio dual (Restricción 1)
Dual1 = (FOi7 - ZMAX) #Calculamos el precio dual para la restricción 1

print('\n ANÁLISIS DE SENSIBILIDAD')
print('Precio dual de la máquina 1: {} $/h'.format(Dual1)) #Imprimimos el precio dual 1
</code></pre>
</div>
<p>Recordemos que la función objetivo del punto <em><strong>G </strong></em>(<em><strong>z</strong></em> en <em><strong>G</strong>) </em>se representa por la variable<em><strong> FOi7</strong></em> (función objetivo en la intersección 7). La operación efectúa la diferencia entre este valor y la función objetivo en el vértice óptimo base (<em><strong>ZMAX</strong></em>).</p>
<p>Al ejecutar el código obtendremos lo siguiente en la consola de Windows:</p>
<figure id="attachment_26271" aria-describedby="caption-attachment-26271" style="width: 592px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual1.png" alt="precio_dual1" width="592" height="262" class="wp-image-26271 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual1.png 592w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/precio_dual1-300x133.png 300w" sizes="(max-width: 592px) 100vw, 592px" /><figcaption id="caption-attachment-26271" class="wp-caption-text">Figura 6: Resultados del caso JOBCO (Precio dual máquina 1)</figcaption></figure>
<p>&nbsp;</p>
<p>Ahora bien, debemos recordar que hemos hecho énfasis en que el análisis de sensibilidad del caso 1 (cambios en el lado derecho de las restricciones &#8211; capacidad de los recursos), precisa de unos límites dentro de los cuáles los resultados del modelo me permiten calcular el impacto que tiene sobre la solución óptima. Es decir, para nuestro caso en particular: <em>los límites dentro de los cuales el precio dual para la máquina 1 es 14$/h. </em>Dicho de otra manera, estamos en condiciones de calcular el impacto de una variación en la máquina 1 en los ingresos totales, pero, <em>¿Hasta cuándo esa proyección es válida? </em>Y sí, existen unos límites dentro de los cuales nuestra proyección, tasa o precio dual es válido, por fuera de ellos, sería necesario recalcular.</p>
<p>Revisemos nuevamente la <em>figura 4</em>:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1.png" alt="jobco_analisis_sensibilidad1" width="640" height="480" class="aligncenter size-full wp-image-26267" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1.png 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad1-300x225.png 300w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>Veamos cómo la línea que representa los cambios en la capacidad de la <em><strong>máquina 1 </strong></em>(recurso) se mueve paralela a sí misma, y bien puede hacerlo sobre el segmento de la línea que representa la capacidad de la <em><strong>máquina 2</strong></em> (línea verde, o algebraicamente: <em>línea BF, desde el punto B hasta el punto F</em>). Esa es básicamente la clave para determinar los límites de validez de nuestro precio dual, mejor conocidos como <em><strong>intervalos de factibilidad</strong></em>. Pero <em>¿Cuál es la clave? </em>Sí, la ecuación de la <em><strong>máquina 1</strong></em> puede moverse para formar una intersección con la <em><strong>línea verde</strong></em> (a medida que cambie su capacidad) entre <em><strong>B</strong></em> y <em><strong>F</strong></em>, de manera que en estos vértices se encuentran los límites. Y para ser directos, la idea consiste en calcular la capacidad de la máquina 1 en cada uno de estos vértices. <em>¿Cómo lo hacemos? </em>Veamos:</p>
<p>La ecuación que representa la capacidad de la máquina 1 está dada por:</p>
<p style="text-align: center;">2<strong>x1</strong> + <strong>x2</strong> &lt;= 8 horas (máquina 1)</p>
<p>Por ende, si retiramos el valor de la capacidad del recurso (8 horas), obtendremos la ecuación de la utilización o el uso del recurso:</p>
<p style="text-align: center;">2<strong>x1</strong> + <strong>x2 </strong>= Utilización o uso de la máquina 1</p>
<p>Ahora, teniendo la ecuación de la restricción 1 (máquina 1), podemos evaluar cuál sería su utilización en los vértices <em><strong>B </strong></em>y <em><strong>F</strong></em>. Así encontraríamos los límites de validez para esta restricción (intervalos de factibilidad para nuestro precio dual = 14$/h). Dicho de otro modo, encontraríamos la capacidad mínima y máxima de la máquina 1, para que una variación unitaria en la disponibilidad de la misma (máquina 1), represente una variación de 14$ en la función objetivo. Por fuera de ese intervalo, no podemos asegurar de ninguna manera que el precio dual es de 14$/h, sería necesario recalcular.</p>
<p>Volvamos a la determinación de los límites. Ya tenemos la ecuación de la restricción (la verdad, siempre la hemos tenido), ahora la evaluaremos en los puntos <em><strong>B </strong></em>y <em><strong>F</strong></em>. Para ello es preciso conocer las coordenadas de cada uno de estos puntos. En <em>Python</em> podemos utilizar el siguiente fragmento:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Intervalos de factibilidad
IFmin1 = (2 * xi2) + yi2
IFmax1 = (2 * xi6) + yi6

print('Límite mínimo de factibilidad (PD máquina 1): {} h'.format(IFmin1))
print('Límite máximo de factibilidad (PD máquina 1): {} h'.format(IFmax1))
</code></pre>
</div>
<p>Veamos: El punto <em><strong>B</strong></em> según nuestro código, corresponde a la segunda intersección, por ende sus coordenadas están definidas por las variables <em><strong>xi1 </strong></em>y <em><strong>yi1</strong></em>. Así entonces, evaluamos la capacidad de la <em><strong>máquina 1</strong></em> en estas coordenadas y tenemos el límite mínimo de factibilidad para el precio dual de la máquina 1 <em>(<strong>IFmin1</strong>).</em></p>
<p>Así mismo, el punto <b><i>F </i></b>según nuestro código, corresponde a la sexta intersección, por ende sus coordenadas están definidas por las variables <em><strong>xi6 </strong></em>y <em><strong>yi6</strong></em>. Así entonces, evaluamos la capacidad de la <em><strong>máquina 1</strong></em> en estas coordenadas y tenemos el límite mínimo de factibilidad para el precio dual de la máquina 1 (<em><strong>IFmax1</strong></em>).</p>
<p>Al ejecutar el código obtendremos lo siguiente en la consola de Windows:</p>
<figure id="attachment_26276" aria-describedby="caption-attachment-26276" style="width: 592px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad1_a.png" alt="intervalo_de_factibilidad1_a" width="592" height="327" class="size-full wp-image-26276" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad1_a.png 592w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad1_a-300x166.png 300w" sizes="(max-width: 592px) 100vw, 592px" /><figcaption id="caption-attachment-26276" class="wp-caption-text">Figura 7: Intervalos de factibilidad para la máquina 1 (Caso JOBCO)</figcaption></figure>
<p>&nbsp;</p>
<p>Podemos efectuar nosotros mismos los cálculos correspondientes:</p>
<p>El punto <em><strong>B</strong></em> tiene la siguientes coordenadas (<em><strong>x </strong></em>= 0, <em><strong>y </strong></em>= 2,67)</p>
<p>Evaluando estas coordenadas en la ecuación de la <strong><em>máquina 1</em></strong>, tenemos:</p>
<p style="text-align: center;">2(<strong>0</strong>)+ (<strong>2,67</strong>) = Límite mínimo de factibilidad (máquina 1)</p>
<p style="text-align: center;">2(<strong>0</strong>)+ (<strong>2,67</strong>) = 2,67 horas</p>
<p>El punto <em><strong>F</strong></em> tiene la siguientes coordenadas (<em><strong>x </strong></em>= 8, <em><strong>y </strong></em>= 0)</p>
<p>Evaluando estas coordenadas en la ecuación de la <strong><em>máquina 1</em></strong>, tenemos:</p>
<p style="text-align: center;">2(<strong>8</strong>)+ (<strong>0</strong>) = Límite máximo de factibilidad (máquina 1)</p>
<p style="text-align: center;">2(<strong>8</strong>)+ (<strong>0</strong>) = 16 horas</p>
<p>Ya efectuamos los procedimientos de manera manual y mediante <em>Python</em>. La conclusión es que el precio dual de $14/h permanece válido en el intervalo:</p>
<p style="text-align: center;"><strong>2,67</strong> h &lt;= Capacidad de la <em><strong>máquina 1</strong></em> &lt;= <strong>16</strong> h</p>
<p>Los cambios que se encuentren fuera de esos <em><strong>intervalos de factibilidad</strong> </em>producen un precio dual diferente, y por lo tanto, precisan nuevos cálculos.</p>
<p>En nuestro código, incluiremos las líneas que permitan llegar al precio dual y a los intervalos de factibilidad de la <em><strong>máquina 2</strong>. </em>Al ejecutarlo, obtendremos lo siguiente:</p>
<figure id="attachment_26273" aria-describedby="caption-attachment-26273" style="width: 640px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad2.png" alt="jobco_analisis_sensibilidad2" width="640" height="480" class="wp-image-26273 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad2.png 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/jobco_analisis_sensibilidad2-300x225.png 300w" sizes="(max-width: 640px) 100vw, 640px" /><figcaption id="caption-attachment-26273" class="wp-caption-text">Figura 8: Representación gráfica de un cambio en la disponibilidad del recurso 2 (Nuevo vértice H)</figcaption></figure>
<p>&nbsp;</p>
<figure id="attachment_26277" aria-describedby="caption-attachment-26277" style="width: 548px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad2_b.png" alt="intervalo_de_factibilidad2_b" width="548" height="404" class="size-full wp-image-26277" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad2_b.png 548w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/intervalo_de_factibilidad2_b-300x221.png 300w" sizes="(max-width: 548px) 100vw, 548px" /><figcaption id="caption-attachment-26277" class="wp-caption-text">Figura 9: Precio dual e intervalos de factibilidad para la máquina 2 (Caso JOBCO)</figcaption></figure>
<p>&nbsp;</p>
<p>La conclusión es que el precio dual de $2/h permanece válido en el intervalo:</p>
<p style="text-align: center;"><strong>4</strong> h &lt;= Capacidad de la <em><strong>máquina 2</strong></em> &lt;= <strong>24</strong> h</p>
<p>Los precios duales y los intervalos de factibilidad amplían la base que ofrece la solución óptima para la toma de decisiones. <em>Taha </em>propone una serie de preguntas alrededor de este caso (ejercicio 3.6-1), que pueden ser abordadas gracias al análisis de sensibilidad.</p>
<p><em><strong>Pregunta 1</strong></em>: Si JOBCO puede incrementar la capacidad de ambas máquinas, ¿Cuál máquina tendrá la prioridad?</p>
<p><strong>Respuesta: </strong>De acuerdo a los precios duales para las máquinas 1 y 2, cada hora adicional de la máquina 1 incrementa el ingreso total en $14; mientras tanto, cada hora adicional de la máquina 2 incrementa el ingreso total en $2. Por lo tanto, la máquina 1 debe tener la prioridad.</p>
<p><em><strong>Pregunta 2</strong></em>: Se sugiere incrementar las capacidades de las máquinas 1 y 2 al costo adicional de $10/h para cada máquina. ¿Es esto aconsejable?</p>
<p><strong>Respuesta: </strong>De acuerdo a los precios duales para las máquinas 1 y 2.  Los ingresos netos adicionales por hora serían de la siguiente manera:</p>
<p><em>Máquina 1:</em></p>
<p>14$/h (Precio dual máquina 1) &#8211; 10$/h (Costo para aumentar capacidad) = 4 $/h (Ingreso neto)</p>
<p><em>Máquina 2:</em></p>
<p>2$/h (Precio dual máquina 2) &#8211; 10$/h (Costo para aumentar capacidad) = &#8211; 8 $/h (Ingreso neto)</p>
<p>Por consiguiente, solo la <em>máquina 1</em> debe considerarse para el incremento de capacidad.</p>
<p><em><strong>Pregunta 3</strong></em>: Si la capacidad de la máquina 1 se incrementa de 8 a 13 horas, ¿Cómo impactará este incremento al ingreso óptimo?</p>
<p><strong>Respuesta</strong>: Lo primero que debe evaluarse es que la nueva capacidad de la máquina 1 se encuentre dentro del intervalo de factibilidad (2,67 h &#8211; 16 h). Ya que sí se encuentra en dicho intervalo (13 h), el siguiente paso consiste en calcular el <em>cambio en la disponibilidad</em>:</p>
<p style="text-align: center;"><em>Cambio en la capacidad</em> = 13 (Nueva capacidad) &#8211; 8 (Capacidad inicial)</p>
<p style="text-align: center;"><em>Cambio en la capacidad</em> = 5 horas</p>
<p>Por ende, multiplicamos dicho cambio por la tasa de cambio del ingreso (precio dual):</p>
<p style="text-align: center;"><em>Cambio en el ingreso</em> = Precio dual  * Cambio en la capacidad</p>
<p style="text-align: center;"><em>Cambio en el ingreso</em> = 14 $/h * (5 horas)</p>
<p style="text-align: center;"><em>Cambio en el ingreso</em> = 70 $</p>
<p style="text-align: center;"><em>Nuevo ingreso</em> = Ingreso base (Solución óptima) + Cambio en el ingreso</p>
<p style="text-align: center;"><em>Nuevo ingreso</em> = $ 128 + $ 70</p>
<p style="text-align: center;"><em>Nuevo ingreso</em> = $ 198</p>
<p>En el caso de que la nueva capacidad de la máquina se encuentre por fuera del intervalo de factibilidad para dicho recurso, no se dispondría de información suficiente para llegar a una conclusión válida.</p>
<hr />
<p>A continuación, dejamos a disposición el código completo del análisis de sensibilidad gráfica desarrollado en <em>Python</em>:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Caso 1: Investigación de Operaciones (9na edición), de Hamdy A. Taha 
# (University of Arkansas, Fayetteville), (Ejemplo 3.6-1)
# Autor del código en Python: Bryan Salazar López, Ing. M.Sc. (2021)

#Librerías necesarias
import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import LineString

#Ecuaciones e intervalos (Para tabular)
x = np.arange(-20, 20, 5)
y = np.arange(-20, 20, 5)
y1 = 8 - (2 * x)
y2 = (8 - x) / 3
y3 = 0 * x
x1 = 0 * y
z = (-30 * x) / 20
y1v = (8 + 1) - (2 * x) #Variación en la máquina 1 (8 + 1)
y2v = ((8 + 1) - x) / 3 #Variación en la máquina 2 (8 + 1)

#Identificadores para las líneas
primera_linea = LineString(np.column_stack((x, y1)))
segunda_linea = LineString(np.column_stack((x, y2)))
tercera_linea = LineString(np.column_stack((x, y3)))
cuarta_linea = LineString(np.column_stack((x1, y)))
quinta_linea = LineString(np.column_stack((x, z)))
sexta_linea = LineString(np.column_stack((x, y1v))) #Nueva línea Máquina 1 (M1)
septima_linea = LineString(np.column_stack((x, y2v))) #Nueva línea Máquina 2 (M2)

#Graficando las líneas
plt.plot(x, y1, '-', linewidth=2, color='b')
plt.plot(x, y2, '-', linewidth=2, color='g')
plt.plot(x, y3, '-', linewidth=2, color='r')
plt.plot(x1, y, '-', linewidth=2, color='y')
#plt.plot(x, z, ':', linewidth=1, color='k')
plt.plot(x, y1v, ':', linewidth=1, color='b') #Nueva línea M1
plt.plot(x, y2v, ':', linewidth=1, color='g') #Nueva línea M2


#Generando las intersecciones (vértices)
primera_interseccion = cuarta_linea.intersection(segunda_linea)
segunda_interseccion = primera_linea.intersection(segunda_linea)
tercera_interseccion = primera_linea.intersection(tercera_linea)
cuarta_interseccion = tercera_linea.intersection(cuarta_linea)
quinta_interseccion = cuarta_linea.intersection(primera_linea)
sexta_interseccion = segunda_linea.intersection(tercera_linea)
septima_interseccion = sexta_linea.intersection(segunda_linea) #Nuevo vértice M1
octava_interseccion = septima_linea.intersection(primera_linea) #Nuevo vértice M2

#Graficando los vértices
plt.plot(*primera_interseccion.xy, 'o', color='k')
plt.plot(*segunda_interseccion.xy, 'o', color='k')
plt.plot(*tercera_interseccion.xy, 'o', color='k')
plt.plot(*cuarta_interseccion.xy, 'o', color='k')
plt.plot(*quinta_interseccion.xy, 'o', color='silver')
plt.plot(*sexta_interseccion.xy, 'o', color='silver')
plt.plot(*septima_interseccion.xy, 'o', color='k') #Graficar nuevo vértice M1
plt.plot(*octava_interseccion.xy, 'o', color='k') #Graficar nuevo vértice M2

#Identificando los valores de las coordenadas x y y de cada vértice
xi1m, yi1m = primera_interseccion.xy
xi2m, yi2m = segunda_interseccion.xy
xi3m, yi3m = tercera_interseccion.xy
xi4m, yi4m = cuarta_interseccion.xy
xi5m, yi5m = quinta_interseccion.xy
xi6m, yi6m = sexta_interseccion.xy
xi7m, yi7m = septima_interseccion.xy #Coordenadas del nuevo vértice M1
xi8m, yi8m = octava_interseccion.xy #Coordenadas del nuevo vértice M2

#Cambiamos el formato de matriz a float
xi1 = np.float64(np.array(xi1m))
xi2 = np.float64(np.array(xi2m))
xi3 = np.float64(np.array(xi3m))
xi4 = np.float64(np.array(xi4m))
xi5 = np.float64(np.array(xi5m))
xi6 = np.float64(np.array(xi6m))
xi7 = np.float64(np.array(xi7m)) #Nueva coordenada en x (Máquina 1)
xi8 = np.float64(np.array(xi8m)) #Nueva coordenada en x (Máquina 2)
yi1 = np.float64(np.array(yi1m))
yi2 = np.float64(np.array(yi2m))
yi3 = np.float64(np.array(yi3m))
yi4 = np.float64(np.array(yi4m))
yi5 = np.float64(np.array(yi5m))
yi6 = np.float64(np.array(yi6m))
yi7 = np.float64(np.array(yi7m)) #Nueva coordenada en y (Máquina 1)
yi8 = np.float64(np.array(yi8m)) #Nueva coordenada en y (Máquina 2)

#literales de las intersecciones (nombres en el gráfico)
plt.annotate(' A', (xi4, yi4))
plt.annotate(' B', (xi1, yi1))
plt.annotate(' C', (xi2, yi2))
plt.annotate(' D', (xi3, yi3))
plt.annotate(' E', (xi5, yi5))
plt.annotate(' F', (xi6, yi6)) 
plt.annotate(' G', (xi7, yi7)) #Nombrar el nuevo vértice como G
plt.annotate(' H', (xi8, yi8)) #Nombrar el nuevo vértice como H

#Evaluando la función objetivo en cada vértice
FOi1 = (xi1 * 30) + (yi1 * 20)
FOi2 = (xi2 * 30) + (yi2 * 20)
FOi3 = (xi3 * 30) + (yi3 * 20)
FOi4 = (xi4 * 30) + (yi4 * 20)
FOi7 = (xi7 * 30) + (yi7 * 20) #Calcular la FO en el nuevo vértice G
FOi8 = (xi8 * 30) + (yi8 * 20) #Calcular la FO en el nuevo vértice H

#Calculando el mejor resultado (Maximizar)
ZMAX = max(FOi1, FOi2, FOi3, FOi4)

#Imprimiendo la solución óptima en la consola
print('\n SOLUCIÓN ÓPTIMA')
print('Solución óptima: {} '.format(ZMAX))
print('Función objetivo en punto G: {} '.format(FOi7))
print('Función objetivo en punto H: {} '.format(FOi8))

#Ordenando las coordenadas de los vértices (Las coordenadas x en m y las coordenadas y en n)
m = [xi1, xi2, xi3, xi4]
n = [yi1, yi2, yi3, yi4]

#Graficando el polígono solución a partir de las coordenadas de los vértices (importante el orden según las manecillas)
plt.fill(m, n, color='silver')

#Identificando el índice del vértice de la mejor solución
dict1 = {0:FOi1, 1:FOi2, 2:FOi3, 3:FOi4}
posicion = max(dict1, key=dict1.get)

#Obteniendo las coordenadas del vértice de la mejor solución de acuerdo al índice del paso anterior
XMAX = m[posicion]
YMAX = n[posicion]

#Imprimiendo las coordenadas del vértice de la mejor solución (variables de decisión)
print('\n VARIABLES DE DECISIÓN')
print('Cantidad de producto X1 a producir: {} unidades'.format(XMAX))
print('Cantidad de producto X2 a producir: {} unidades'.format(YMAX))

#Precio dual (Restricción 1)
Dual1 = (FOi7 - ZMAX) #Calculamos el precio dual para la restricción 1
Dual2 = (FOi8 - ZMAX) #Calculamos el precio dual para la restricción 1

#Imprimir los precios duales
print('\n ANÁLISIS DE SENSIBILIDAD')
print('Precio dual de la máquina 1: {} $/h'.format(Dual1)) #Imprimimos el precio dual 1
print('Precio dual de la máquina 2: {} $/h'.format(Dual2)) #Imprimimos el precio dual 2

#Intervalos de factibilidad (Máquina 1)
IFmin1 = (2 * xi1) + yi1
IFmax1 = (2 * xi6) + yi6

#Intervalos de factibilidad (Máquina 2)
IFmin2 = xi3 + (3 * yi3)
IFmax2 = xi5 + (3 * yi5)

#Imprimir los intervalos de factibilidad
print('\n INTERVALOS DE FACTIBILIDAD')
print('Límite mínimo de factibilidad (PD máquina 1): {} h'.format(IFmin1))
print('Límite máximo de factibilidad (PD máquina 1): {} h'.format(IFmax1))
print('Límite mínimo de factibilidad (PD máquina 2): {} h'.format(IFmin2))
print('Límite máximo de factibilidad (PD máquina 2): {} h'.format(IFmax2))


#Configuraciones adicionales del gráfico
plt.grid()
plt.xlabel('X1')
plt.ylabel('X2')
plt.title('JOBCO')

plt.show()
</code></pre>
</div>
<hr />
<p>En el próximo artículo desarrollaremos el segundo caso de análisis de sensibilidad: <em>cambios en la utilidad unitaria o el costo unitario.</em></p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/">Análisis de sensibilidad gráfica mediante el uso de Python (Caso 1)</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Método gráfico de la programación lineal mediante el uso de Python</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/#comments</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Sat, 17 Jul 2021 19:50:16 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Método gráfico]]></category>
		<category><![CDATA[Optimización]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Solución gráfica]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=26194</guid>

					<description><![CDATA[<p>Tal como lo mencionamos en el artículo en el que abordamos inicialmente los pasos de resolución gráfica de los modelos de programación lineal; dada la limitación en la cantidad de variables que puede soportar el método gráfico (2 variables), y dada la forma manual de resolución del mismo, este es difícilmente útil en la práctica. &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/">Método gráfico de la programación lineal mediante el uso de Python</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Tal como lo mencionamos en el artículo en el que abordamos inicialmente los pasos de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico/"><em><strong>resolución gráfica</strong></em></a> de los modelos de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal/"><strong>programación lineal</strong></a>; dada la limitación en la cantidad de variables que puede soportar el método gráfico (2 variables), y dada la forma manual de resolución del mismo, este es difícilmente útil en la práctica.</p>
<h3>¿Por qué debería aprender a solucionar gráficamente un modelo de PL?</h3>

		<div id="por-que-deberia-aprender-a-solucionar-graficamente-un-modelo-de-pl" data-title="¿Por qué debería aprender a solucionar gráficamente un modelo de PL?" class="index-title"></div>
	
<p>La solución gráfica de los modelos de programación lineal, puede proporcionar una perspectiva que permita un entendimiento más amplio de los métodos generales de resolución (<a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-simplex/"><strong>método simplex</strong></a> o solucionadores). Del mismo modo, permite una mayor comprensión de los diversos elementos que componen un modelo de PL (dado su componente visual), siendo especialmente útil en el aprendizaje de diferentes tópicos como pueden ser: <em>tipos de solución, tipos de restricciones y análisis de sensibilidad</em>.</p>
<h3>¿Solo puedo solucionar gráficamente un modelo de PL de manera manual?</h3>
<p>Si bien hemos reconocido las bondades del aprendizaje de la solución gráfica, razón por la cual su enseñanza perdura en la academia; también hemos de reconocer que la naturaleza manual de sus cálculos y procedimientos gráficos, pueden hacer tedioso su aprendizaje.</p>
<p>Ahora bien, es preciso considerar que gran parte de los programas solucionadores de modelos PL cuentan con algún módulo gráfico interactivo (TORA, WinQSB, por ejemplo), que puede remplazar los procedimientos manuales. Así entonces, existen otras alternativas a los procedimientos y cálculos manuales, que permiten lograr un acercamiento más ágil al método de solución gráfica de PL.</p>
<h2>Solución gráfica de programación lineal mediante Python</h2>
<p>Ya hemos justificado de manera suficiente la permanencia del método gráfico en los programas de enseñanza de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-la-investigacion-de-operaciones/"><strong>Investigación de Operaciones</strong></a>; sin embargo, es necesario reconocer la posibilidad actual de integrar diversas disciplinas a través de los procesos de aprendizaje que enriquezcan la formación en la materia.</p>
<p>El objetivo de este artículo consiste precisamente en abordar el método gráfico como alternativa de solución de modelos de programación lineal, al tiempo que generamos un código en Python (programación), que nos permita automatizar procedimientos gráficos y cálculos.</p>
<p>El propósito de integrar un lenguaje de programación como lo es Python, consiste en acercar a las personas a la posibilidad de integrar métodos tradicionales de la investigación de operaciones con herramientas tecnológicas vigentes. <em>¿Por qué Python? </em>Porque es gratuito; está respaldado por los desarrollos de una gran comunidad (casi que existe una línea de código desarrollado para cada requerimiento); sus aplicaciones no se limitan a un área en concreto, y por lo tanto podemos integrar diversos desarrollos a nuestros métodos.</p>
<p>En síntesis, queremos aportar nuevas propuestas en la enseñanza de la investigación de operaciones a través de la programación.</p>
<hr />
<h3>Requisitos técnicos</h3>

		<div id="requisitos-tecnicos" data-title="Requisitos técnicos" class="index-title"></div>
	
<p>Los requisitos técnicos varían de acuerdo a si queremos ejecutar los códigos en nuestro equipo, o si queremos utilizar un entorno colaborativo (<em>recomendado</em>).</p>
<h4><em>Requisitos en nuestro equipo</em></h4>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Tener una versión de Python instalada (preferiblemente una versión posterior a 3.6): <a href="https://www.python.org/downloads/release/python-380/"><strong>Descargar Python 3.8x 64-bit</strong></a></li>
<li>Tener una versión de <em>pip</em> (paquetes de instalación) de Python superior a 9.01 (Python 3.4 o superiores vienen con <em>pip</em> incorporado). Esto le permitirá instalar librerías con un comando simple y directo: <em>pip</em>.</li>
<li>Tener un editor para código, nosotros recomendamos un editor simple, por ejemplo: Sublime Text: <a href="https://www.sublimetext.com/3"><strong>Descargar Sublime Text</strong></a><strong>. </strong>También puede utilizar un IDE (Entorno de Desarrollo Integrado), como <em><strong>Spyder.</strong></em> Todo dependerá del alcance de su proyecto.</li>
</ul>

		</div>
	
<h4><em>Requisitos en un entorno colaborativo</em></h4>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<hr />
<h3>Librerías necesarias</h3>

		<div id="librerias-necesarias" data-title="Librerías necesarias" class="index-title"></div>
	
<p>Con la instalación de los programas, o el uso de un entorno colaborativo, ya estamos listos para empezar a programar, sin embargo, cada desarrollo, por pequeño que este sea, tiene sus particularidades; y existen herramientas (librerías) que nos facilitan considerablemente nuestros desarrollos, de acuerdo a nuestras necesidades. Para colocarlo en perspectiva, piense en se teléfono celular, tenemos diferentes <em>apps</em>, cada una de ellas nos facilita algunas tareas, de acuerdo a nuestras necesidades; pues bien, así funcionan las librerías en Python.</p>
<p>El desarrollo que queremos lograr requiere de las siguientes librerías:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><strong>Numpy</strong>: Numpy es una librería (biblioteca) que contiene un conjunto amplio de soluciones especializadas en cálculos matemáticos de alto nivel. Numpy nos facilita la vida a la hora de trabajar con matrices, vectores, rangos, etc.</li>
</ul>

		</div>
	
<p>Instalar una librería en Python es muy sencillo, de ahí la importancia de contar con <em>pip</em> (paquetes de instalación). <em>Pip </em>nos permite instalar cualquier librería con un comando escrito en la <span class="tie-highlight tie-highlight-black"><em><strong>Lista de comandos de Windows (CMD)</strong></em></span>. Recuerde que si utiliza un entorno colaborativo, este incluye gran parte de las librerías necesarias.</p>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span> Para abrir la <em><strong>Lista de comandos de Windows </strong></em>pulsa a la vez la tecla de Windows (normalmente tiene el logotipo de Microsoft, está ubicada cerca de la tecla Alt) y la letra R. Se te abrirá una pequeña ventana con el título de «Ejecutar». Dentro del rectángulo para introducir texto que verás en la ventana, escribe cmd y pulsa sobre el botón de «Aceptar».
			</div>
		</div>
	
<p>Para instalar Numpy tan solo debemos escribir el siguiente comando en la lista de comandos: <span>pip install numpy</span></p>
<figure id="attachment_26196" aria-describedby="caption-attachment-26196" style="width: 559px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy.jpg" alt="Numpy" width="559" height="159" class="wp-image-26196 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy.jpg 559w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy-300x85.jpg 300w" sizes="(max-width: 559px) 100vw, 559px" /><figcaption id="caption-attachment-26196" class="wp-caption-text">Figura 1: Comando para instalar librería Numpy</figcaption></figure>
<p>Damos <em><strong>ENTER </strong></em>y veremos lo siguiente:</p>
<figure id="attachment_26197" aria-describedby="caption-attachment-26197" style="width: 575px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy2.jpg" alt="Numpy2" width="575" height="301" class="wp-image-26197 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy2.jpg 575w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Numpy2-300x157.jpg 300w" sizes="(max-width: 575px) 100vw, 575px" /><figcaption id="caption-attachment-26197" class="wp-caption-text">Figura 2: Proceso de descarga de librería Numpy finalizado</figcaption></figure>
<p>Así de sencillo es instalar una librería en Python.</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><strong>Matplotlib</strong>: Matplotlib es una librería (biblioteca) completa para crear visualizaciones estáticas, animadas e interactivas en Python. Y bien, si queremos abordar el método gráfico, necesitamos una librería que nos ayude con las gráficas.</li>
</ul>

		</div>
	
<p>Para instalar Matplotlib tan solo debemos escribir el siguiente comando en la lista de comandos: pip install matplotlib</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li><strong>Shapely</strong>: Shapely es una librería (biblioteca) completa que nos permite la manipulación y análisis de objetos geométricos. Nos resultará de mucha ayuda al momento de manipular nuestras gráficas.</li>
</ul>

		</div>
	
<p>Para instalar Shapely tan solo debemos escribir el siguiente comando en la lista de comandos: pip install Shapely</p>
<p>Así de sencillo es instalar librerías en Python. Con estas librerías estamos listos para crear nuestro desarrollo.</p>
<hr />
<h2>El problema</h2>
<blockquote class=" quote-simple "><p>Un autobús que hace el recorrido Cali-Buga, ofrece asientos para fumadores al precio de 10.000 pesos y a no fumadores al precio de 6.000 pesos. Al no fumador se le deja llevar 50 Kg. de peso y al fumador 20 Kg. Si el autobús tiene 90 asientos y admite un equipaje de hasta 3.000 Kg. ¿Cuál ha de ser la oferta de asientos de la compañía para cada tipo de pasajeros, con la finalidad de optimizar el beneficio? Además, debe considerarse que por políticas de la empresa, deben ofrecerse cómo mínimo 10 asientos para pasajeros no fumadores.</p></blockquote>
<h3>Modelamiento mediante programación lineal</h3>
<p><em><strong>Variables</strong></em></p>
<p><strong>x</strong>: Cantidad de asientos reservados a fumadores.</p>
<p><strong>y</strong>: Cantidad de asientos reservados a no fumadores.</p>
<p><em><strong>Restricciones</strong></em></p>
<p>20<strong>x</strong> + 50<strong>y</strong> &lt;= 3000 <em>(Equipaje permitido)</em></p>
<p><strong>x</strong> + <strong>y</strong> &lt;= 90 <em>(Cantidad de asientos disponibles)</em></p>
<p><strong>y</strong> &gt;= 10 <em>(Política de asientos mínimos para no fumadores)</em></p>
<p><strong>y</strong> &gt;= 0 <em>(No negatividad)</em></p>
<p><strong>x</strong> &gt;= 0 <em>(No negatividad)</em></p>
<p><em><strong>Función objetivo </strong></em></p>
<p><em><strong>z = </strong></em>10000<strong>x</strong> + 6000<strong>y  </strong><em>(Maximizar)</em></p>
<hr />
<h2>Método gráfico mediante Python</h2>
<h3>Paso 1: Importar las librerías</h3>

		<div id="paso-1-importar-las-librerias" data-title="Paso 1: Importar las librerías" class="index-title"></div>
	
<p>El siguiente fragmento de código importa las librerías necesarias para nuestro desarrollo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Librerías necesarias
import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import LineString
</code></pre>
</div>
<h3>Paso 2: Ecuaciones e intervalos</h3>

		<div id="paso-2-ecuaciones-e-intervalos" data-title="Paso 2: Ecuaciones e intervalos" class="index-title"></div>
	
<p>Para poder graficar las restricciones (paso esencial del método gráfico), es necesario representar las ecuaciones por medio de líneas en nuestra gráfica. Cada restricción estará representada por una línea recta. El paso que antecede trazar una línea recta consiste en la determinación de un mínimo de dos puntos que al unirse la conformen.</p>
<p>Cada punto en el plano cartesiano se encuentra conformado por una coordenada en <em><strong>x</strong> </em>y una coordenada en <em><strong>y</strong> </em>(en el caso que así definamos llamar a la abscisa y a la ordenada). Recordemos que a partir del modelo algebraico inicial, nuestras restricciones se encuentran representadas por ecuaciones. Así entonces, la tarea consiste en, a partir de una ecuación, obtener un conjunto mínimo de dos coordenadas.</p>
<p>Manualmente este proceso se realiza tabulando, es decir, despejando el valor de una de las variables de la ecuación, a partir de la asignación arbitraria de un valor a la variable restante.</p>
<p>La tarea que tenemos mediante la programación en Python consistirá entonces en automatizar este proceso.</p>
<p>Lo primero que debemos hacer es despejar cada una de las inecuaciones convertidas en ecuaciones del problema, para eso, en nuestro caso, vamos a despejar en función de <em><strong>y</strong></em>:</p>
<p>Por ejemplo, para nuestra <strong>primera restricción</strong>:</p>
<p>20<strong>x</strong> + 50<strong>y</strong> &lt;= 3000 <em>(Inecuación)</em></p>
<p>20<strong>x</strong> + 50<strong>y</strong> = 3000 <em>(Ecuación)</em></p>
<p><strong>y</strong> = (3000 &#8211; 20<strong>x</strong>) / 50 <em>(Despejamos <strong>y</strong>)</em></p>
<p><strong>y1</strong> = (3000 &#8211; 20<strong>x</strong>) / 50 <em>(Asignamos un identificador único a <strong>y</strong>)</em></p>
<p>Repetimos el procedimiento para nuestra <strong>segunda restricción</strong>:</p>
<p><strong>x</strong> + <strong>y</strong> &lt;= 90 <em>(Inecuación)</em></p>
<p><strong>x</strong> + <strong>y</strong> = 90 <em>(Ecuación)</em></p>
<p><strong>y</strong> = 90 &#8211; <strong>x</strong> <em>(Despejamos <strong>y</strong>)</em></p>
<p><strong>y2</strong> = 90 &#8211; <strong>x</strong> <em>(Asignamos un identificador único a <strong>y</strong>)</em></p>
<p>En el caso de la <strong>tercera restricción</strong>:</p>
<p><strong>y</strong> &gt;= 10 <em>(Inecuación)</em></p>
<p><strong>y</strong> = 10 <em>(Ecuación)</em></p>
<p>En este caso, dado que la intención es graficar, es fundamental que cada ecuación contenga las dos variables. Dado que en la ecuación original la variable <em><strong>x </strong></em>no hace parte, podemos incluirla multiplicándola por 0. Es decir, para todos los valores que tome <em><strong>x </strong></em>la ecuación permanecerá inalterable.</p>
<p><strong>y</strong> = 10 + (0 * <strong>x</strong>) <em>(Ecuación, <strong>y</strong> ya se encuentra despejada)</em></p>
<p><strong>y3</strong> = 10 + (0 * <strong>x</strong>) <em>(Asignamos un identificador único a <strong>y</strong>)</em></p>
<p>En el caso de la <strong>cuarta restricción</strong>:</p>
<p><strong>y</strong> &gt;= 0 <em>(Inecuación)</em></p>
<p><strong>y</strong> = 0 <em>(Ecuación)</em></p>
<p>En este caso, dado que la intención es graficar, es fundamental que cada ecuación contenga las dos variables. Dado que en la ecuación original la variable <em><strong>x </strong></em>no hace parte, podemos incluirla multiplicándola por 0. Es decir, para todos los valores que tome <em><strong>x </strong></em>la ecuación permanecerá inalterable.</p>
<p><strong>y</strong> = 0 * <strong>x</strong> <em>(Ecuación, <strong>y</strong> ya se encuentra despejada)</em></p>
<p><strong>y4</strong> = 0 * <strong>x</strong> <em>(Asignamos un identificador único a <strong>y</strong>)</em></p>
<p>Todas las ecuaciones anteriores tienen algo en común: Se encuentran despejadas para obtener el valor de <em><strong>y </strong></em>a partir de <em><strong>x</strong></em><em>, </em>de manera que necesitamos alguna función que nos permita asignar diferentes valores a <em><strong>x </strong></em>en un rango dado. Para eso utilizaremos la función <strong><em>np.arange.</em></strong></p>
<p style="text-align: center;"><strong>x </strong>= <strong>np.arange</strong>(-100, 150, 50)</p>
<p>Esta función permite asignar a <em><strong>x</strong></em> diferentes valores de acuerdo a unos argumentos dados (<em>valor mínimo, valor máximo, intervalo o paso</em>). Es decir que de acuerdo a la anterior línea de código, a la variable <em><strong>x </strong></em>le serán asignados diversos valores desde el valor <em>-100 </em>hasta el valor <em>150 </em>con intervalos de <em>50. </em>Así entonces, tomará los siguientes valores:</p>
<table border="0" cellpadding="0" cellspacing="0" width="80" style="border-collapse: collapse; width: 60pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" width="80" style="height: 15pt; width: 60pt; text-align: center;"><em><strong>x</strong></em></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">-100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">-50</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">50</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">150</td>
</tr>
</tbody>
</table>
<p>Dado que las ecuaciones que pretenden hallar <em><strong>y1, y2, y3 </strong></em>y <em><strong>y4 </strong></em>son dependientes de <em><strong>x</strong></em><em>, </em>tomarán sus valores respectivos de acuerdo a los resultados de cada ecuación, para cada valor de <em><strong>x</strong></em>:</p>
<table border="0" cellpadding="0" cellspacing="0" width="400" style="border-collapse: collapse; width: 300pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl66" width="80" style="height: 15pt; width: 60pt;"><em><strong>x</strong></em></td>
<td class="xl66" width="80" style="width: 60pt;"><em><strong>y1</strong></em></td>
<td class="xl66" width="80" style="width: 60pt;"><em><strong>y2</strong></em></td>
<td class="xl66" width="80" style="width: 60pt;"><em><strong>y3</strong></em></td>
<td class="xl66" width="80" style="width: 60pt;"><em><strong>y4</strong></em></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-100</td>
<td class="xl65">100</td>
<td class="xl65">190</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-50</td>
<td class="xl65">80</td>
<td class="xl65">140</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">0</td>
<td class="xl65">60</td>
<td class="xl65">90</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">50</td>
<td class="xl65">40</td>
<td class="xl65">40</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">100</td>
<td class="xl65">20</td>
<td class="xl65">-10</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15pt;">150</td>
<td class="xl65">0</td>
<td class="xl65">-60</td>
<td class="xl65">10</td>
<td class="xl65">0</td>
</tr>
</tbody>
</table>
<p>La última de nuestras restricciones (<strong>quinta restricción</strong>), está dada en función de hallar <em><strong>x </strong></em>a partir <em><strong>y</strong></em>, de manera que efectuamos la misma tarea en un sentido inverso:</p>
<p><strong>x</strong> &gt;= 0 <em>(Inecuación)</em></p>
<p><strong>x</strong> = 0 <em>(Ecuación)</em></p>
<p>En este caso, dado que la intención es graficar, es fundamental que cada ecuación contenga las dos variables. Dado que en la ecuación original la variable <em><strong>y </strong></em>no hace parte, podemos incluirla multiplicándola por 0. Es decir, para todos los valores que tome <em><strong>y </strong></em>la ecuación permanecerá inalterable.</p>
<p><strong>x</strong> = 0 * <strong>y</strong> <em>(Ecuación, <strong>x</strong> ya se encuentra despejada)</em></p>
<p><strong>x1</strong> = 0 * <strong>y</strong> <em>(Asignamos un identificador único a <strong>x</strong>)</em></p>
<p>En este caso, ya que requerimos que <em><strong>y </strong></em>tome diversos valores, utilizaremos la función <em><strong>np.arange.</strong></em></p>
<p style="text-align: center;"><strong>y </strong>= <strong>np.arange</strong>(-100, 150, 50)</p>
<p>Esta función permite asignar a <em><strong>y</strong></em> diferentes valores de acuerdo a unos argumentos dados (<em>valor mínimo, valor máximo, intervalo o paso</em>). Es decir que de acuerdo a la anterior línea de código, a la variable <em><strong>y </strong></em>le serán asignados diversos valores desde el valor <em>-100 </em>hasta el valor <em>150 </em>con intervalos de <em>50. </em>Así entonces, tomará los siguientes valores:</p>
<table border="0" cellpadding="0" cellspacing="0" width="80" style="border-collapse: collapse; width: 60pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" width="80" style="height: 15pt; width: 60pt; text-align: center;"><em><strong>y</strong></em></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">-100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">-50</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">50</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" align="right" style="height: 15pt; text-align: center;">150</td>
</tr>
</tbody>
</table>
<p>Dada que nuestra quinta restricción pretende hallar <em><strong>x1</strong></em> a partir de los valores de <em><strong>y, </strong></em>tendríamos lo siguiente:</p>
<table border="0" cellpadding="0" cellspacing="0" width="160" style="border-collapse: collapse; width: 120pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl66" width="80" style="height: 15.0pt; width: 60pt;"><em><strong>y</strong></em></td>
<td class="xl66" width="80" style="width: 60pt;"><em><strong>x1</strong></em></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-100</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-50</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">0</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">50</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">100</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">150</td>
<td class="xl65">0</td>
</tr>
</tbody>
</table>
<p>Por último, consideramos la inclusión de la <em><strong>función objetivo</strong></em><strong>, </strong>para ello:</p>
<p>10000<strong>x</strong> + 6000<strong>y</strong> = 0 <em>(Ecuación)</em></p>
<p><strong>y</strong> = (- 10000<strong>x</strong>) / 6000 <em>(Despejamos <strong>y</strong>)</em></p>
<p><strong>y5</strong> = (- 10000<strong>x</strong>) / 6000 <em>(Asignamos un identificador único a <strong>y</strong>)</em></p>
<p>En este caso, la función que ya asignamos a <em><strong>x </strong></em>(np.arange), nos prestará los valores necesarios para tabular <em><strong>y5</strong></em>.</p>
<p>Lo que hicimos hasta ahora consiste en explicar con alto grado de detalle la función de cada una las líneas del código que utilizaremos en Python. Todo lo anterior queda reducido al siguiente fragmento:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Ecuaciones e intervalos (Para tabular)
x = np.arange(-100, 150, 50)
y1 = (3000 - (20 * x))/ 50
y2 = 90 - x
y3 = 10 + (0 * x)
y4 = 0 * x
y5 = (-10000 * x) / 6000
y = np.arange(-100, 150, 50)
x1 = 0 * y
</code></pre>
</div>
<h3>Paso 3: Tabular coordenadas e identificar las líneas</h3>

		<div id="paso-3-tabular-coordenadas-e-identificar-las-lineas" data-title="Paso 3: Tabular coordenadas e identificar las líneas" class="index-title"></div>
	
<p>En el paso anterior desarrollamos unas líneas de código que representan los cálculos correspondientes a cada una de las ecuaciones del modelo. Si bien con fines prácticos mostramos la forma en que se tabularían los datos, es hasta este paso en que la información se organiza en tablas (propiamente matrices de dos columnas: <em><strong>x </strong></em>y <em><strong>y</strong></em>). La información se organiza a partir de una instancia básica de la librería <em>Numpy, </em>tal como se explicará a continuación:</p>
<p style="text-align: center;"><strong>np.column_stack</strong>((x, y1))</p>
<p>Esta instancia tomará los valores del <strong><em>paso 1</em></strong> y los tabulará en una matriz 2D (dos columnas). En este ejemplo:</p>
<table border="0" cellpadding="0" cellspacing="0" width="160" style="border-collapse: collapse; width: 120pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl66" width="80" style="height: 15.0pt; width: 60pt;">x</td>
<td class="xl66" width="80" style="width: 60pt;">y1</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-100</td>
<td class="xl65">100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">-50</td>
<td class="xl65">80</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">0</td>
<td class="xl65">60</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">50</td>
<td class="xl65">40</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">100</td>
<td class="xl65">20</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">150</td>
<td class="xl65">0</td>
</tr>
</tbody>
</table>
<p>El siguiente paso consiste en, a partir de una instancia básica de la librería <em>Shapely, </em>generar la línea correspondiente a cada ecuación de acuerdo al tabulado generado, tal como se aprecia a continuación:</p>
<p style="text-align: center;"><strong>LineString</strong>(<strong>np.column_stack</strong>((x, y1)))</p>
<p><em>LineString</em> es una instancia de la librería <em>Shapely </em>que permite unir cada punto (coordenada), de manera que genera una línea. Ahora bien, cada línea de código deberá corresponder o asociarse a una variable única que nos permitirá identificar cada una de las líneas, por ejemplo:</p>
<p style="text-align: center;"><strong>primera_línea</strong> = <strong>LineString</strong>(<strong>np.column_stack</strong>((x, y1)))</p>
<p>Realizamos el mismo procedimiento para cada una de las líneas, siendo cuidadosos al momento de asignar las variables asociadas a cada una de las líneas:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Identificadores para las líneas
primera_linea = LineString(np.column_stack((x, y1)))
segunda_linea = LineString(np.column_stack((x, y2)))
tercera_linea = LineString(np.column_stack((x, y3)))
cuarta_linea = LineString(np.column_stack((x1, y)))
quinta_linea = LineString(np.column_stack((x, y4)))
sexta_linea = LineString(np.column_stack((x, y5)))
</code></pre>
</div>
<p>Podemos observar como cada conjunto de variables corresponde estrictamente a cada una de las líneas asociadas a cada ecuación en particular.</p>
<h3>Paso 4: Graficar las líneas</h3>

		<div id="paso-4-graficar-las-lineas" data-title="Paso 4: Graficar las líneas" class="index-title"></div>
	
<p>En este paso vamos a utilizar la librería <em>Matplotlib </em>para graficar nuestras líneas (ecuaciones).</p>

		<div class="clearfix"></div>
		<div class="toggle tie-sc-close">
			<h3 class="toggle-head">Colores de línea <span class="fa fa-angle-down" aria-hidden="true"></span></h3>
			<div class="toggle-content">
<table class="table table-bordered table-striped">
<tbody>
<tr>
<th style="text-align: center;">Alias</th>
<th style="text-align: center;">Color</th>
</tr>
</tbody>
<tbody>
<tr>
<td style="text-align: center;">b</td>
<td style="text-align: center;">Azul</td>
</tr>
<tr>
<td style="text-align: center;">g</td>
<td style="text-align: center;">Verde</td>
</tr>
<tr>
<td style="text-align: center;">r</td>
<td style="text-align: center;">Rojo</td>
</tr>
<tr>
<td style="text-align: center;">c</td>
<td style="text-align: center;">Cyan</td>
</tr>
<tr>
<td style="text-align: center;">m</td>
<td style="text-align: center;">Magenta</td>
</tr>
<tr>
<td style="text-align: center;">y</td>
<td style="text-align: center;">Amarillo</td>
</tr>
<tr>
<td style="text-align: center;">k</td>
<td style="text-align: center;">Negro</td>
</tr>
<tr>
<td style="text-align: center;">w</td>
<td style="text-align: center;">Blanco</td>
</tr>
</tbody>
</table>

			</div>
		</div>
	

		<div class="clearfix"></div>
		<div class="toggle tie-sc-close">
			<h3 class="toggle-head">Tipos de línea <span class="fa fa-angle-down" aria-hidden="true"></span></h3>
			<div class="toggle-content">
<table class="table table-bordered table-striped">
<tbody>
<tr>
<th style="text-align: center;">Estilo de línea</th>
</tr>
</tbody>
<tbody>
<tr>
<td style="text-align: center;">&#8211;</td>
<td><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/linea1-150x10.jpg" alt="" width="150" height="10" class="aligncenter size-thumbnail wp-image-26201" /></td>
</tr>
<tr>
<td style="text-align: center;">&#8211; &#8211;</td>
<td><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/linea2-150x10.jpg" alt="" width="150" height="10" class="aligncenter size-thumbnail wp-image-26202" /></td>
</tr>
<tr>
<td style="text-align: center;">:</td>
<td><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/linea3-150x10.jpg" alt="línea3" width="150" height="10" class="aligncenter size-thumbnail wp-image-26203" /></td>
</tr>
<tr>
<td style="text-align: center;">: &#8211;</td>
<td><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/linea4-150x10.jpg" alt="línea4" width="150" height="10" class="aligncenter size-thumbnail wp-image-26204" /></td>
</tr>
</tbody>
</table>

			</div>
		</div>
	
<p>El argumento <em>linewidth </em>determinará el grosor de la línea. Debemos considerar asignar las mismas variables que en el paso anterior.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Graficando las líneas
plt.plot(x, y1, '-', linewidth=2, color='b')
plt.plot(x, y2, '-', linewidth=2, color='g')
plt.plot(x, y3, '-', linewidth=2, color='r')
plt.plot(x1, y, '-', linewidth=2, color='y')
plt.plot(x, y4, '-', linewidth=2, color='k')
plt.plot(x, z, ':', linewidth=1, color='k')
</code></pre>
</div>
<p>Ya en este paso tenemos lo suficiente para esbozar nuestras líneas, sin embargo es necesario crear algunas configuraciones generales de nuestra gráfica, y aunque estas líneas de código van al cierre, nos adelantaremos para ir conociendo la evolución parcial de nuestro desarrollo.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Configuraciones adicionales del gráfico
plt.grid()
plt.xlabel('Asientos para fumadores')
plt.ylabel('Asientos para no fumadores')
plt.title('Método Gráfico')

plt.show()
</code></pre>
</div>
<p>Estos ajustes permiten asignar los títulos de los ejes, así como el título general del gráfico, así mismo especifica la función que permite que el resultado del código se muestre como imagen.</p>
<p>En este paso queremos ejecutar el código parcial. Nosotros utilizaremos <em>Google Colaboratory</em>, puedes ver la ejecución en nuestro cuaderno: <a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>.</p>
<figure id="attachment_26453" aria-describedby="caption-attachment-26453" style="width: 397px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-4_Ecuaciones-representadas-en-lineas-Metodo-grafico.png" alt="Figura 4: Ecuaciones representadas en líneas (Método gráfico)" width="397" height="278" class="size-full wp-image-26453" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-4_Ecuaciones-representadas-en-lineas-Metodo-grafico.png 397w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-4_Ecuaciones-representadas-en-lineas-Metodo-grafico-300x210.png 300w" sizes="(max-width: 397px) 100vw, 397px" /><figcaption id="caption-attachment-26453" class="wp-caption-text">Figura 4: Ecuaciones representadas en líneas (Método gráfico)</figcaption></figure>
<p>&nbsp;</p>
<p>De manera que, de acuerdo al código ejecutado, nuestro programa es capaz de esbozar gráficamente cada una de nuestras ecuaciones sobre el plano cartesiano. Ya que incluso tenemos una representación de la función objetivo (línea punteada), e infiriendo el polígono factible (de acuerdo a la naturaleza de las restricciones: &lt;= o &gt;=), podemos geométricamente establecer cual es el vértice solución.</p>
<p>Ahora bien, podemos identificar que en la gráfica se pueden apreciar algunos polígonos, pero: <em>¿Cuál es nuestro polígono factible? </em>Bien, para eso debemos considerar la naturaleza de las restricciones; en cuyo caso, cada línea trazada dividirá el cuadrante solución en dos semiplanos: uno a cada lado de la línea trazada. Solo una de estas dos mitades satisface cada desigualdad, de manera que debemos considerar si cada restricción es «menor o igual», «mayor o igual», o en su defecto «igual». Por ejemplo:</p>
<figure id="attachment_26216" aria-describedby="caption-attachment-26216" style="width: 491px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/semiplanos.jpg" alt="semiplanos" width="491" height="322" class="wp-image-26216" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/semiplanos.jpg 563w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/semiplanos-300x197.jpg 300w" sizes="(max-width: 491px) 100vw, 491px" /><figcaption id="caption-attachment-26216" class="wp-caption-text">Figura 5: Semiplanos obtenidos a partir de una línea de ecuación</figcaption></figure>
<p>En la anterior gráfica (Figura 5) podemos observar la línea que representa la segunda restricción. Así mismo, podemos observar el concepto de semiplanos: <em><strong>A</strong> y <strong>B</strong> </em>(Uno a cada lado de la línea trazada). Como se trata de una restricción «menor o igual» (líneas hacia abajo y hacia la izquierda); solo la mitad <em><strong>A </strong></em>satisface la desigualdad.</p>
<figure id="attachment_26209" aria-describedby="caption-attachment-26209" style="width: 614px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico2.jpg" alt="metodo_grafico2" width="614" height="460" class="wp-image-26209 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico2.jpg 614w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico2-300x225.jpg 300w" sizes="(max-width: 614px) 100vw, 614px" /><figcaption id="caption-attachment-26209" class="wp-caption-text">Figura 6: Polígono factible</figcaption></figure>
<p>En este caso representamos con flechas el lado, es decir la mitad que divide cada línea trazada, en el cual se encuentran los valores que satisfacen cada restricción. Las restricciones «menores o igual» apuntarán sus flechas hacia abajo y/o a la izquierda; los «mayores o igual» apuntarán sus flechas hacia arriba y/o a la derecha. Así entonces encontramos con suma facilidad nuestro polígono factible (zona gris).</p>
<h3>Paso 5: Determinar y graficar las intersecciones</h3>

		<div id="paso-5-determinar-y-graficar-las-intersecciones" data-title="Paso 5: Determinar y graficar las intersecciones" class="index-title"></div>
	
<p>Algunos textos, como por ejemplo <em>Investigación de Operaciones de </em><em>Taha</em>, consideran que los dos pasos fundamentales de la solución gráfica consisten en: determinar el espacio de soluciones factibles y determinar la solución óptima entre todos los puntos localizados en el espacio de soluciones.</p>
<p>Consideremos que el área gris de la Figura 6 corresponde al polígono o espacio de soluciones factibles. Todos los puntos (coordenadas en x, y) que se encuentran en este espacio, satisfacen al mismo tiempo todas las restricciones del modelo. Los puntos que se encuentra fuera, satisfacen algunas o ninguna de las restricciones.</p>
<p>Ahora bien, la cantidad de puntos dentro del espacio de soluciones factibles es sumamente grande, sin embargo, hemos de recordar que este método se rige bajo criterios de optimización. En nuestro ejemplo, este criterio es: <em>maximizar</em>. Recordemos que en programación lineal, la solución óptima siempre está asociada con un punto de esquina o vértice del polígono factible. Y que estos vértices se generan en cada punto de intersección de dos o más líneas, es decir, en el punto en que se igualan dos o más ecuaciones.</p>
<p>Lo anterior es sumamente importante, ya que para efectos de nuestro desarrollo en Python, podemos utilizar alguna función básica que, dadas las líneas, nos represente las intersecciones. En la práctica, determinar el punto de intersección de dos líneas requiere de la solución de un sistema de ecuaciones de 2 x 2, ya sea por el método de <em>sustitución, igualación, eliminación</em>, etc.</p>
<p>En nuestro caso, vamos a utilizar la función <strong>intersection</strong>() de Python. Lo que debemos hacer es determinar que intersecciones queremos determinar y graficar. Por ejemplo:</p>
<p>Si queremos determinar la intersección de la línea amarilla (cuarta línea, así la nombramos en el paso anterior) y la línea azul (primera línea), debemos escribir el siguiente código:</p>
<p style="text-align: center;">primera_interseccion = cuarta_linea.<strong>intersection</strong>(primera_linea)</p>
<p>Esta línea de código creará una variable llamada <em>primera_interseccion</em> que estará definida por la intersección entre la <em>cuarta línea</em> y la <em>primera línea</em>. El siguiente fragmento creará las intersecciones restantes del polígono solución (consideramos que el orden es importante, así que las creamos de acuerdo a las manecillas del reloj).</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Generando las intersecciones (vértices)
primera_interseccion = cuarta_linea.intersection(primera_linea)
segunda_interseccion = primera_linea.intersection(segunda_linea)
tercera_interseccion = segunda_linea.intersection(tercera_linea)
cuarta_interseccion = tercera_linea.intersection(cuarta_linea)
</code></pre>
</div>
<p>El espacio de soluciones factibles tienen cuatro intersecciones, de acuerdo al código anterior quedan definidas de acuerdo a los nombres que le asignamos a las variables. Ahora, el paso siguiente consiste en graficar dichas restricciones.</p>
<p>Acá pueden encontrar el listado con los tipos de marcadores (símbolos) que pueden utilizar: <a href="https://matplotlib.org/stable/api/markers_api.html"><em><strong>marcadores pyplot</strong></em></a>. Nosotros utilizaremos un marcador en círculo (representado así en el código: «o»).</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Graficando los vértices
plt.plot(*primera_interseccion.xy, 'o', color='k')
plt.plot(*segunda_interseccion.xy, 'o', color='k')
plt.plot(*tercera_interseccion.xy, 'o', color='k')
plt.plot(*cuarta_interseccion.xy, 'o', color='k')
</code></pre>
</div>
<p>Así entonces, podemos ejecutar nuestro código (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) nuevamente y veremos:</p>
<figure id="attachment_26454" aria-describedby="caption-attachment-26454" style="width: 397px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-7_-Vertices-del-espacio-de-solucion-factible.png" alt="Figura 7: Vértices del espacio de solución factible" width="397" height="278" class="wp-image-26454 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-7_-Vertices-del-espacio-de-solucion-factible.png 397w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-7_-Vertices-del-espacio-de-solucion-factible-300x210.png 300w" sizes="(max-width: 397px) 100vw, 397px" /><figcaption id="caption-attachment-26454" class="wp-caption-text">Figura 7: Vértices del espacio de solución factible</figcaption></figure>
<p>Como podemos observar en la Figura 7, hemos logrado graficar los vértices del polígono factible.</p>
<p>Ahora es cuando debemos preguntarnos: <em>¿Qué más necesitamos? </em>Pues bien, si bien gráficamente podemos determinar el vértice solución (basta con trasladar paralelamente la línea punteada hasta cada vértice e identificar en cual de ellos la línea no corta el polígono solución); sería importante determinar el valor de las coordenadas en cada uno de los vértices, ya que con ello podemos evaluar la ecuación de la función objetivo para cada punto de esquina.</p>
<p>Lo que necesitamos es el valor numérico de cada punto, y la verdad, ya lo tenemos dentro de nuestras variables; lo que debemos hacer es imprimirlo en la consola de Windows, para eso podemos utilizar el siguiente fragmento:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Imprimiendo las coordenadas de los vértices en la consola
print('\n COORDENADAS DE LAS INTERSECCIONES')
print('Coordenadas de la primera intersección: {} '.format(primera_interseccion))
print('Coordenadas de la segunda intersección: {} '.format(segunda_interseccion))
print('Coordenadas de la tercera intersección: {} '.format(tercera_interseccion))
print('Coordenadas de la cuarta intersección: {} '.format(cuarta_interseccion))
</code></pre>
</div>
<p>Bien podíamos utilizar tan solo la función <strong>print</strong>() en su versión más simple, por ejemplo:</p>
<p style="text-align: center;"><strong>print</strong>(primera_interseccion)</p>
<p>Esto serviría para obtener el punto de la primera intersección con sus dos coordenadas (<em><strong>x</strong></em>, <em><strong>y</strong></em>). Sin embargo, hemos utilizado algún texto que identifique con precisión la información que estamos generando. En este caso utilizamos un poco de texto, dentro de las llaves {} el programa introducirá el valor de cada variable. Esta es una cuestión accesoria y de estilo, siéntanse libres de explorar con el código. Ejecutamos (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) y, junto a la imagen, tendremos el siguiente resultado:</p>
<figure id="attachment_26455" aria-describedby="caption-attachment-26455" style="width: 533px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-8_Valores-de-las-coordenadas-en-cada-vertice.png" alt="Figura 8: Valores de las coordenadas en cada vértice" width="533" height="124" class="wp-image-26455 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-8_Valores-de-las-coordenadas-en-cada-vertice.png 533w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-8_Valores-de-las-coordenadas-en-cada-vertice-300x70.png 300w" sizes="(max-width: 533px) 100vw, 533px" /><figcaption id="caption-attachment-26455" class="wp-caption-text">Figura 8: Valores de las coordenadas en cada vértice</figcaption></figure>
<p>&nbsp;</p>
<p>De esta manera hemos obtenido los valores de los puntos de cada vértice.</p>
<h3>Paso 6: Determinar la solución óptima</h3>

		<div id="paso-6-determinar-la-solucion-optima" data-title="Paso 6: Determinar la solución óptima" class="index-title"></div>
	
<p>Básicamente el objetivo de la solución gráfica se logra en este paso, es decir, hallar la solución óptima en términos de la función objetivo y los valores de las variables de decisión. Con un poco de lógica podemos inferir que en el punto en el que estamos, esta es una cuestión sencilla. Lo único que debemos hacer es, de acuerdo a los valores de cada vértice, evaluar la ecuación de la función objetivo en cada uno de ellos, de manera que podamos determinar cual es el mejor resultado (de acuerdo al criterio de optimización, en este caso: <em>maximizar</em>).</p>
<p>Ya que nos encontramos trabajando en <em>Python</em>, es necesario aclarar que el valor de los vértices obtenidos corresponde a un formato de tipo <em>point </em>(punto), y que básicamente, no es posible operar cada una de las variables (<em>x, y) </em>que contiene sin antes extraerlas. Por esa razón, lo siguiente que debemos hacer es extraer el valor de cada variable contenida en cada punto de manera independiente:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Identificando los valores de las coordenadas (x, y) de cada vértice
xi1m, yi1m = primera_interseccion.xy
xi2m, yi2m = segunda_interseccion.xy
xi3m, yi3m = tercera_interseccion.xy
xi4m, yi4m = cuarta_interseccion.xy
</code></pre>
</div>
<p>El anterior código acaba de generar un total de 8 variables, cada una de ellas contendrá la información correspondiente a la proyección de cada eje sobre cada punto, por ejemplo: <em><strong>xi1m</strong></em><em> </em>contendrá la información correspondiente al valor que tienen el punto de la primera intersección sobre el eje <em><strong>x</strong></em>. Es decir, acabamos de extraer las variables <em><strong>x</strong></em> y <em><strong>y </strong></em>de cada uno de los puntos de intersección.</p>
<p>Sin embargo, ese valor extraído se encuentra en un formato de matriz y contiene más información que la que necesitamos, de hecho por eso le colocamos la letra <em><strong>m</strong>, </em>para reconocer que es una matriz. Lo que debemos hacer es cambiar el formato de los valores extraídos. Formas existen muchas, nosotros utilizaremos la siguiente:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Cambiamos el formato de la variable de matriz a real
xi1 = np.float64(np.array(xi1m))
xi2 = np.float64(np.array(xi2m))
xi3 = np.float64(np.array(xi3m))
xi4 = np.float64(np.array(xi4m))
yi1 = np.float64(np.array(yi1m))
yi2 = np.float64(np.array(yi2m))
yi3 = np.float64(np.array(yi3m))
yi4 = np.float64(np.array(yi4m))
</code></pre>
</div>
<p><em>float 64</em> es un formato que corresponde a números reales, es decir: números que pueden ser positivos o negativos con decimales. En este punto tenemos las variables correspondientes a cada uno de los valores de los vértices en los ejes <em><strong>x </strong></em>y <em><strong>y</strong></em>. Lo siguiente será evaluar la ecuación objetivo en cada uno de los vértices, es decir, en las coordenadas de cada intersección. Por ejemplo:</p>
<p style="text-align: center;"><strong>FOi1</strong> = (<strong>xi1</strong> * 10000) + (<strong>yi1</strong> * 6000)</p>
<p>Creamos la variable <em><strong>FOi1</strong></em>, es decir: La función objetivo en la intersección 1 (vértice 1). La cual multiplicará los coeficientes de la ecuación objetivo por el valor de la variable <strong>xi1 </strong>y <strong>yi1</strong>, es decir, las coordenadas <strong>x </strong>y <strong>y </strong>en el vértice 1. En este caso, y ya que conocemos estos valores (los hemos impreso en la consola), generaría el siguiente resultado:</p>
<p style="text-align: center;"><strong>FOi1</strong> = (<strong>0</strong> * 10000) + (<strong>60</strong> * 6000)</p>
<p style="text-align: center;"><strong>FOi1</strong> = 360000</p>
<p>Lo que equivale a decir que el resultado de la función objetivo en el vértice 1 es igual a 360.000. Así entonces, generamos el fragmento de código de estas ecuaciones y variables, así como las funciones que permitan que estos valores se impriman en la consola:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Evaluando la función objetivo en cada vértice
FOi1 = (xi1 * 10000) + (yi1 * 6000)
FOi2 = (xi2 * 10000) + (yi2 * 6000)
FOi3 = (xi3 * 10000) + (yi3 * 6000)
FOi4 = (xi4 * 10000) + (yi4 * 6000)

#Imprimiendo las evaluaciones de la FO en cada vértice (Consola)
print('\n EVALUACIÓN DE LA FO EN LOS VÉRTICES')
print('Función objetivo en la intersección 1: {} pesos'.format(FOi1))
print('Función objetivo en la intersección 2: {} pesos'.format(FOi2))
print('Función objetivo en la intersección 3: {} pesos'.format(FOi3))
print('Función objetivo en la intersección 4: {} pesos'.format(FOi4))
</code></pre>
</div>
<p>Ejecutamos nuestro código (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>), y tendremos, además de la gráfica, el siguiente resultado:</p>
<figure id="attachment_26456" aria-describedby="caption-attachment-26456" style="width: 519px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-9_Funcion-objetivo-en-cada-uno-de-los-vertices.png" alt="Figura 9: Función objetivo en cada uno de los vértices" width="519" height="124" class="size-full wp-image-26456" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-9_Funcion-objetivo-en-cada-uno-de-los-vertices.png 519w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-9_Funcion-objetivo-en-cada-uno-de-los-vertices-300x72.png 300w" sizes="(max-width: 519px) 100vw, 519px" /><figcaption id="caption-attachment-26456" class="wp-caption-text">Figura 9: Función objetivo en cada uno de los vértices</figcaption></figure>
<p>&nbsp;</p>
<p>A esta altura debemos suponer que estamos muy cerca de lograr que nuestro desarrollo determine, según el criterio de optimización, la solución óptima. Ya que tenemos los valores de la función objetivo en cada uno de los vértices, será cuestión de determinar su valor máximo o mínimo, según se el caso. En nuestro problema, requerimos determinar el valor máximo; para eso utilizamos una función básica de Python:</p>
<p style="text-align: center;"><strong>ZMAX </strong>= <strong>max</strong>(FOi1, FOi2, FOi3, FOi4)</p>
<p>Creamos la variable <em><strong>ZMAX</strong></em>, es decir: La variable que contendrá el valor máximo entre las variables que contienen los valores de la función objetivo en cada vértice. Así entonces obtendremos de una muy sencilla, nuestra solución óptima. El siguiente código creará la variable y la función que permitirá que se imprima dicho resultado en la consola:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Calculando el mejor resultado (Maximizar)
ZMAX = max(FOi1, FOi2, FOi3, FOi4)

#Imprimiendo la solución óptima en la consola
print('\n SOLUCIÓN ÓPTIMA')
print('Solución óptima: {} pesos'.format(ZMAX))
</code></pre>
</div>
<p>Ejecutamos nuestro código (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) y tendremos, además de la gráfica, el siguiente resultado:</p>
<figure id="attachment_26457" aria-describedby="caption-attachment-26457" style="width: 478px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-10_Solucion-optima.png" alt="Figura 10: Solución óptima" width="478" height="70" class="size-full wp-image-26457" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-10_Solucion-optima.png 478w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Figura-10_Solucion-optima-300x44.png 300w" sizes="(max-width: 478px) 100vw, 478px" /><figcaption id="caption-attachment-26457" class="wp-caption-text">Figura 10: Solución óptima</figcaption></figure>
<p>En este punto nuestro desarrollo permite que obtengamos, además de la gráfica, la solución óptima. <em>¿Qué más podemos necesitar?</em> Pues bien, necesitamos como mínimo imprimir el valor de las variables de decisión, es decir, el valor de las coordenadas <em><strong>x </strong></em>y <em><strong>y </strong></em>en el vértice donde se encuentra la solución óptima (punto óptimo). En <em>Python </em>pueden existir muchas alternativas para obtener esta información, nosotros utilizaremos el concepto de directorio.</p>
<p>Lo primero que vamos a hacer es organizar la información, es decir, crear un vector que almacene los valores de cada variable (sea <em><strong>x</strong> </em>o <strong><em>y</em></strong>) en todos los vértices, por ejemplo:</p>
<p style="text-align: center;"><strong>m</strong> = [<strong>xi1</strong>, <strong>xi2</strong>, <strong>xi3</strong>, <strong>xi4</strong>]
<p>El vector <em><strong>m </strong></em>contendrá los valores (ordenados) de las variables <em><strong>x </strong></em>en cada uno de los vértices, empezando por el vértice 1 y finalizando en el vértice 4. Lo mismo realizamos con las variables <em><strong>y</strong></em>.</p>
<p style="text-align: center;"><strong>n</strong> = [<strong>yi1</strong>, <strong>yi2</strong>, <strong>yi3</strong>, <strong>yi4</strong>]
<p>En este punto, y con la información tal cual como tenemos organizada, podemos realizar un pequeño aporte al gráfico: rellenar nuestro polígono solución. Para eso, utilizamos una función llamada <em>fill</em>, y nuestros vectores recién creados:</p>
<p style="text-align: center;">plt.<strong>fill</strong>(<strong>m</strong>, <strong>n</strong>, color=&#8217;silver&#8217;)</p>
<p>Esta función utiliza los vectores <em><strong>m </strong></em>y <em><strong>n </strong></em>para crear un relleno en el gráfico al desplazarse por los puntos de los vectores (coordenadas), en sentido de las manecillas del reloj. Es decir, iniciará en el vértice 1 y finalizará en el vértice 4. Además, hemos especificado que el color de este relleno sea <em>silver:</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Ordenando las coordenadas de los vértices (Las coordenadas x en m y las coordenadas y en n)
m = [xi1, xi2, xi3, xi4]
n = [yi1, yi2, yi3, yi4]

#Graficando el polígono solución a partir de las coordenadas de los vértices 
plt.fill(m, n, color='silver')
</code></pre>
</div>
<p>Ejecutamos nuestro código (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) y tendremos la siguiente gráfica:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico3.png" alt="método_gráfico3" width="640" height="480" class="aligncenter wp-image-26223 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico3.png 640w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico3-300x225.png 300w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>Ahora podemos apreciar el espacio de soluciones factibles desde la gráfica que nos exporta el programa.</p>
<p>Volvamos a nuestro directorio. Lo que vamos a hacer es crear un arreglo con las variables que contienen los resultados de las funciones objetivo (<em><strong>FOi1, FOi2, FOi3 </strong></em>y <em><strong>FOi4</strong></em>), y a cada uno le asignaremos un índice de acuerdo a su orden (ya veremos para qué). Por ejemplo:</p>
<p style="text-align: center;"><strong>dict1</strong> = {0:<strong>FOi1</strong>, 1:<strong>FOi2</strong>, 2:<strong>FOi3</strong>, 3:<strong>FOi4</strong>}</p>
<p>Así entonces, a cada variable le asignamos un número inicia en 0 y finaliza en 3 (ya veremos para qué). La variable <em><strong>dict</strong></em> hace referencia a nuestro directorio, y dentro de él se encuentra el arreglo con nuestros índices.</p>
<p>Lo siguiente que haremos es extraer el índice, no la variable de este arreglo, de acuerdo al mayor valor de la variable, no del índice. Es decir, una función que evalúe las variables de las funciones objetivo, y en lugar de devolvernos dicho valor, nos traiga el índice que tiene al lado. Nosotros de antemano conocemos que el mayor valor es el de <em><strong>FOi3</strong></em>, por esta razón, la función debe arrojarnos como respuesta el índice <em><strong>2</strong></em> (2:<strong>FOi3</strong>). <em>¿Por qué no iniciamos desde 1 y sí desde 0? </em>En Python, las posiciones dentro de un arreglo inician desde 0.</p>
<p>La siguiente función nos traerá el índice de acuerdo al mayor valor de la variable:</p>
<p style="text-align: center;"><strong>posicion</strong> = <strong>max</strong>(<strong>dict1</strong>, key=dict1.get)</p>
<p>Creamos la variable <em><strong>posicion </strong></em>y en ella se almacenará el índice asociado al mayor valor de la variable del arreglo. <em>¿Y esto para qué nos sirve? </em>Pues bien, recuerdan que tenemos un par de vectores donde se almacenan las variables <em><strong>x </strong></em>y <em><strong>y </strong></em>de cada uno de los vértices (y que hicimos énfasis en ordenarlos). Con la variable <em><strong>posicion</strong></em> podemos acceder a la variable específica dentro de estos vectores, es decir, a la posición en la que se encuentra cada una de las variables asociadas al vértice solución. <em>¿Podemos plantearlo de una manera más sencilla? </em>Vamos a intentarlo:</p>
<p>Según de la manera ordenada en la que hemos venido trabajando el código, podemos inferir que si la función objetivo solución se encuentra en la variable <em><strong>FOi3</strong></em> sus coordenadas serán <em><strong>xi3 </strong></em>y <em><strong>y13</strong></em>. Así entonces, si tenemos la posición de la función objetivo dentro de un arreglo (<em><strong>dict1</strong></em>)<strong></strong><em><strong>,</strong></em> esa misma posición corresponderá a la de cada variable dentro del vector que la contenga (el vector <em><strong>m </strong></em>contiene las variables <em><strong>x</strong></em> de los vértices, mientras el vector <em><strong>n </strong></em>contiene las variables <em><strong>y</strong></em> de los vértices).</p>
<p>Así entonces, el valor de las coordenadas del vértice solución la obtenemos de la siguiente forma:</p>
<p style="text-align: center;"><strong>XMAX</strong> = <strong>m</strong>[<strong>posicion</strong>]
<strong>YMAX</strong> = <strong>n</strong>[<strong>posicion</strong>]
<p>La variable <em><strong>XMAX</strong></em> tomará el valor de la variable <em><strong>x</strong></em> que se encuentra en el vector <em><strong>m </strong></em>en la posición de la variable <em><strong>posicion</strong></em>. Es decir, corresponderá al valor de la variable <em><strong>x</strong></em> en el vértice solución. Veamos:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Identificando el índice del vértice de la mejor solución
dict1 = {0:FOi1, 1:FOi2, 2:FOi3, 3:FOi4}
posicion = max(dict1, key=dict1.get)

#Obteniendo las coordenadas del vértice de la mejor solución de acuerdo al índice
XMAX = m[posicion]
YMAX = n[posicion]

#Imprimiendo las coordenadas del vértice de la mejor solución (variables de decisión)
print('\n VARIABLES DE DECISIÓN')
print('Cantidad de asientos a reservar para fumadores: {} '.format(XMAX))
print('Cantidad de asientos a reservar para no fumadores: {} '.format(YMAX))
</code></pre>
</div>
<p>En el anterior fragmento, bien pudimos utilizar la función <strong>print</strong>(<strong>XMAX</strong>) de una manera sencilla. Sin embargo, le dimos un formato que permitiera darle un nombre a las variables de acuerdo al problema (variables de decisión).</p>
<p>Ejecutamos nuestro código y tendremos, además de la gráfica, el siguiente resultado en la consola:</p>
<figure id="attachment_26458" aria-describedby="caption-attachment-26458" style="width: 591px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Variables-de-decision.png" alt="Variables de decisión" width="591" height="89" class="size-full wp-image-26458" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Variables-de-decision.png 591w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/Variables-de-decision-300x45.png 300w" sizes="(max-width: 591px) 100vw, 591px" /><figcaption id="caption-attachment-26458" class="wp-caption-text">Variables de decisión</figcaption></figure>
<p>En este punto podemos decir que nuestro código tiene la capacidad de esbozar una solución gráfica del problema de programación lineal. Al mismo tiempo nos permite obtener información básica relevante.</p>
<p>Podemos hacer algunos ajustes accesorios, todos a elección del desarrollador. En este caso, vamos a agregar un par de líneas de código que permitan adicionar al gráfico un par de anotaciones con las coordenadas del vértice solución y el valor la solución óptima. Veamos:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Generando las anotaciones de las coordenadas y solución óptima en el gráfico
plt.annotate('  X: {0} / Y: {1} (Coordenadas)'.format(XMAX, YMAX), (XMAX, YMAX))
plt.annotate('  Solución óptima: {}'.format(ZMAX), (XMAX, YMAX+3))
</code></pre>
</div>
<p>Ejecutamos nuestro código (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) y tendremos la siguiente gráfica:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico4.png" alt="método_gráfico4" width="656" height="563" class="aligncenter wp-image-26225 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico4.png 656w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/metodo_grafico4-300x257.png 300w" sizes="(max-width: 656px) 100vw, 656px" /></p>
<p>También podemos eliminar las líneas rectas, para ello anteponemos # en la línea que ordena imprimir cada una de las ecuaciones. Ejecutamos (<a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><em><strong>Método Gráfico</strong></em></a>) y tendremos:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/descarga.png" alt="poligono solucion factible" width="528" height="278" class="aligncenter size-full wp-image-26459" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/descarga.png 528w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/07/descarga-300x158.png 300w" sizes="(max-width: 528px) 100vw, 528px" /></p>
<hr />
<p>Así entonces, hemos finalizado con la construcción de nuestro código para efectuar una solución gráfica de un modelo de programación lineal. Este puede, con ligeras modificaciones, ajustarse a cualquier problema susceptible de ser solucionado mediante método gráfico.</p>
<hr />
<p>El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1EFa3Fb2nAB7ws6faGc73Me8KAhjYKqXM?usp=sharing"><strong>Método Gráfico</strong></a>.</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>#Autor: Bryan Salazar López, Ing. M.Sc. (2021)
#Librerías necesarias
import matplotlib.pyplot as plt
import numpy as np
from shapely.geometry import LineString

#Ecuaciones e intervalos (Para tabular)
x = np.arange(-100, 150, 50)
y = np.arange(-100, 150, 50)
y1 = (3000 - (20 * x))/ 50
y2 = 90 - x
y3 = 10 + (0 * x)
x1 = 0 * y
y4 = 0 * x
z = (-10000 * x) / 6000

#Identificadores para las líneas
primera_linea = LineString(np.column_stack((x, y1)))
segunda_linea = LineString(np.column_stack((x, y2)))
tercera_linea = LineString(np.column_stack((x, y3)))
cuarta_linea = LineString(np.column_stack((x1, y)))
quinta_linea = LineString(np.column_stack((x, y4)))
sexta_linea = LineString(np.column_stack((x, z)))

#Graficando las líneas
plt.plot(x, y1, '-', linewidth=2, color='b')
plt.plot(x, y2, '-', linewidth=2, color='g')
plt.plot(x, y3, '-', linewidth=2, color='r')
plt.plot(x1, y, '-', linewidth=2, color='y')
plt.plot(x, y4, '-', linewidth=2, color='k')
plt.plot(x, z, ':', linewidth=1, color='k')

#Generando las intersecciones (vértices)
primera_interseccion = cuarta_linea.intersection(primera_linea)
segunda_interseccion = primera_linea.intersection(segunda_linea)
tercera_interseccion = segunda_linea.intersection(tercera_linea)
cuarta_interseccion = tercera_linea.intersection(cuarta_linea)

#Graficando los vértices
plt.plot(*primera_interseccion.xy, 'o')
plt.plot(*segunda_interseccion.xy, 'o')
plt.plot(*tercera_interseccion.xy, 'o')
plt.plot(*cuarta_interseccion.xy, 'o')

#Imprimiendo las coordenadas de los vértices en la consola
print('\n COORDENADAS DE LAS INTERSECCIONES')
print('Coordenadas de la primera intersección: {} '.format(primera_interseccion))
print('Coordenadas de la segunda intersección: {} '.format(segunda_interseccion))
print('Coordenadas de la tercera intersección: {} '.format(tercera_interseccion))
print('Coordenadas de la cuarta intersección: {} '.format(cuarta_interseccion))

#Identificando los valores de las coordenadas x y y de cada vértice
xi1m, yi1m = primera_interseccion.xy
xi2m, yi2m = segunda_interseccion.xy
xi3m, yi3m = tercera_interseccion.xy
xi4m, yi4m = cuarta_interseccion.xy

#Cambiamos el formato de matriz a float
xi1 = np.float64(np.array(xi1m))
xi2 = np.float64(np.array(xi2m))
xi3 = np.float64(np.array(xi3m))
xi4 = np.float64(np.array(xi4m))
yi1 = np.float64(np.array(yi1m))
yi2 = np.float64(np.array(yi2m))
yi3 = np.float64(np.array(yi3m))
yi4 = np.float64(np.array(yi4m))

#Evaluando la función objetivo en cada vértice
FOi1 = (xi1 * 10000) + (yi1 * 6000)
FOi2 = (xi2 * 10000) + (yi2 * 6000)
FOi3 = (xi3 * 10000) + (yi3 * 6000)
FOi4 = (xi4 * 10000) + (yi4 * 6000)

#Imprimiendo las evaluaciones de la FO en cada vértice (Consola)
print('\n EVALUACIÓN DE LA FO EN LOS VÉRTICES')
print('Función objetivo en la intersección 1: {} pesos'.format(FOi1))
print('Función objetivo en la intersección 2: {} pesos'.format(FOi2))
print('Función objetivo en la intersección 3: {} pesos'.format(FOi3))
print('Función objetivo en la intersección 4: {} pesos'.format(FOi4))

#Calculando el mejor resultado (Maximizar)
ZMAX = max(FOi1, FOi2, FOi3, FOi4)

#Imprimiendo la solución óptima en la consola
print('\n SOLUCIÓN ÓPTIMA')
print('Solución óptima: {} pesos'.format(ZMAX))

#Ordenando las coordenadas de los vértices (Las coordenadas x en m y las coordenadas y en n)
m = [xi1, xi2, xi3, xi4]
n = [yi1, yi2, yi3, yi4]

#Graficando el polígono solución a partir de las coordenadas de los vértices 
plt.fill(m, n, color='silver')

#Identificando el índice del vértice de la mejor solución
dict1 = {0:FOi1, 1:FOi2, 2:FOi3, 3:FOi4}
posicion = max(dict1, key=dict1.get)

#Obteniendo las coordenadas del vértice de la mejor solución de acuerdo al índice
XMAX = m[posicion]
YMAX = n[posicion]

#Imprimiendo las coordenadas del vértice de la mejor solución (variables de decisión)
print('\n VARIABLES DE DECISIÓN')
print('Cantidad de asientos a reservar para fumadores: {} '.format(XMAX))
print('Cantidad de asientos a reservar para no fumadores: {} '.format(YMAX))

#Generando las anotaciones de las coordenadas y solución óptima en el gráfico
plt.annotate('  X: {0} / Y: {1} (Coordenadas)'.format(XMAX, YMAX), (XMAX, YMAX))
plt.annotate('  Solución óptima: {}'.format(ZMAX), (XMAX, YMAX+3))


#Configuraciones adicionales del gráfico
plt.grid()
plt.xlabel('Asientos para fumadores')
plt.ylabel('Asientos para no fumadores')
plt.title('Método Gráfico')

plt.show()
</code></pre>
</div>
<p>&nbsp;</p>
<hr />
<p>En artículos posteriores abordaremos <em><strong><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/analisis-de-sensibilidad-grafica-mediante-el-uso-de-python-caso-1/">Análisis de sensibilidad</a> </strong></em>en la solución gráfica mediante <em>Python, y </em>evaluaremos la pertinencia de la misma.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/">Método gráfico de la programación lineal mediante el uso de Python</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/metodo-grafico-de-la-programacion-lineal-mediante-el-uso-de-python/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Uso de matrices para definir un modelo de programación lineal en Google OR-Tools</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Sun, 18 Apr 2021 18:00:09 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Álgebra lineal]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Matrices]]></category>
		<category><![CDATA[Optimización lineal]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25469</guid>

					<description><![CDATA[<p>Un factor importante al abordar optimización lineal es la eficiencia del modelamiento. En el artículo introductorio a problemas de programación lineal mediante Google OR-Tools, abordamos con fines prácticos, un ejemplo con pocas variables y restricciones. Cuando el número de variables y restricciones aumenta, se hace necesario contar con herramientas que permitan modelar eficientemente bajo estas &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/">Uso de matrices para definir un modelo de programación lineal en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Un factor importante al abordar optimización lineal es la eficiencia del modelamiento. En el artículo introductorio a <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/"><em><strong>problemas de programación lineal mediante Google OR-Tools</strong></em></a>, abordamos con fines prácticos, un ejemplo con pocas variables y restricciones.</p>
<p>Cuando el número de variables y restricciones aumenta, se hace necesario contar con herramientas que permitan modelar eficientemente bajo estas condiciones. Una herramienta importante, considerando el modelamiento en lenguajes de programación, consiste en el uso de bucles sobre matrices, que permitan automatizar la definición de variables y restricciones; es decir, pasar de la definición individual de variables a una definición automatizada apoyándose en el uso de matrices.</p>
<ul></ul>
<hr />
<p><span>El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar problemas de programación lineal (optimización lineal) usando matrices para la definición de las variables y restricciones del modelo. </span></p>
<h2>El problema
		<div id="el-problema" data-title="El problema" class="index-title"></div>
	</h2>
<p>En el artículo de introducción, abordamos un caso descrito en el libro <em>Applied Mathematical Programming, de Bradley, Hax, and Magnanti (Addison-Wesley, 1977), del MIT (Cápitulo 2 página 50). </em>Con fines prácticos, hemos adaptado dicho modelo, incorporando nuevos procesos al sistema, que impliquen nuevas variables y restricciones:<a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"></a></p>
<blockquote class=" quote-simple "><p>El propietario de una tienda que produce remolques para automóviles desea determinar la mejor combinación para sus tres productos: remolques de plataforma plana, remolques económicos y remolques de lujo. Su taller se limita a trabajar 24 días al mes en el trabajo de los metales, 60 días al mes en el trabajo de la madera, 40 días al mes en el trabajo de pintura, 50 días al mes en el trabajo de montaje y 45 días al mes en el trabajo de acabados para estos productos. La siguiente tabla indica los datos de producción de los remolques.</p></blockquote>
<p><em><strong>Tabla 1</strong></em></p>
<table width="645">
<tbody>
<tr>
<td width="240" style="text-align: center;"></td>
<td colspan="3" width="289" style="text-align: center;">Uso por cada unidad de tráiler</td>
<td rowspan="2" width="116" style="text-align: center;">Recursos disponibles</td>
</tr>
<tr>
<td width="240" style="text-align: center;"></td>
<td width="98" style="text-align: center;">Plataforma plana</td>
<td width="94" style="text-align: center;">Económica</td>
<td width="97" style="text-align: center;">Lujosa</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Días de trabajo en metales</td>
<td width="98" style="text-align: center;">0,5</td>
<td width="94" style="text-align: center;">2</td>
<td width="97" style="text-align: center;">1</td>
<td width="116" style="text-align: center;">24</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Días de trabajo en madera</td>
<td width="98" style="text-align: center;">1</td>
<td width="94" style="text-align: center;">2</td>
<td width="97" style="text-align: center;">4</td>
<td width="116" style="text-align: center;">60</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Días de trabajo en pintura</td>
<td width="98" style="text-align: center;">1,5</td>
<td width="94" style="text-align: center;">0,5</td>
<td width="97" style="text-align: center;">2</td>
<td width="116" style="text-align: center;">40</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Días de trabajo en montaje</td>
<td width="98" style="text-align: center;">1</td>
<td width="94" style="text-align: center;">1,5</td>
<td width="97" style="text-align: center;">1,5</td>
<td width="116" style="text-align: center;">50</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Días de trabajo en acabados</td>
<td width="98" style="text-align: center;">0,5</td>
<td width="94" style="text-align: center;">1</td>
<td width="97" style="text-align: center;">1,5</td>
<td width="116" style="text-align: center;">45</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Contribución ($ x 100)</td>
<td width="98" style="text-align: center;">6</td>
<td width="94" style="text-align: center;">14</td>
<td width="97" style="text-align: center;">13</td>
<td></td>
</tr>
</tbody>
</table>
<p>Podemos observar, que respecto al problema original, se han incorporado tres procesos más, lo cual implica la adición de nuevas variables y nuevas restricciones.</p>
<h2>Modelamiento del problema
		<div id="modelamiento-del-problema" data-title="Modelamiento del problema" class="index-title"></div>
	</h2>
<p><em>Sean las variables de decisión del problema:</em></p>
<p>x<sub>0</sub> = Número de remolques de plataforma plana producidos por mes</p>
<p>x<sub>1</sub> = Número de remolques económicos producidos por mes</p>
<p>x<sub>2</sub> = Número de remolques de lujo producidos por mes</p>
<p><em>Suponiendo que los costos de la capacidad de trabajo en metal, madera, pintura, montaje y acabados sean fijos, el problema se convierte en un problema de maximización:</em></p>
<p>Zmax = 6<strong>x<sub>0</sub></strong> + 14<strong>x<sub>1</sub></strong> + 13<strong>x<sub>2</sub></strong></p>
<p><em>Sujeto a las siguientes restricciones de capacidad:</em></p>
<p>0,5<strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + <strong>x<sub>2</sub></strong> &lt;= 24,</p>
<p><strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + 4<strong>x<sub>2</sub></strong> &lt;= 60,</p>
<p>1,5<strong>x<sub>0</sub></strong> + 0,5<strong>x<sub>1</sub></strong> + 2<strong>x<sub>2</sub></strong> &lt;= 40,</p>
<p>1<strong>x<sub>0</sub></strong> + 1,5<strong>x<sub>1</sub></strong> + 1,5<strong>x<sub>2</sub></strong> &lt;= 50,</p>
<p>0,5<strong>x<sub>0</sub></strong> + 1<strong>x<sub>1</sub></strong> + 1,5<strong>x<sub>2</sub></strong> &lt;= 45,</p>
<p><em>Sujeto a las siguientes restricciones de no-negatividad:</em></p>
<p><strong>x<sub>0</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>1</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>2</sub></strong> &gt;= 0,</p>
<p><em>Podemos, del mismo modo, establecer un conjunto de variables que correspondan a las horas ociosas para cada uno de los procesos del sistema (metal, madera, pintura, montaje y acabados):</em></p>
<p>x<sub>3</sub> = Número de horas ociosas en el trabajo en metal al mes,</p>
<p>x<sub>4</sub> = Número de horas ociosas en el trabajo en madera al mes,</p>
<p>x<sub>5</sub> = Número de horas ociosas en el trabajo en pintura al mes,</p>
<p>x<sub>6</sub> = Número de horas ociosas en el trabajo en montaje al mes,</p>
<p>x<sub>7</sub> = Número de horas ociosas en el trabajo en acabados al mes,</p>
<p><em>Reescribimos las restricciones (adicionando las variables de horas ociosas). Podemos observar que las inecuaciones ahora serán igualdades, para que de esta forma ahora podamos tener información relacionada a los recursos. En otras palabras, lo que se utiliza (horas productivas) + lo que sobre (horas ociosas) = tiempo disponible:</em></p>
<p>0,5<strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + <strong>x<sub>2</sub></strong> + <strong>x<sub>3</sub></strong> = 24,</p>
<p><strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + 4<strong>x<sub>2</sub></strong> + <strong>x<sub>4</sub></strong> = 60,</p>
<p>1,5<strong>x<sub>0</sub></strong> + 0,5<strong>x<sub>1</sub></strong> + 2<strong>x<sub>2</sub></strong> + <strong>x<sub>5</sub></strong> = 40,</p>
<p>1<strong>x<sub>0</sub></strong> + 1,5<strong>x<sub>1</sub></strong> + 1,5<strong>x<sub>2</sub></strong> + <strong>x<sub>6</sub></strong> = 50,</p>
<p>0,5<strong>x<sub>0</sub></strong> + 1<strong>x<sub>1</sub></strong> + 1,5<strong>x<sub>2</sub></strong> + <strong>x<sub>7</sub></strong> = 45,</p>
<p><strong>x<sub>0</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>1</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>2</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>3</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>4</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>5</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>6</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>7</sub></strong> &gt;= 0,</p>
<p>Así entonces, tenemos el problema completamente modelado.</p>
<hr />
<h2>Resolución del modelo mediante Google OR-Tools
		<div id="resolucion-del-modelo-mediante-google-or-tools" data-title="Resolución del modelo mediante Google OR-Tools" class="index-title"></div>
	</h2>
<p>Tal como lo hemos mencionado, el objetivo de este artículo es abordar un problema de optimización lineal utilizando matrices para la definición de variables. De tal manera que detallaremos nuevamente cada uno de los pasos utilizados en el modelamiento.</p>
<p>De acuerdo a lo mencionado en el artículo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/"><strong>introducción a Google OR-Tools</strong></a>, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del lenguaje de programación Python.</p>
<h3><em>Paso 1: Importar la librería
		<div id="paso-1-importar-la-libreria" data-title="Paso 1: Importar la librería" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código importa las librerías necesarias:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
from ortools.linear_solver import pywraplp
</code></pre>
</div>
<h3><em>Paso 2: Crear la data del modelo (matrices)</em>
		<div id="paso-2-crear-la-data-del-modelo-matrices" data-title="Paso 2: Crear la data del modelo (matrices)" class="index-title"></div>
	</h3>
<p>Es recomendable previamente disponer las restricciones por medio de un tabulado que nos permita observar las matrices con claridad. En este caso, la matriz en la cual se relacionan los coeficientes de las restricciones (considerando las variables de horas ociosas), se puede apreciar en la <em>tabla 2. </em></p>
<p><em><strong>Tabla 2</strong></em></p>
<table width="774">
<tbody>
<tr>
<td width="240" style="text-align: center;"></td>
<td colspan="8" width="432" style="text-align: center;">Uso por cada unidad de tráiler</td>
<td rowspan="2" width="102" style="text-align: center;">Recursos disponibles</td>
</tr>
<tr>
<td width="240"></td>
<td width="54" style="text-align: center;">X0</td>
<td width="54" style="text-align: center;">X1</td>
<td width="54" style="text-align: center;">X2</td>
<td width="54" style="text-align: center;">X3</td>
<td width="54" style="text-align: center;">X4</td>
<td width="54" style="text-align: center;">X5</td>
<td width="54" style="text-align: center;">X6</td>
<td width="54" style="text-align: center;">X7</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Metales (días)</td>
<td width="54" style="text-align: center;">0,5</td>
<td width="54" style="text-align: center;">2</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="102" style="text-align: center;">24</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Madera (días)</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">2</td>
<td width="54" style="text-align: center;">4</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="102" style="text-align: center;">60</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Pintura (días)</td>
<td width="54" style="text-align: center;">1,5</td>
<td width="54" style="text-align: center;">0,5</td>
<td width="54" style="text-align: center;">2</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="102" style="text-align: center;">40</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Montaje (días)</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">1,5</td>
<td width="54" style="text-align: center;">1,5</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">0</td>
<td width="102" style="text-align: center;">50</td>
</tr>
<tr>
<td width="240" style="text-align: center;">Acabados (días</td>
<td width="54" style="text-align: center;">0,5</td>
<td width="54" style="text-align: center;">1</td>
<td width="54" style="text-align: center;">1,5</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">0</td>
<td width="54" style="text-align: center;">1</td>
<td width="102" style="text-align: center;">45</td>
</tr>
</tbody>
</table>
<p>Podemos observar que, en la matriz de entrada de los coeficientes de las restricciones incorporaremos todas las variables, incluso las que no forman parte de la ecuación (incluidas con coeficiente 0), esto para conservar un orden de la matriz.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">def create_data_model():
  """Almacena la data de entrada del modelo"""
  data = {}
  data['restriccion_coef'] = [
      [0.5,   2,   1,   1,   0,   0,   0,   0],
      [  1,   2,   4,   0,   1,   0,   0,   0],
      [1.5, 0.5,   2,   0,   0,   1,   0,   0],
      [  1, 1.5, 1.5,   0,   0,   0,   1,   0],
      [0.5,   1, 1.5,   0,   0,   0,   0,   1],
  ]
  data['limites'] = [24, 60, 40, 50, 45]
  data['obj_coef'] = [6, 14, 13, 0, 0, 0, 0, 0]
  data['num_vars'] = 8
  data['num_restricciones'] = 5
  return data</span></code></pre>
</div>

		<div class="checklist tie-list-shortcode">
<ul>
<li>La matriz <em>restriccion_coef </em>contiene todos los coeficientes de la matriz de restricciones.</li>
<li>La matriz <em>limites </em>contiene todos los valores de la parte derecha de las inecuaciones (límites).</li>
<li>La matriz <em>obj_coef </em>contiene todos los coeficientes de la función objetivo (los valores en cero representa el peso en la función objetivo de las variables asociadas con las horas ociosas).</li>
</ul>

		</div>
	
<h3><em>Paso 3: Declarar el solucionador y crear las variables del modelo
		<div id="paso-3-crear-las-variables-del-modelo" data-title="Paso 3: Crear las variables del modelo" class="index-title"></div>
	</em></h3>
<p>Haciendo uso de las matrices definidas previamente, el siguiente fragmento de código automatiza la creación de las variables por medio de un bucle:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">def main():
  data = create_data_model()
  # Declara el solucionador GLOP
  solver = pywraplp.Solver.CreateSolver('GLOP')
  # Crea las variables por medio de un bucle tomando las matrices (data).
  infinity = solver.infinity()
  x = {}
  for j in range(data['num_vars']):
    x[j] = solver.IntVar(0, infinity, 'x[%i]' % j)
  print('Número de variables =', solver.NumVariables())
</span></code></pre>
</div>
<p>Por medio del fragmento anterior se definen cada una de las variables (enteras), y se establecen los límites de cada una de ellas (0, infinity = desde cero hasta el infinito), es decir, hace las veces de restricciones de no-negatividad.</p>
<h3><em>Paso 4: Definir las restricciones del modelo
		<div id="paso-4-definir-las-restricciones-del-modelo" data-title="Paso 4: Definir las restricciones del modelo" class="index-title"></div>
	</em></h3>
<p>Haciendo uso de las matrices definidas previamente, el siguiente fragmento de código automatiza la definición de las restricciones por medio de un bucle:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">  for i in range(data['num_restricciones']):
    constraint = solver.RowConstraint(data['limites'][i], data['limites'][i], '')
    for j in range(data['num_vars']):
      constraint.SetCoefficient(x[j], data['restriccion_coef'][i][j])
  print('Número de restricciones =', solver.NumConstraints())
</span></code></pre>
</div>
<p>Usando el método <em>RowConstraint</em> creamos las restricciones de manera automatizada para el modelo. Los signos de las inecuaciones o igualdades (límites) se expresan dentro del método. Citaremos algunos ejemplos:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Inecuación mayor o igual a: solver.RowConstraint(data[&#8216;limites&#8217;][i], infinity, »)</li>
<li>Inecuación menor o igual a: solver.RowConstraint(0, data[&#8216;limites&#8217;][i], »)</li>
<li>Igualdad,ecuación igual a: solver.RowConstraint(data[&#8216;limites&#8217;][i], data[&#8216;limites&#8217;][i], »)</li>
</ul>

		</div>
	
<p>La expresión <em>data[&#8216;limites][i] </em>toma el valor correspondiente al límite asociado a la fila de cada restricción (fila <em>i</em>) desde la matriz <em>límites</em>. Así entonces, y tomando como ejemplo la primera restricción (fila 0, es decir <em>i </em>=0), lo que expresaría el código en cada caso sería:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Inecuación mayor o igual a: valores desde 24 hasta el infinito (&gt;= 24)</li>
<li>Inecuación menor o igual a: valores desde 0 hasta el 24 (&lt;= 24)</li>
<li>Igualdad, ecuación igual a: valores desde 24 hasta 24 (= 24)</li>
</ul>

		</div>
	
<h3><em>Paso 5: Definir la función objetivo del modelo
		<div id="paso-5-definir-la-funcion-objetivo-del-modelo" data-title="Paso 5: Definir la función objetivo del modelo" class="index-title"></div>
	</em></h3>
<p>Haciendo uso de las matrices definidas previamente, el siguiente fragmento de código automatiza la definición de la función objetivo por medio de un bucle (maximizar):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">  objective = solver.Objective()
  for j in range(data['num_vars']):
    objective.SetCoefficient(x[j], data['obj_coef'][j])
  objective.SetMaximization()
</span></code></pre>
</div>
<h3><em>Paso 6: Invocar el solucionador
		<div id="paso-6-invocar-el-solucionador" data-title="Paso 6: Invocar el solucionador " class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código sirve para invocar el solucionador del modelo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">status = solver.Solve()
</span></code></pre>
</div>
<h3><em>Paso 7: Definir las salidas del solucionador
		<div id="paso-7-definir-lassalidas-del-solucionador" data-title="Paso 7: Definir lassalidas del solucionador" class="index-title"></div>
	</em></h3>
<p>De acuerdo a los bucles definidos, las variables toman los nombres base siguiendo el formato x[i]; de manera que por medio del siguiente código, renombraremos las variables para una mejor comprensión de las salidas del solucionador.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">    print('Valor objetivo =', solver.Objective().Value())
    print('Solución:')
    print('Remolques de plataforma plana producidos por mes =', x[0].solution_value())
    print('Remolques económicos producidos por mes =', x[1].solution_value())
    print('Remolques de lujo producidos por mes =', x[2].solution_value())
    print('Horas ociosas en el trabajo en metal al mes =', x[3].solution_value())
    print('Horas ociosas en el trabajo en madera al mes =', x[4].solution_value())
    print('Horas ociosas en el trabajo en pintura al mes =', x[5].solution_value())
    print('Horas ociosas en el trabajo en montaje al mes =', x[6].solution_value())
    print('Horas ociosas en el trabajo en acabados al mes =', x[7].solution_value())
    print()
    print('Problema resuelto en %f milisegundos' % solver.wall_time())
    print('Problema resuelto en %d iteraciones' % solver.iterations())
    
  else:
    print('El problema no tiene solución óptima.')
</span></code></pre>
</div>
<hr />
<p>Es posible que el desarrollo de los siete pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de optimización lineal. Ahora bien, haciendo el uso de matrices, se puede mejorar considerablemente la eficiencia del modelamiento.</p>
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p>Lo primero que debemos considerar, es que es preciso contar con la instalación de <em>Python </em>en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando <em>pip</em> y por supuesto, el software <em>OR-Tools.</em> Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:</p>
<p style="text-align: center;"><a href="https://developers.google.com/optimization/install/python/windows" target="_blank" class="shortc-button medium blue">Instalación de OR-Tools para Python</a>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico, por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>.</p>
<p>En este caso, haremos uso del editor <em>Sublime Text, </em>al cual llevaremos íntegramente el código completo del programa:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">#Caso desde: Bradley, Hax, and Magnanti, 
#'Applied Mathematical Programming', Chapter 2
#Nuevos procesos adicionados por Salazar (2021)

# Importar la librería de Google OR-Tools
from ortools.linear_solver import pywraplp

def create_data_model(): 
  """Almacena la data de entrada del modelo""" 
  data = {} 
  data['restriccion_coef'] = [ 
      [0.5,   2,   1,   1,   0,   0,   0,   0], 
      [  1,   2,   4,   0,   1,   0,   0,   0], 
      [1.5, 0.5,   2,   0,   0,   1,   0,   0], 
      [  1, 1.5, 1.5,   0,   0,   0,   1,   0], 
      [0.5,   1, 1.5,   0,   0,   0,   0,   1],
  ] 
  data['limites'] = [24, 60, 40, 50, 45] 
  data['obj_coef'] = [6, 14, 13, 0, 0, 0, 0, 0] 
  data['num_vars'] = 8 
  data['num_restricciones'] = 5 
  return data

def main():
  data = create_data_model()
  # Declara el solucionador GLOP
  solver = pywraplp.Solver.CreateSolver('GLOP')
  # Crea las variables por medio de un bucle tomando las matrices (data).
  infinity = solver.infinity()
  x = {}
  for j in range(data['num_vars']):
    x[j] = solver.IntVar(0, infinity, 'x[%i]' % j)
  print('Número de variables =', solver.NumVariables())

  # Definir las restricciones por medio de un bucle tomando las matrices (data).
  for i in range(data['num_restricciones']):
    constraint = solver.RowConstraint(data['limites'][i], data['limites'][i], '')
    for j in range(data['num_vars']):
      constraint.SetCoefficient(x[j], data['restriccion_coef'][i][j])
  print('Número de restricciones =', solver.NumConstraints())

  # Define la función objetivo por medio de un bucle tomando las matrices (data).
  objective = solver.Objective()
  for j in range(data['num_vars']):
    objective.SetCoefficient(x[j], data['obj_coef'][j])
  objective.SetMaximization()
  # In Python, you can also set the objective as follows.
  # obj_expr = [data['obj_coeffs'][j] * x[j] for j in range(data['num_vars'])]
  # solver.Maximize(solver.Sum(obj_expr))

  status = solver.Solve()

  if status == pywraplp.Solver.OPTIMAL:
    print('Valor objetivo =', solver.Objective().Value())
    print('Solución:')
    print('Remolques de plataforma plana producidos por mes =', x[0].solution_value())
    print('Remolques económicos producidos por mes =', x[1].solution_value())
    print('Remolques de lujo producidos por mes =', x[2].solution_value())
    print('Horas ociosas en el trabajo en metal al mes =', x[3].solution_value())
    print('Horas ociosas en el trabajo en madera al mes =', x[4].solution_value())
    print('Horas ociosas en el trabajo en pintura al mes =', x[5].solution_value())
    print('Horas ociosas en el trabajo en montaje al mes =', x[6].solution_value())
    print('Horas ociosas en el trabajo en acabados al mes =', x[7].solution_value())
    print()
    print('Problema resuelto en %f milisegundos' % solver.wall_time())
    print('Problema resuelto en %d iteraciones' % solver.iterations())
    
  else:
    print('El problema no tiene solución óptima.')


if __name__ == '__main__':
  main()</span></code></pre>
</div>
<p>Es necesario que el editor esté configurado de acuerdo a la sintaxis de <em>Python </em>y el archivo se guarde como tal, con la extensión <em>.py</em>.</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Syntax.png" alt="Sintaxis" width="511" height="584" class="aligncenter wp-image-25388" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Syntax.png 742w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Syntax-263x300.png 263w" sizes="(max-width: 511px) 100vw, 511px" /></p>
<p>Lo siguiente será ejecutar el código, para eso podemos utilizar la consola del sistema (símbolo del sistema) o CMD. En ella, debemos dirigirnos hacia el directorio en el cual se encuentre nuestro archivo <em>.py</em> y ejecutarlo de la siguiente manera:</p>
<p style="text-align: center;">python <span style="color: #ff0000;">Nombre del archivo</span>.py</p>
<p>En nuestro caso, hemos guardado el modelo como <em>PL_matrix.py, </em>así que de esa manera lo ejecutamos desde la consola del sistema:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/cmd_programacion_lineal_matrices2.png" alt="cmd_programación_lineal_matrices2" width="674" height="320" class="aligncenter size-full wp-image-25471" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/cmd_programacion_lineal_matrices2.png 674w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/cmd_programacion_lineal_matrices2-300x142.png 300w" sizes="(max-width: 674px) 100vw, 674px" /></p>
<p>Y bien, tenemos la solución a este problema de programación lineal en 5 milisegundos (varía de acuerdo a las especificaciones del equipo) y en 4 iteraciones.</p>
<p>Dada la adición de variables de holgura o exceso, podemos identificar que existen dos restricciones redundantes: Horas montaje y horas de acabados.</p>
<hr />
<p>Ahora bien, el modelo de optimización lineal y el <em>script </em>del solucionador quedaron desarrollados en un lenguaje de programación estándar y ampliamente utilizado. Desde luego, las posibilidades de integrar datos de entrada y procesar los datos de salidas son interesantes. Por ejemplo, es posible desarrollar un<span> </span><em>script </em>mediante el cual el código ya desarrollado tome las matrices de entrada desde un archivo de Excel, o desde archivo <em>csv</em>.</p>
<p>Podemos observar que las variables que forman parte de la solución toman valores continuos, y dado el caso práctico, no es ajustado a la realidad mencionar que se producirán <em>9,99</em> remolques de lujo. De manera que, en próximos artículos abordaremos la solución de problemas de programación lineal entera y mixta (optimización lineal entera y mixta MIP), ya que Google OR-Tools cuenta con un solucionador específico para abordar este tipo de modelos.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/">Uso de matrices para definir un modelo de programación lineal en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Programación lineal en Google OR-Tools</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Sat, 17 Apr 2021 19:36:21 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Optimización lineal]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25451</guid>

					<description><![CDATA[<p>Tal como lo hemos mencionado en artículos anteriores (programación lineal); la optimización lineal, es el nombre con el que se conoce al cálculo de la mejor solución a un problema modelado como un conjunto de restricciones lineales y una función objetivo también lineal. El objetivo de este artículo consiste en utilizar las librerías del software &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/">Programación lineal en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Tal como lo hemos mencionado en artículos anteriores (<em><strong><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal/">programación lineal</a></strong></em>); la optimización lineal, es el nombre con el que se conoce al cálculo de la mejor solución a un problema modelado como un conjunto de restricciones lineales y una función objetivo también lineal.</p>
<ul></ul>
<hr />
<p><span>El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar problemas de programación lineal (optimización lineal). </span></p>
<h2>El problema
		<div id="el-problema" data-title="El problema" class="index-title"></div>
	</h2>
<p>Con el propósito de evaluar los resultados obtenidos a través del tratamiento de un problema técnicamente formulado y abordado, utilizaremos un caso descrito en el libro <em>Applied Mathematical Programming, de Bradley, Hax, and Magnanti (Addison-Wesley, 1977), del MIT (Cápitulo 2 página 50).</em><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"></a></p>
<blockquote class=" quote-simple "><p>El propietario de una tienda que produce remolques para automóviles desea determinar la mejor combinación para sus tres productos: remolques de plataforma plana, remolques económicos y remolques de lujo. Su taller se limita a trabajar 24 días al mes en el trabajo de los metales y 60 días al mes en el trabajo de la madera para estos productos. La siguiente tabla indica los datos de producción de los remolques.</p></blockquote>
<table width="492">
<tbody>
<tr>
<td width="172"></td>
<td colspan="3" width="240" style="text-align: center;">Uso por cada unidad de tráiler</td>
<td rowspan="2" width="80" style="text-align: center;">Recursos disponibles</td>
</tr>
<tr>
<td style="text-align: center;"></td>
<td width="80" style="text-align: center;">Plataforma plana</td>
<td width="80" style="text-align: center;">Económica</td>
<td width="80" style="text-align: center;">Lujosa</td>
</tr>
<tr>
<td style="text-align: center;">Días de trabajo en metales</td>
<td style="text-align: center;">0,5</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">24</td>
</tr>
<tr>
<td style="text-align: center;">Días de trabajo en madera</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">60</td>
</tr>
<tr>
<td style="text-align: center;">Contribución ($ x 100)</td>
<td style="text-align: center;">6</td>
<td style="text-align: center;">14</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;"></td>
</tr>
</tbody>
</table>
<h2>Modelamiento del problema
		<div id="modelamiento-del-problema" data-title="Modelamiento del problema" class="index-title"></div>
	</h2>
<p><em>Sean las variables de decisión del problema:</em></p>
<p>x<sub>0</sub> = Número de remolques de plataforma plana producidos por mes</p>
<p>x<sub>1</sub> = Número de remolques económicos producidos por mes</p>
<p>x<sub>2</sub> = Número de remolques de lujo producidos por mes</p>
<p><em>Suponiendo que los costos de la capacidad de trabajo en metal y madera sean fijos, el problema se convierte en un problema de maximización:</em></p>
<p>Zmax = 6<strong>x<sub>0</sub></strong> + 14<strong>x<sub>1</sub></strong> + 13<strong>x<sub>2</sub></strong></p>
<p><em>Sujeto a las siguientes restricciones de capacidad:</em></p>
<p>0,5<strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + <strong>x<sub>2</sub></strong> &lt;= 24,</p>
<p><strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + 4<strong>x<sub>2</sub></strong> &lt;= 60,</p>
<p><em>Sujeto a las siguientes restricciones de no-negatividad:</em></p>
<p><strong>x<sub>0</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>1</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>2</sub></strong> &gt;= 0,</p>
<p><em>Podemos, del mismo modo, establecer un par de variables que correspondan a las horas ociosas para las dos tareas establecidas (metal y madera):</em></p>
<p>x<sub>3</sub> = Número de horas ociosas en el trabajo en metal al mes,</p>
<p>x<sub>4</sub> = Número de horas ociosas en el trabajo en madera al mes,</p>
<p><em>Reescribimos las restricciones (adicionando las variables de horas ociosas). Podemos observar que las inecuaciones ahora serán igualdades, para que de esta forma ahora podamos tener información relacionada a los recursos. En otras palabras, lo que se utiliza (horas productivas) + lo que sobre (horas ociosas) = tiempo disponible:</em></p>
<p>0,5<strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + <strong>x<sub>2</sub></strong> + <strong>x<sub>3</sub></strong> = 24,</p>
<p><strong>x<sub>0</sub></strong> + 2<strong>x<sub>1</sub></strong> + 4<strong>x<sub>2</sub></strong> + <strong>x<sub>4</sub></strong> = 60,</p>
<p><strong>x<sub>0</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>1</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>2</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>3</sub></strong> &gt;= 0,</p>
<p><strong>x<sub>4</sub></strong> &gt;= 0,</p>
<p>Así entonces, tenemos el problema completamente modelado.</p>
<hr />
<h2>Resolución del modelo mediante Google OR-Tools
		<div id="resolucion-del-modelo-mediante-google-or-tools" data-title="Resolución del modelo mediante Google OR-Tools" class="index-title"></div>
	</h2>
<p>De acuerdo a lo mencionado en el artículo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/"><strong>introducción a Google OR-Tools</strong></a>, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del lenguaje de programación Python.</p>
<h3><em>Paso 1: Importar la librería
		<div id="paso-1-importar-la-libreria" data-title="Paso 1: Importar la librería" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código importa las librerías necesarias:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
from ortools.linear_solver import pywraplp
</code></pre>
</div>
<h3><em>Paso 2: Declarar el solucionador
		<div id="paso-2-declarar-el-solucionador" data-title="Paso 2: Declarar el solucionador" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código declara el solucionador GLOP <em>(Google OR-Tools posee múltiples solucionadores):</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">solver = pywraplp.Solver.CreateSolver('GLOP')</span></code></pre>
</div>
<h3><em>Paso 3: Crear las variables del modelo
		<div id="paso-3-crear-las-variables-del-modelo" data-title="Paso 3: Crear las variables del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento crea las variables del modelo, así mismo indica el tipo de variables correspondientes y su rango de valores. Así entonces, desde la creación de las variables se pueden abordar las restricciones de no-negatividad (entre 0 e infinito):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">x0 = solver.NumVar(0, solver.infinity(), 'x0')
x1 = solver.NumVar(0, solver.infinity(), 'x1')
x2 = solver.NumVar(0, solver.infinity(), 'x2')
x3 = solver.NumVar(0, solver.infinity(), 'x3')
x4 = solver.NumVar(0, solver.infinity(), 'x4')

print('Número de variables =', solver.NumVariables())
</span></code></pre>
</div>
<h3><em>Paso 4: Definir las restricciones del modelo
		<div id="paso-4-definir-las-restricciones-del-modelo" data-title="Paso 4: Definir las restricciones del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código define las restricciones del modelo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Restricción 0: 0.5x0 + 2x1 + x2 + x3 = 24.
solver.Add(0.5 * x0 + 2 * x1 + x2 + x3 == 24.0)

# Restricción 1: x0 + 2x1 + 4x2 + x4 = 60.
solver.Add(x0 + 2 * x1 + 4 *x2 + x4 == 60.0)

print('Número de restricciones =', solver.NumConstraints())
</span></code></pre>
</div>
<h3><em>Paso 5: Definir la función objetivo del modelo
		<div id="paso-5-definir-la-funcion-objetivo-del-modelo" data-title="Paso 5: Definir la función objetivo del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código define la función objetivo del modelo (maximizar):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Función objetivo (max): 6x0 + 14x1 + 13x2
solver.Maximize(6 * x0 + 14 * x1 + 13 * x2)
</span></code></pre>
</div>
<h3><em>Paso 6: Invocar el solucionador
		<div id="paso-6-invocar-el-solucionador" data-title="Paso 6: Invocar el solucionador " class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código sirve para invocar el solucionador del modelo:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">status = solver.Solve()
</span></code></pre>
</div>
<h3><em>Paso 7: Definir las salidas del solucionador
		<div id="paso-7-definir-lassalidas-del-solucionador" data-title="Paso 7: Definir lassalidas del solucionador" class="index-title"></div>
	</em></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">if status == pywraplp.Solver.OPTIMAL:
    print('Solución:')
    print('Valor objetivo =', solver.Objective().Value())
    print('Número de remolques de plataforma plana producidos por mes =', x0.solution_value())
    print('Número de remolques económicos producidos por mes =', x1.solution_value())
    print('Número de remolques de lujo producidos por mes =', x2.solution_value())
    print('Número de horas ociosas en el trabajo en metal al mes =', x3.solution_value())
    print('Número de horas ociosas en el trabajo en madera al mes =', x4.solution_value())
else:
    print('El problema no tiene solución óptima.')
</span></code></pre>
</div>
<hr />
<p>Es posible que el desarrollo de los siete pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de optimización lineal.</p>
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p><em><strong>Alternativa 1, ejecución en nuestro equipo:</strong></em></p>
<p>Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de <em>Python </em>en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando <em>pip</em> y por supuesto, el software <em>OR-Tools.</em> Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:</p>
<p style="text-align: center;"><a href="https://developers.google.com/optimization/install/python/windows" target="_blank" class="shortc-button medium blue">Instalación de OR-Tools para Python</a>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>, o <em><strong>Spyder</strong></em> (Una herramienta más completa y por ende más robusta y pesada).</p>
<p><em><strong>Alternativa 2, ejecución en un entorno virtual (Recomendado):</strong></em></p>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<hr />
<p>El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1Zf86dKpHJrsLRCjKJoWsE43hsZ11GIo_?usp=sharing"><strong>Programación Lineal</strong></a>.</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">#Desde: Bradley, Hax, and Magnanti, 'Applied Mathematical Programming', Chapter 2

# Importar la librería de Google OR-Tools
from ortools.linear_solver import pywraplp

def LinearProgrammingExample():
    solver = pywraplp.Solver.CreateSolver('GLOP')

    x0 = solver.NumVar(0, solver.infinity(), 'x0')
    x1 = solver.NumVar(0, solver.infinity(), 'x1')
    x2 = solver.NumVar(0, solver.infinity(), 'x2')
    x3 = solver.NumVar(0, solver.infinity(), 'x3')
    x4 = solver.NumVar(0, solver.infinity(), 'x4')

    print('Número de variables =', solver.NumVariables())

    # Restricción 0: 0.5x0 + 2x1 + x2 + x3 = 24.
    solver.Add(0.5 * x0 + 2 * x1 + x2 + x3 == 24.0)

    # Restricción 1: x0 + 2x1 + 4x2 + x4 = 60.
    solver.Add(x0 + 2 * x1 + 4 *x2 + x4 == 60.0)

    print('Número de restricciones =', solver.NumConstraints())

    # Función objetivo (max): 6x0 + 14x1 + 13x2
    solver.Maximize(6 * x0 + 14 * x1 + 13 * x2)

    # Declarar el solucionador.
    status = solver.Solve()

    # Declarar las salidas del solucionador
    if status == pywraplp.Solver.OPTIMAL:
        print('Solución:')
        print('Valor objetivo =', solver.Objective().Value())
        print('Número de remolques de plataforma plana producidos por mes =', x0.solution_value())
        print('Número de remolques económicos producidos por mes =', x1.solution_value())
        print('Número de remolques de lujo producidos por mes =', x2.solution_value())
        print('Número de horas ociosas en el trabajo en metal al mes =', x3.solution_value())
        print('Número de horas ociosas en el trabajo en madera al mes =', x4.solution_value())
    else:
      if status == solver.FEASIBLE:
        print('Se encontró una solución potencialmente subóptima.')
      else:
        print('El problema no tiene solución óptima.')

    # Información avanzada del solucionador

    print('\nUso avanzado:')
    print('Problema resuelto en %f milisegundos' % solver.wall_time())
    print('Problema resuelto en %d iteraciones' % solver.iterations())

LinearProgrammingExample()</span></code></pre>
</div>
<p>Al ejecutar nuestro desarrollo en <em><a href="https://colab.research.google.com/drive/1Zf86dKpHJrsLRCjKJoWsE43hsZ11GIo_?usp=sharing">Colaboratory</a>, </em>tenemos:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/pl_solucion.png" alt="pl_solucion" width="673" height="260" class="aligncenter size-full wp-image-26347" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/pl_solucion.png 673w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/pl_solucion-300x116.png 300w" sizes="(max-width: 673px) 100vw, 673px" /></p>
<p>Podemos observar que se ha obtenido la misma respuesta que se encuentra consignada en el libro <em><strong><a href="http://web.mit.edu/15.053/www/AMP-Chapter-02.pdf">Applied Mathematical Programming (Página 51)</a></strong>.</em></p>
<hr />
<p>Ahora bien, el modelo de optimización lineal y el <em>script </em>del solucionador quedaron desarrollados en un lenguaje de programación estándar y ampliamente utilizado. Desde luego, las posibilidades de integrar datos de entrada y procesar los datos de salidas son interesantes. Por ejemplo, es posible desarrollar un<span> </span><em>script </em>mediante el cual el código ya desarrollado tome los datos de entrada desde un archivo de Excel, o desde un servidor externo.</p>
<p>También, es posible desarrollar una interfaz amigable desde la cual se ingrese la información; o vincular los datos de salida con algún modelo o documento determinado.</p>
<p>En próximos artículos abordaremos algunos <em>scripts </em>haciendo uso de las librerías de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/uso-de-matrices-para-definir-un-modelo-de-programacion-lineal-en-google-or-tools/"><em><strong>Google OR-Tools que nos permitan utilizar bucles de matrices</strong></em></a>, con el objetivo de automatizar el proceso de definición de variables y restricciones, mejorando la eficiencia del modelamiento.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/">Programación lineal en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Problema de la ruta más corta en Google OR-Tools</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Thu, 15 Apr 2021 21:44:18 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[La ruta más corta]]></category>
		<category><![CDATA[Microsoft Excel]]></category>
		<category><![CDATA[Problema de flujo de costo mínimo]]></category>
		<category><![CDATA[Problemas de flujos de red]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25407</guid>

					<description><![CDATA[<p>Podemos decir que el problema o algoritmo de la ruta más corta es una popularización del problema del flujo del costo mínimo, una variación de los modelos generales de flujos. Cuando nos referimos al costo mínimo, este en realidad puede expresarse en diversas magnitudes: distancia, tiempo, volumen, y en general, cual cualquier unidad que represente el caso &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/">Problema de la ruta más corta en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Podemos decir que el problema o algoritmo de la ruta más corta es una popularización del problema del flujo del costo mínimo, una variación de los modelos generales de flujos. Cuando nos referimos al <em><strong>costo mínimo</strong></em>, este en realidad puede expresarse en diversas magnitudes: distancia, tiempo, volumen, y en general, cual cualquier unidad que represente el caso de estudio.</p>
<p>En el problema del flujo de costo mínimo, cada arco de la red tiene un costo asociado a transportar unidades a través de él, es decir, por ejemplo, que el arco que une a los nodos 0 y 1, tiene un costo subyacente a su transporte, en los términos que representen mejor al modelo. Además del costo, cada arco debe considerar una capacidad de transporte.</p>
<p>Del mismo modo, debe considerarse que en este problema los nodos tienen una naturaleza, y existen algunos nodos especiales:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>nodos de oferta: puntos desde los cuales se suministran unidades de flujo (fuentes)</li>
<li>nodos de demanda: puntos hacia los cuales se llevan unidades de flujo para su consumo, sumidero o hundimiento (destinos)</li>
<li>nodos neutrales: puntos a través de los cuales pasan unidades de flujo, no genera nuevas unidades, no consume unidades, son puntos de tránsito.</li>
</ul>

		</div>
	
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo.png" alt="Ejemplo de tipos de nodos" width="606" height="427" class="aligncenter size-full wp-image-25409" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo.png 606w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Tipos-de-nodo-300x211.png 300w" sizes="(max-width: 606px) 100vw, 606px" /></p>
<p>En el anterior gráfico se puede apreciar una representación básica de lo que sería la naturaleza de los nodos aplicada a un caso común: una planta que produce material, lo envía hacia algunos centros de distribución o consolidación y un cliente, lugar que representa el destino final del material. Claramente pueden apreciarse cuales son los nodos de oferta, de demanda y de tránsito. Ahora bien, es posible que en la práctica existan múltiples nodos de oferta y múltiples nodos de demanda, del mismo modo, es posible que los nodos de tránsito también suministren o consuman unidades, lo que los convertiría en nodos mixtos, y lo que podría representar múltiples casos prácticos, por ejemplo: un centro de distribución ubicado en una planta, el cual recibe unidades para consumo interno y despacha excedentes hacia otros centros o clientes.</p>
<h2>Problema de la ruta más corta
		<div id="problema-de-la-ruta-mas-corta" data-title="Problema de la ruta más corta" class="index-title"></div>
	</h2>
<p>Tal como se mencionó en la introducción del artículo, el problema de la ruta más corta puede abordarse desde la perspectiva del problema del flujo de costo mínimo, en el cual el objetivo consiste en determinar el plan de rutas que genere la trayectoria con la mínima distancia total, que una un <em>nodo fuente (puro)</em> con un <em>nodo destino (puro)</em>, sin importar el número de nodos que existan entre estos.</p>
<p>Así entonces, en su versión más básica, el flujo y la capacidad de los arcos puede reducirse a la unidad (1), asumiendo que no se transportan materiales y que el único objetivo que se persigue consiste en determinar la ruta más corta que une a un nodo fuente con un nodo destino. Dicho de otro modo, el nodo fuente produce una unidad que es transportada por medio de arcos con capacidad de unidad y se consume en la medida de una unidad en el nodo destino.</p>
<p>Es preciso reiterar que cuando nos referimos distancia nos ajustamos al nombre del algoritmo «la ruta más corta», sin embargo, lo que se considera distancia, bien puede expresarse en otras unidades de medida, por ejemplo: costo.</p>
<hr />
<p><span>El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar el problema de la ruta más corta a través de una interfaz base del algoritmo del flujo de costo mínimo. Posteriormente, abordaremos un <em>script </em>básico en <em>Python</em> que nos permita integrar al modelo de optimización, data de entrada proveniente de fuentes como un documento de Excel.</span></p>
<h2>El problema
		<div id="el-problema" data-title="El problema" class="index-title"></div>
	</h2>
<p>Con el propósito de evaluar los resultados obtenidos mediante distintos métodos y solucionadores, utilizaremos el mismo problema que abordamos mediante <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><b>programación lineal </b>y el módulo de <em>network modeling</em> de WinQSB.</a></p>
<blockquote class=" quote-simple "><p>Un minero ha quedado atrapado en una mina, la entrada a la mina se encuentra ubicada en el nodo 0, se conoce de antemano que el minero permanece atrapado en el nodo 8, para llegar a dicho nodo hay que atravesar una red de túneles que van conectados entre sí. El tiempo de vida que le queda al minero sin recibir auxilio es cada vez menor y se hace indispensable hallar la ruta de acceso al nodo 8 más corta. Las distancias entre nodos de la mina se encuentran en la siguiente gráfica dadas en cientos de metros:</p></blockquote>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero.png" alt="Ruta_corta_minero" width="662" height="287" class="aligncenter size-full wp-image-25411" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero-300x130.png 300w" sizes="(max-width: 662px) 100vw, 662px" /></p>
<h2>Resolviendo un problema de la ruta más corta mediante Google OR-Tools
		<div id="resolviendo-un-problema-de-la-ruta-mas-corta-mediante-google-or-tools" data-title="Resolviendo un problema de la ruta más corta mediante Google OR-Tools" class="index-title"></div>
	</h2>
<p>De acuerdo a lo mencionado en el artículo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/"><strong>introducción a Google OR-Tools</strong></a>, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del lenguaje de programación Python.</p>
<p>Es posible que para la ejecución de este <em>script, </em>debas instalar el comando <em>future</em>, el cual puedes encontrar en el siguiente enlace: <em><strong><a href="https://pypi.org/project/future/">future 0.18.2</a>. </strong></em>La instalación es muy simple, tan solo escribir en el <em>cmd (símbolo del sistema) lo siguiente:</em></p>
<p style="text-align: center;"><strong>pip install future</strong></p>
<h3><em>Paso 1: Importar la librería
		<div id="paso-1-importar-la-libreria" data-title="Paso 1: Importar la librería" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código importa las librerías necesarias:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
<span class="pln">from __future__ import print_function
from ortools.graph import pywrapgraph</span></code></pre>
</div>
<h3><em>Paso 2: Crear la data del modelo
		<div id="paso-2-crear-la-data-del-modelo" data-title="Paso 2: Crear la data del modelo" class="index-title"></div>
	</em></h3>
<p>Define cuatro matrices paralelas: nodos_fuente, nodos_destino, capacidades, y costos_unitarios, entre cada par. Por ejemplo, el arco desde el nodo 0 hacia el nodo 1 tiene una capacidad de 1 y un costo asociado de 4 (distancia). En su versión más básica el objetivo consiste en unir un nodo fuente y un nodo destino; por esta razón, en este ejemplo la capacidad de todos los nodos equivale a 1 y el flujo es binario, donde tomará valores de 1 en el caso en que el arco forme parte del conjunto solución y 0 en el caso contrario:</p>
<p>Pueden observarse las matrices perfectamente alineadas, ya que el orden es importante. El orden entre la matriz de nodos_fuente y nodos_destino definirá el valor de los arcos del modelo (distancia).</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">nodos_fuente  = [ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7]
nodos_destino = [ 1, 2, 2, 3, 1, 3, 4, 5, 6, 3, 6, 7, 6, 8, 5, 7, 8, 6, 8]
capacidades   = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
distancia     = [ 4, 2, 2, 7, 4, 9, 6, 1, 5, 2, 3, 2, 1, 5, 4, 3, 6, 2, 6]

suministros = [1, 0, 0, 0, 0, 0, 0, 0, -1]</span></code></pre>
</div>
<p>También definimos la matriz de suministros asociada a los nodos, donde los valores positivos corresponde a oferta y los valores positivos corresponde a demanda. Los ceros se asocian a los nodos de tránsito. En nuestro ejemplo solo existe un nodo de oferta = nodo 0, y un nodo de demanda = nodo 8.</p>
<h3><em>Paso 3: Declarar el solucionador y agregar los arcos del modelo
		<div id="paso-3-declarar-el-solucionador-y-agregar-los-arcos-del-modelo" data-title="Paso 3: Declarar el solucionador y agregar los arcos del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento define en primera instancia el solucionador (declara). Luego, define cada arco del problema, es decir, con base en las matrices definidas como datos de entrada (en su orden), establece el valor (costo = distancia) de cada arco. Posteriormente, define los suministros (o demandas) para cada nodo.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Crea una instancia para el solucionador
min_cost_flow = pywrapgraph.SimpleMinCostFlow()

# Define cada arco del problema
for i in range(0, len(nodos_fuente)):
  min_cost_flow.AddArcWithCapacityAndUnitCost(nodos_fuente[i], nodos_destino[i],
                                              capacidades[i], distancia[i])

# Define los suministros para cada nodo.
for i in range(0, len(suministros)):
  min_cost_flow.SetNodeSupply(i, suministros[i])
</span></code></pre>
</div>
<h3><em>Paso 4: Invocar al solucionador y definir la información de salida del modelo
		<div id="paso-4-invocar-al-solucionador-y-definir-la-informacion-de-salida-del-modelo" data-title="Paso 4: Invocar al solucionador y definir la información de salida del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código utiliza las librerías predeterminadas de Google OR-Tools para abordar problemas de flujo de costo mínimo. Así mismo, se especifican las salidas del solucionador: arco / flujo / capacidad  / distancia, para cada arco; y distancia total de la red.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Encuentra el costo mínimo entre el nodo 0 y el nodo 8
if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
  print('Distancia mínima:', min_cost_flow.OptimalCost())
  print('')
  print(' Arco Flujo / Capacidad Distancia')
  for i in range(min_cost_flow.NumArcs()):
    cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
    print('%1s -&gt; %1s %3s / %3s %3s' % (
        min_cost_flow.Tail(i),
        min_cost_flow.Head(i),
        min_cost_flow.Flow(i),
        min_cost_flow.Capacity(i),
        cost))
else:
  print('Hubo un problema con la entrada de flujo de distancia mínima.')
</span></code></pre>
</div>
<hr />
<p>Es posible que el desarrollo de los cuatro pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de asignación.</p>
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p><em><strong>Alternativa 1, ejecución en nuestro equipo:</strong></em></p>
<p>Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de <em>Python </em>en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando <em>pip</em> y por supuesto, el software <em>OR-Tools.</em> Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:</p>
<p style="text-align: center;"><a href="https://developers.google.com/optimization/install/python/windows" target="_blank" class="shortc-button medium blue">Instalación de OR-Tools para Python</a>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>, o <em><strong>Spyder</strong></em> (Una herramienta más completa y por ende más robusta y pesada).</p>
<p><em><strong>Alternativa 2, ejecución en un entorno virtual (Recomendado):</strong></em></p>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<hr />
<p>El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1EcLAl1bjQU9-j35-6TjMsTPWe_j1ykg-?usp=sharing"><strong>Ruta más corta</strong></a>.</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">#Desde: Salazar, ingenieriaindustrialonline.com - Algoritmo de la ruta más corta

from __future__ import print_function
from ortools.graph import pywrapgraph

def main():
  """MinCostFlow adaptado a la ruta más corta - interfaz de ejemplo."""

  # Define cuatro matrices paralelas: nodos_fuente, nodos_destino, 
  # capacidades, y costos_unitarios entre cada par.

  nodos_fuente      = [ 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 7, 7]
  nodos_destino     = [ 1, 2, 2, 3, 1, 3, 4, 5, 6, 3, 6, 7, 6, 8, 5, 7, 8, 6, 8]
  capacidades       = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
  distancia         = [ 4, 2, 2, 7, 4, 9, 6, 1, 5, 2, 3, 2, 1, 5, 4, 3, 6, 2, 6]

  # Define una matriz con los suministros de cada nodo (valores positivos = 
  # suministros) y (valores negativos = demandas)

  suministros = [1, 0, 0, 0, 0, 0, 0, 0, -1]


  # Crea una instancia para el solucionador
  min_cost_flow = pywrapgraph.SimpleMinCostFlow()

  # Define cada arco del problema
  for i in range(0, len(nodos_fuente)):
    min_cost_flow.AddArcWithCapacityAndUnitCost(nodos_fuente[i], nodos_destino[i],
                                                capacidades[i], distancia[i])

  # Define los suministros para cada nodo.

  for i in range(0, len(suministros)):
    min_cost_flow.SetNodeSupply(i, suministros[i])


  # Encuentra el costo mínimo entre el nodo 0 y el nodo 8
  if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
    print('Distancia mínima:', min_cost_flow.OptimalCost())
    print('')
    print('  Arco    Flujo / Capacidad  Distancia')
    for i in range(min_cost_flow.NumArcs()):
      cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
      print('%1s -&gt; %1s    %3s   / %3s       %3s' % (
          min_cost_flow.Tail(i),
          min_cost_flow.Head(i),
          min_cost_flow.Flow(i),
          min_cost_flow.Capacity(i),
          cost))
  else:
    print('Hubo un problema con la entrada de flujo de distancia mínima.')

if __name__ == '__main__':
  main()
</span></code></pre>
</div>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png" alt="ruta_mas_corta_solucion" width="611" height="397" class="aligncenter size-full wp-image-26354" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png 611w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion-300x195.png 300w" sizes="(max-width: 611px) 100vw, 611px" /></p>
<p>Y bien, tenemos la solución a este problema simple de asignación en menos de 1 segundo.</p>
<hr />
<p>Podemos observar que se ha obtenido la misma respuesta que la lograda mediante <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><b>programación lineal </b>y el módulo de <em>network modeling </em> de WinQSB</a>:</p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo.png" alt="Ruta_corta_minero_rojo" width="662" height="287" class="aligncenter size-full wp-image-25413" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Ruta_corta_minero_rojo-300x130.png 300w" sizes="(max-width: 662px) 100vw, 662px" /></p>
<hr />
<h2>Problema de la ruta más corta mediante Google OR-Tools, importando los datos desde Excel
		<div id="problema-de-la-ruta-mas-corta-mediante-google-or-tools-importando-los-datos-desde-excel" data-title="Problema de la ruta más corta mediante Google OR-Tools, importando los datos desde Excel" class="index-title"></div>
	</h2>
<p>Tal como lo planteamos al inicio del artículo, <span>abordaremos un <em>script </em>básico en <em>Python</em> que nos permita integrar al modelo de optimización, data de entrada proveniente de fuentes como un documento de Excel. Esto con el propósito de introducirnos en las bondades del modelamiento en lenguajes de programación.</span></p>
<h3><em>Paso 1: Construir una base de datos en Microsoft Excel
		<div id="paso-1-construir-una-base-de-datos-en-microsoft-excel" data-title="Paso 1: Construir una base de datos en Microsoft Excel" class="index-title"></div>
	</em></h3>
<p>En este caso, utilizaremos una hoja de cálculo haciendo uso de Microsoft Excel, en su extensión predeterminada <em>xlsx</em>, desde la cual, construiremos una base de datos que consignará toda la información de entrada relacionada con el modelo. Un consejo práctico, es que este archivo se guarde dentro del mismo directorio en el que se encuentra el modelo desarrollado en <em>Python.</em></p>
<figure id="attachment_25416" aria-describedby="caption-attachment-25416" style="width: 507px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1.png" alt="datos_excel_hoja1" width="507" height="638" class="wp-image-25416 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1.png 507w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja1-238x300.png 238w" sizes="(max-width: 507px) 100vw, 507px" /><figcaption id="caption-attachment-25416" class="wp-caption-text">Hoja 1</figcaption></figure>
<p>Se puede apreciar la forma en la cual hemos consignado la información, desde luego, respetando el orden de los datos, ya que, tal como lo mencionamos, es muy importante, ya que de ello depende la asociación de cada arco con su distancia. La anterior información la hemos consignado en la <em>Hoja 1</em> del archivo, ya que en la <em>Hoja 2</em> consignaremos la información relacionada con el suministros (con el objetivo práctico de mostrar cómo se importan datos desde hojas específicas), de esta manera:</p>
<figure id="attachment_25417" aria-describedby="caption-attachment-25417" style="width: 390px" class="wp-caption aligncenter"><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2.png" alt="datos_excel_hoja2" width="390" height="441" class="wp-image-25417 size-full" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2.png 390w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/datos_excel_hoja2-265x300.png 265w" sizes="(max-width: 390px) 100vw, 390px" /><figcaption id="caption-attachment-25417" class="wp-caption-text">Hoja 2</figcaption></figure>
<p>Del mismo modo, el orden de los suministros es muy importante, razón por la cual hemos construido una columna denominada <em><strong>nodos</strong></em> que puede servir como guía para consignar la información de los suministros de forma ordenada.</p>
<p>Es de vital importancia, nombrar cada columna y tener claridad sobre ello, desaconsejamos el uso de caracteres especiales y sugerimos utilizar nombres cortos relacionados con la información contenida.</p>
<p>Posterior a consignar la información, podemos guardar el documento, en este caso lo hemos nombrado: <em>data_flujo. </em>Este dato es importante, ya que lo utilizaremos desde el <em>script.</em></p>
<h3><em>Paso 2: Importar la librería Pandas
		<div id="paso-2-importar-la-libreria-pandas" data-title="Paso 2: Importar la librería Pandas" class="index-title"></div>
	</em></h3>

		<div class="box info  ">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Pandas es un paquete de Python que proporciona estructuras de datos rápidas, flexibles y expresivas diseñadas para que el trabajo con datos estructurados (tabulares, multidimensionales, potencialmente heterogéneos) y de series de tiempo sea fácil e intuitivo. (Fuente: https://pypi.org/project/pandas/)
			</div>
		</div>
	
<p>En caso de requerir la instalación de la librería, tan solo debes escribir el siguiente comando en el símbolo del sistema:</p>
<p style="text-align: center;"><span>pip install pandas</span></p>
<p>Esta paquete nos proporcionará la posibilidad de importar y trabajar con datos de fuentes como Microsoft Excel. El siguiente fragmento de código se adicionará al <em>script </em>e importará la librería:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
# Importar la librería de Pandas
<span class="pln">from __future__ import print_function
from ortools.graph import pywrapgraph
import pandas as pd</span></code></pre>
</div>
<p>Una vez que importemos la librería, podemos trabajar con sus funciones.</p>
<h3><em>Paso 3: Importar datos desde Microsoft Excel
		<div id="paso-3-importar-datos-desde-microsoft-excel" data-title="Paso 3: Importar datos desde Microsoft Excel" class="index-title"></div>
	</em></h3>
<p>El desarrollo para importar la información desde Excel debe estar correctamente ordenada (correspondencia entre filas). Por ejemplo:</p>
<table border="0" cellpadding="0" cellspacing="0" width="400" style="border-collapse: collapse; width: 300pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl63" width="80" style="height: 15pt; width: 60pt; text-align: center;"><strong>fila</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>fuentes</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>destinos</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>capacidad</strong></td>
<td class="xl63" width="80" style="width: 60pt; text-align: center;"><strong>distancia</strong></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl63" style="height: 15pt; text-align: center;">1</td>
<td class="xl63" style="text-align: center;">0</td>
<td class="xl63" style="text-align: center;">1</td>
<td class="xl63" style="text-align: center;">1</td>
<td class="xl63" style="text-align: center;">4</td>
</tr>
</tbody>
</table>
<p>La anterior fila representa el arco entre el nodo <em><strong>0 </strong></em>y el nodo <em><strong>1</strong></em> cuya capacidad es de <em><strong>1 </strong></em>y de dimensión (distancia) <em><strong>4</strong></em>.</p>
<p>En el caso de los suministros, debe considerarse el orden la columna en Excel. Por ejemplo:</p>
<table border="0" cellpadding="0" cellspacing="0" width="240" style="border-collapse: collapse; width: 180pt;" class=" aligncenter">
<tbody>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl66" width="80" style="height: 15.0pt; width: 60pt;"><strong>fila</strong></td>
<td class="xl66" width="80" style="width: 60pt;"><strong>nodos</strong></td>
<td class="xl66" width="80" style="width: 60pt;"><strong>suministros</strong></td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15.0pt;">1</td>
<td class="xl65">0</td>
<td class="xl65">1</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15pt;">5</td>
<td class="xl65">4</td>
<td class="xl65">0</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td height="20" class="xl65" style="height: 15pt;">9</td>
<td class="xl65">8</td>
<td class="xl65">-1</td>
</tr>
</tbody>
</table>
<p>En este caso, el nodo <em><strong>0 </strong></em>será de oferta, es decir, desde ahí se crea flujo; el nodo <em><strong>8</strong></em> será de demanda, es decir, en este nodo se consume (sumidero). El nodo <em><strong>4</strong></em> será un nodo de tránsito (no crea inicia flujo ni lo consume), solo es un nodo de paso.</p>
<p>El siguiente fragmento de código importará la data que se encuentra consignada en el documento de Excel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code>def create_data():

    excel = pd.read_excel('data_flujo.xlsx')
    excel_1 = pd.read_excel('data_flujo.xlsx', sheet_name=1)

    data = {}

    data['fuentes'] = excel['fuentes'].tolist() 
    data['destinos'] = excel['destinos'].tolist() 
    data['capacidad'] = excel['capacidad'].tolist() 
    data['distancias'] = excel['distancia'].tolist() 
    data['suministro'] = excel_1['suministros'].tolist() 

    return data </code></pre>
</div>
<p>Los datos de entrada del modelo los trataremos dentro de una función <em>create_data</em>, desde ahí utilizaremos algunas funciones de la librería <em>Pandas (pd)</em> para disponer correctamente de la información contenida en el documento de Excel. Veamos cómo:</p>
<p style="text-align: center;"><strong>excel</strong> = pd.<strong>read_excel</strong>(&#8216;data_flujo.xlsx&#8217;)</p>
<p>En este caso, creamos la variable <em><strong>excel</strong></em> y dentro de ella utilizamos la función <em><strong>read_excel</strong></em> la cual permite leer el documento de Excel (en nuestro caso <em>data_flujo.xlsx</em>), e importarlo (en la variable <em>excel</em>) en formato <em>dataframe</em> (conjunto de columnas).</p>
<p>También creamos la variable <em><strong>excel_1</strong></em><strong> </strong>y dentro de ella utilizamos la función <em><strong>read_excel</strong></em> la cual permite leer el documento de Excel (en nuestro caso <em>data_flujo.xlsx</em>), e importarlo (en la variable <em>excel</em>). En este caso adicionamos el argumento <em>sheet_name</em>, el cual nos permite dentro del documento buscar la información en una hoja específica. Las hojas de Excel, en el caso de Python, se denominan desde el índice 0. Es decir que en nuestro caso, al expresar <em>sheet_name=1</em> indicamos que lea la <em>Hoja 2</em> del documento.</p>
<p>Creamos el directorio <em><strong>data</strong>, </em>temporalmente vacío, en el se consignarán posteriormente cada lista de datos junto a su nombre (índice).</p>
<p>Ahora detallaremos cómo creamos cada listado de datos:</p>
<p style="text-align: center;"><strong>data</strong>[&#8216;fuentes&#8217;] = <strong>excel</strong>[&#8216;fuentes&#8217;].<strong>tolist</strong>()</p>
<p>En este caso <strong>data</strong>[&#8216;fuentes&#8217;] indica que crearemos el índice &#8216;fuentes&#8217; dentro del directorio <strong>data </strong>(que se encontraba vacío). <strong>excel</strong>[&#8216;fuentes&#8217;] indica que dentro del <em>dataframe</em> Excel, queremos obtener la columna &#8216;fuentes&#8217;. Y la función <strong>tolist</strong>() convertirá esta columna en una lista (formato <em>list</em>). En defintiva, dentro de <strong>data</strong>[&#8216;fuentes&#8217;] tendremos el listado de fuentes obtenido desde Excel.</p>
<p>Este mismo procedimiento lo repetimos para los datos restantes. Al finalizar, dentro del directorio <strong>data</strong> quedará contenida toda la información de entrada del modelo. Como la función <em>create_data</em> retorna la variable <em>data</em>, esto quiere decir, que toda la información de entrada quedará contenida dentro de la función <em>create_data</em>, esto permitirá su uso posterior.</p>
<h3><em>Paso 4: Invocar la data del modelo en el main
		<div id="paso-4-invocar-la-data-del-modelo-en-el-main" data-title="Paso 4: Invocar la data del modelo en el main" class="index-title"></div>
	</em></h3>
<p>Para utilizar los listados o inputs obtenidos desde Excel, necesitamos invocar la función «<em>create_data</em>» esta retornará el directorio con todos los datos.</p>
<p>Recomendamos utilizar el mismo nombre «data» para crear el directorio dentro de esta función:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln">data = create_data()
</span></code></pre>
</div>
<p>Ahora el «data» de esta función (main), contiene el directorio de la función <em>«create_data</em>«. Ya podemos usar los listados.</p>
<p>Quiere decir esto, que para acceder específicamente al listado que contiene los destinos, por ejemplo, es necesario invocar a data[&#8216;destinos&#8217;].</p>
<hr />
<p>Básicamente, estas son las modificaciones que deben realizarse sobre el modelo inicialmente desarrollado. De esta forma quedará nuestro código completo, el cual importará la data de entrada desde un archivo de Excel:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># """From Salazar, ingenieriaindustrialonline.com - Algoritmo de la ruta más corta"""

from __future__ import print_function
from ortools.graph import pywrapgraph #Librería de Google Or Tools
import pandas as pd #Librería pandas para obtener data desde Excel

"""MinCostFlow adaptado a la ruta más corta - interfaz de ejemplo."""

#Este desarrollo requiere que la información extraida desde Excel esté ordenada
#de acuerdo a sus filas. Por ejemplo:

#fila     fuentes   destinos    capacidad   distancia
# 1          0          1           1           4

#Esta fila representa el arco de capacidad 1 y distancia 4 que conecta el nodo fuente 0
#al nodo destino 1

#En el caso de los suministros, debe considerarse el orden de la columna de Excel. Por ejemplo:

#fila      nodos   suministros
# 1          0          1
# 5          4          0
# 9          8         -1

#En este caso, el nodo 0 será de oferta (desde ahí se crea flujo) y el nodo 8 será de demanda (ahí se consume).
#El nodo 4 será un nodo de tránsito (no crea flujo y no consume), solo es un nodo de paso.


#Creamos la data del modelo (La extraemos desde Excel)
def create_data():

    #La variable "excel" traerá la información contenida en el archivo "data_flujo.xlsx" (crea un dataframe organizado en columnas)
    excel = pd.read_excel('data_flujo.xlsx')
    excel_1 = pd.read_excel('data_flujo.xlsx', sheet_name=1) #Función que permite leer una hoja en específico (hoja 2 de Excel - Inicia desde 0)

    data = {} #Crea un directorio llamado data, en él agregaremos cada lista de datos junto con su nombre (índice)

    #La información contenida en Excel viene dada en un dataframe con todos los datos en columnas.
    #A continuación, extraeremos cada columna en específico, desde el dataframe (todas las columnas) &gt; series (columna en específico)
    #Luego, el "tolist" convertirá cada serie en una lista. Esa lista se guardará en el directorio "data" y se etiqueta con el índice correspondiente

    data['fuentes'] = excel['fuentes'].tolist() #Columna en Excel = 'fuentes' &gt; lista en el directorio "data" con la etiqueta (índice) "fuentes"
    data['destinos'] = excel['destinos'].tolist() #Columna en Excel = 'destinos' &gt; lista en el directorio "data" con la etiqueta (índice) "destinos"
    data['capacidad'] = excel['capacidad'].tolist() #Columna en Excel = 'capacidad' &gt; lista en el directorio "data" con la etiqueta (índice) "capacidad"
    data['distancias'] = excel['distancia'].tolist() #Columna en Excel = 'distancia' &gt; lista en el directorio "data" con la etiqueta (índice) "distancia"
    data['suministro'] = excel_1['suministros'].tolist() #Columna en Excel = 'suministro' &gt; lista en el directorio "data" con la etiqueta (índice) "suministro"

    #Quiere decir esto, que para acceder específicamente al listado que contiene los destinos, es necesario invocar a data['destinos'].

    return data #En "create_data" quedará contenido el directorio "data" el cuál contiene todas las listas con la información del modelo.


def main():

  #Para utilizar los listados o inputs obtenidos desde Excel, necesitamos invocar la función "create_data" esta retornará el directorio con todos los datos.
  #Recomendamos utilizar el mismo nombre "data" para crear el directorio dentro de esta función:

  data = create_data() #Ahora el "data" de esta función, contiene el directorio de la función "create_data". Ya podemos usar los listados.

  # Crea una instancia para el solucionador
  min_cost_flow = pywrapgraph.SimpleMinCostFlow()

  # Define cada arco del problema
  for i in range(0, len(data['fuentes'])):
    min_cost_flow.AddArcWithCapacityAndUnitCost(data['fuentes'][i], data['destinos'][i],
                                                data['capacidad'][i], data['distancias'][i])

  # Define los suministros para cada nodo.
  for i in range(0, len(data['suministro'])):
    min_cost_flow.SetNodeSupply(i, data['suministro'][i])


  # Encuentra el costo mínimo entre el nodo 0 y el nodo 8
  if min_cost_flow.Solve() == min_cost_flow.OPTIMAL:
    print('Distancia mínima:', min_cost_flow.OptimalCost())
    print('')
    print('  Arco    Flujo / Capacidad  Distancia')
    for i in range(min_cost_flow.NumArcs()):
      cost = min_cost_flow.Flow(i) * min_cost_flow.UnitCost(i)
      print('%1s -&gt; %1s    %3s   / %3s       %3s' % (
          min_cost_flow.Tail(i),
          min_cost_flow.Head(i),
          min_cost_flow.Flow(i),
          min_cost_flow.Capacity(i),
          cost))
  else:
    print('Hubo un problema con la entrada de flujo de distancia mínima.')

if __name__ == '__main__':
  main()
</span></code></pre>
</div>
<p><em>Ejecutamos el modelo:</em></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png" alt="ruta_mas_corta_solucion" width="611" height="397" class="aligncenter size-full wp-image-26354" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion.png 611w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/ruta_mas_corta_solucion-300x195.png 300w" sizes="(max-width: 611px) 100vw, 611px" /></p>
<hr />
<p>De esta manera hemos logrado integrar una base de datos que se encuentra en un archivo de Excel, el cual podemos modificar en cualquier momento con suma facilidad; un modelo de optimización flexible basado en el algoritmo de flujos de costo mínimo y un solucionador potente. Así entonces, podemos con suma eficiencia, modelar problemas relacionados con el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/"><em><strong>Algoritmo de la Ruta más corta</strong></em></a>.</p>
<p>En próximos artículos abordaremos algunos <em>scripts </em>básicos en <em>Python </em>que nos permitan exportar los resultados del solucionador en algún formato específico, y de acuerdo a una estructura definida.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/">Problema de la ruta más corta en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Problemas de asignación en Google OR-Tools</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Thu, 15 Apr 2021 00:20:49 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Problema de asignación]]></category>
		<category><![CDATA[Problema de transporte]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25366</guid>

					<description><![CDATA[<p>Tal como se ha abordado en artículos anteriores, dentro de la investigación de operaciones, el problema de asignación corresponde a una variación del problema original de transporte. Es uno de los problemas de optimización combinatoria más popularizados debido a su alto grado de aplicación práctica. Suponga que es necesario llevar a cabo un conjunto de &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/">Problemas de asignación en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[
		<div id="introduccion" data-title="Introducción" class="index-title"></div>
	
<p>Tal como se ha abordado en artículos anteriores, dentro de la investigación de operaciones, el <em><strong><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion/">problema de asignación</a></strong></em> corresponde a una variación del <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/solucion-de-un-modelo-de-transporte-mediante-un-algoritmo-de-asignacion/"><strong>problema original de transporte</strong></a>. Es uno de los problemas de optimización combinatoria más popularizados debido a su alto grado de aplicación práctica.</p>
<p>Suponga que es necesario llevar a cabo un conjunto de tareas, y que para ello se cuenta con un conjunto de operarios; además, cada trabajador en particular ejecutando una tarea en particular tiene un costo específico. Pues bien, el problema consiste en asignar a cada trabajador como máximo una tarea específica, sin que dos trabajadores puedan realizar la misma tarea, mientras se minimiza el costo total.</p>
<p><span>En el </span><strong>modelo de asignación, </strong><span>la idea fundamental de resolución es </span><em><b>¿Qué conjunto de fuentes satisface mejor el conjunto de destinos?</b></em></p>
<p>En artículos anteriores hemos planteado la resolución de problemas de asignación mediante el método húngaro (manual) y mediante programación lineal, haciendo uso de WinQSB.</p>
<p>El objetivo de este artículo consiste en utilizar las librerías del software Google OR-Tools para abordar un problema de asignación clásico y una variación del modelo mediante un criterio de maximización.</p>
<h2>El problema</h2>
<p>Con el propósito de evaluar los resultados obtenidos mediante distintos métodos y solucionadores, utilizaremos el mismo problema que abordamos mediante el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion/"><strong>método húngaro</strong></a>.</p>
<blockquote class=" quote-simple "><p>La compañía de manufactura «Jiménez y Asociados» desea realizar una jornada de mantenimiento preventivo a sus tres máquinas principales A, B y C. El tiempo que demanda realizar el mantenimiento de cada máquina es de 1 día, sin embargo la jornada de mantenimiento no puede durar más de un día, teniendo en cuenta que la compañía cuenta con tres proveedores de servicios de mantenimiento debe de asignarse un equipo de mantenimiento a cada máquina para poder cumplir con la realización del mantenimiento preventivo. Teniendo en cuenta que según el grado de especialización de cada equipo prestador de servicios de mantenimiento el costo de la tarea varía para cada máquina en particular, debe de asignarse el equipo correcto a la máquina indicada con el objetivo de minimizar el costo total de la jornada. Los costos asociados se pueden observar en la siguiente tabla:</p></blockquote>
<p><em><strong>Tabla 1</strong></em></p>
<table width="421">
<tbody>
<tr>
<td width="181" style="text-align: center;"></td>
<td width="80" style="text-align: center;"><strong>Máquina 0</strong></td>
<td width="80" style="text-align: center;"><strong>Máquina 1</strong></td>
<td width="80" style="text-align: center;"><strong>Máquina 2</strong></td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo de mantenimiento 0</strong></td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">9</td>
<td style="text-align: center;">5</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo de mantenimiento 1</strong></td>
<td style="text-align: center;">9</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">3</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo de mantenimiento 2</strong></td>
<td style="text-align: center;">6</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">7</td>
</tr>
</tbody>
</table>
<h2>
		<div id="resolviendo-un-problema-de-asignacion-mediante-google-or-tools" data-title="Resolviendo un problema de asignación mediante Google OR-Tools" class="index-title"></div>
	Resolviendo un problema de asignación mediante Google OR-Tools</h2>
<p>De acuerdo a lo mencionado en el artículo de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/"><strong>introducción a Google OR-Tools</strong></a>, esta herramienta soporta múltiples lenguajes de programación, así entonces, haremos uso del lenguaje de programación Python.</p>
<p>Del mismo modo, OR-Tools cuenta con un par de solucionadores para abordar problemas de asignación:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>MIP</li>
<li>CP-SAT</li>
</ul>

		</div>
	
<p>En este caso, utilizaremos el solucionador MIP.</p>
<h3><em>Paso 1: Importar la librería
		<div id="paso-1-importar-la-libreria" data-title="Paso 1: Importar la librería" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código importa las librerías necesarias:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Importar la librería de Google OR-Tools
from<span class="pln"> ortools</span><span class="pun">.</span><span class="pln">linear_solver </span><span class="kwd">import</span><span class="pln"> pywraplp</span></code></pre>
</div>
<h3><em>Paso 2: Declarar el solucionador (MIP)
		<div id="paso-2-declarar-el-solucionador-mip" data-title="Paso 2: Declarar el solucionador (MIP)" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código declara el solucionador mediante el que se abordará el problema:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Declarar el solucionador que abordará el modelo
<span class="pln">solver = pywraplp.Solver.CreateSolver('SCIP')</span></code></pre>
</div>
<h3><em>Paso 3: Crear la data del modelo
		<div id="paso-3-crear-la-data-del-modelo" data-title="Paso 3: Crear la data del modelo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código crea la data del modelo de asignación:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Crear la data del modelo de asignación
<span class="pln">costs = [
    [10, 9, 5],
    [9, 8, 3],
    [6, 4, 7],
]
num_teams = len(costs)
num_machines = len(costs[0])</span></code></pre>
</div>
<p>La matriz de costos corresponde a los valores de la <em><strong>tabla 1</strong></em> para asignar los equipos de mantenimiento a las máquinas.</p>
<p>El número de equipos de mantenimiento y el número de máquinas (tareas) los calcula mediante la función <em>len </em> a partir de la matriz de costos. En un sentido práctico, cuenta el número de elementos de la matriz.</p>
<h3><em>Paso 4: Crear las variables
		<div id="paso-4-crear-las-variables" data-title="Paso 4: Crear las variables" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código crea las variables binarias enteras del problema de asignación (x[i, j]):</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># x[i, j] es una matriz de variables binarias 0-1, que será 1
# si un equipo de mantenimiento i es asignado a la máquina j.
<span class="pln">x = {}
for i in range(num_teams):
    for j in range(num_machines):
        x[i, j] = solver.IntVar(0, 1, '')</span></code></pre>
</div>
<h3><em>Paso 5: Crear las restricciones
		<div id="paso-5-crear-las-restricciones" data-title="Paso 5: Crear las restricciones" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código crea las restricciones del problema de asignación:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Cada equipo de mtto. podrá ser asignado a un máximo de una máquina
for i in range(num_teams):
<span class="pln">    solver.Add(solver.Sum([x[i, j] for j in range(num_machines)]) &lt;= 1)
# Cada máquina será asignada exactamente a un equipo de mantenimiento
for j in range(num_machines):
    solver.Add(solver.Sum([x[i, j] for i in range(num_teams)]) == 1)
</span></code></pre>
</div>
<h3><em>Paso 6: Crear la función objetivo
		<div id="paso-6-crear-la-funcion-objetivo" data-title="Paso 6: Crear la función objetivo" class="index-title"></div>
	</em></h3>
<p>El siguiente fragmento de código crea la función objetivo con un criterio de optimización <em><strong>minimizar</strong></em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Función objetivo con criterio de optimización: minimizar
objective_terms = []
<span class="pln">for i in range(num_teams):
    for j in range(num_machines):
        objective_terms.append(costs[i][j] * x[i, j])
solver.Minimize(solver.Sum(objective_terms))
</span></code></pre>
</div>
<h3><em>Paso 7: Invocar el solucionador
		<div id="paso-7-invocar-el-solucionador" data-title="Paso 7: Invocar el solucionador" class="index-title"></div>
	</em></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Invoca el solucionador
<span class="pln">status = solver.Solve()
</span></code></pre>
</div>
<h3><em>Paso 8: Imprimir la solución
		<div id="paso-8-imprimir-la-solucion" data-title="Paso 8: Imprimir la solución" class="index-title"></div>
	</em></h3>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Configura los parámetros de impresión, las salidas del modelo
<span class="pln">if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
    print('Costo total = ', solver.Objective().Value(), '\n')
    for i in range(num_teams):
        for j in range(num_machines):
            # Test if x[i,j] is 1 (con tolerancia de punto flotante)
            if x[i, j].solution_value() &gt; 0.5:
                print('Equipo %d asignado a la máquina %d. Costo = %d' %
                      (i, j, costs[i][j]))
</span></code></pre>
</div>
<hr />
<p>Es posible que el desarrollo de los ocho pasos anteriores demande algún grado de complejidad subyacente del uso de un lenguaje de programación; sin embargo, es preciso mencionar que, el modelo anterior queda perfectamente configurado, y puede replicarse con modificaciones menores en múltiples problemas de asignación.</p>
<h2>¿Cómo ejecutar el modelo?
		<div id="como-ejecutar-el-modelo" data-title="¿Cómo ejecutar el modelo?" class="index-title"></div>
	</h2>
<p><em><strong>Alternativa 1, ejecución en nuestro equipo:</strong></em></p>
<p>Lo primero que debemos considerar, en el caso de que queramos ejecutar este código en nuestro equipo, es que es preciso contar con la instalación de <em>Python </em>en nuestro equipo de cómputo, así mismo debemos contar con la última versión del comando <em>pip</em> y por supuesto, el software <em>OR-Tools.</em> Una guía detallada de la instalación de estos requerimientos la podrás encontrar en el siguiente enlace:</p>
<p style="text-align: center;"><a href="https://developers.google.com/optimization/install/python/windows" target="_blank" class="shortc-button medium blue">Instalación de OR-Tools para Python</a>
<p>Ahora, lo recomendable es trabajar con algún editor de código práctico (IDE), por ejemplo: <a href="https://www.sublimetext.com/3"><strong><em>Sublime Text</em></strong></a>, o <em><strong>Spyder</strong></em> (Una herramienta más completa y por ende más robusta y pesada).</p>
<p><em><strong>Alternativa 2, ejecución en un entorno virtual (Recomendado):</strong></em></p>
<p>Podemos utilizar del mismo modo, un entorno virtual. En este caso recomendamos el uso de <em><strong>Colaboratory de Google</strong></em>, un entorno que cuenta con todas las herramientas necesarias para nuestros desarrollos. No tendremos que instalar nada en nuestro equipo, y aprovecharemos la potencia de las máquinas de Google.</p>
<hr />
<p>El código completo de nuestro desarrollo lo presentamos a continuación. También puedes ver el cuaderno de este módulo en nuestro <em>Colaboratory: <a href="https://colab.research.google.com/drive/1iY-IXyaM-uMT9JovuZdMlID4DI2iIl3V?usp=sharing"><strong>Asignación</strong></a>.</em></p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Importar la librería de Google OR-Tools
from ortools<span class="pun">.</span>linear_solver <span class="kwd">import</span> pywraplp

# Declarar el solucionador que abordará el modelo
solver = pywraplp.Solver.CreateSolver('SCIP')

# Data del modelo
costs = [
    [10, 9, 5],
    [9, 8, 3],
    [6, 4, 7],
]
num_teams = len(costs)
num_machines = len(costs[0])

# Variables del modelo
x = {}
for i in range(num_teams):
    for j in range(num_machines):
        x[i, j] = solver.IntVar(0, 1, '')

# Restricciones del modelo
for i in range(num_teams):
    solver.Add(solver.Sum([x[i, j] for j in range(num_machines)]) &lt;= 1)
# Cada máquina será asignada exactamente a un equipo de mantenimiento
for j in range(num_machines):
    solver.Add(solver.Sum([x[i, j] for i in range(num_teams)]) == 1)

# Función objetivo con criterio de optimización: minimizar
objective_terms = []
for i in range(num_teams):
    for j in range(num_machines):
        objective_terms.append(costs[i][j] * x[i, j])
solver.Minimize(solver.Sum(objective_terms))

# Invoca el solucionador
status = solver.Solve()

# Configura los parámetros de impresión, las salidas del modelo
if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
    print('Costo total = ', solver.Objective().Value(), '\n')
    for i in range(num_teams):
        for j in range(num_machines):
            # Test if x[i,j] is 1 (con tolerancia de punto flotante)
            if x[i, j].solution_value() &gt; 0.5:
                print('Equipo %d asignado a la máquina %d. Costo = %d' %
                      (i, j, costs[i][j]))
</span></code></pre>
</div>
<p><em>Ejecutamos:</em></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_min_solucion.png" alt="asignacion_min_solucion" width="639" height="108" class="aligncenter size-full wp-image-26358" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_min_solucion.png 639w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_min_solucion-300x51.png 300w" sizes="(max-width: 639px) 100vw, 639px" /></p>
<p>Y bien, tenemos la solución a este problema simple de asignación en menos de 1 segundo.</p>
<hr />
<p>Podemos observar que se ha obtenido la misma respuesta que la lograda mediante el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion/"><strong><em>método húngaro </em></strong>y <em><strong>programación lineal (WinQSB)</strong></em></a><em>.</em></p>
<p>Ahora bien, el modelo de asignación y el <em>script </em>del solucionador quedaron desarrollados en un lenguaje de programación estándar y ampliamente utilizado. Desde luego, las posibilidades de integrar datos de entrada y procesar los datos de salidas son interesantes. Por ejemplo, es posible desarrollar un <em>script </em>mediante el cual el código ya desarrollado tome los datos de entrada desde un archivo de Excel, o desde un servidor externo.</p>
<p>También, es posible desarrollar una interfaz amigable desde la cual se ingrese la información; o vincular los datos de salida con algún modelo o documento determinado.</p>
<hr />
<p>Ahora, podemos evidenciar una de las bondades de este tipo de modelamiento, y es la flexibilidad. Pues bien, vamos a modificar muy fácilmente la información de entrada y el criterio de optimización, para desarrollar un modelo de asignación cuyo criterio sea maximizar. Para ello, vamos a abordar un problema de asignación planteado anteriormente, con el propósito de contrastar los resultados.</p>
<h2>Modelamiento y solución de un problema de maximización mediante Google OR-Tools
		<div id="modelamiento-y-solucion-de-un-problema-de-maximizacion-mediante-google-or-tools" data-title="Modelamiento y solución de un problema de maximización mediante Google OR-Tools" class="index-title"></div>
	</h2>
<h3><em>El problema</em></h3>
<blockquote class=" quote-simple "><p>Una organización de recolección de café cuenta con tres equipos de siembra y cosecha del mismo (equipos 1, 2, 3). Estos equipos de trabajo se encuentran entrenados para trabajar en condiciones particulares del proceso, condiciones como lo son el tipo de suelo, las condiciones del clima y el tipo de grano. La organización cuenta con cuatro terrenos disponibles para efectuar el proceso de siembra y cosecha (terrenos A, B, C, D), estos terrenos tienen condiciones particulares de suelo, clima y tipo de grano. Cada equipo cuenta con la capacidad de efectuar el proceso en solo uno de los terrenos disponibles, salvo el equipo 2, que cuenta con una serie de herramientas tecnológicas que le permiten realizar la siembra y cosecha del grano en dos de los terrenos disponibles.</p></blockquote>
<p><em>Se ha contratado a un Ingeniero Industrial con el objetivo de realizar las asignaciones precisas que maximicen la cantidad de sacos de café cosechados en total. El siguiente tabulado muestra la capacidad (en cientos de sacos) de cosecha de café de cada uno de los equipos dependiendo de cada uno de los terrenos.</em></p>
<p>Dado que existe un equipo que cuenta con la capacidad de trabajar en dos de los terrenos disponibles (desarrollar dos tareas), renombraremos los equipos partiendo desde el 0, así mismo los terrenos, y el equipo 1 y 2 corresponderán a una misma flotilla (misma capacidad de producción) que puede desarrollar dos tareas (atender dos terrenos). Así entonces, el tabulado con las capacidades de producción se muestra a acontinuación:</p>
<p><em><strong>Tabla 2</strong></em></p>
<div class="code-block code-block-2">
<table width="501">
<tbody>
<tr>
<td width="80" style="text-align: center;"><strong></strong></td>
<td width="80" style="text-align: center;"><strong>Terreno 0</strong></td>
<td width="80" style="text-align: center;"><strong>Terreno 1</strong></td>
<td width="80" style="text-align: center;"><strong>Terreno 2</strong></td>
<td width="80" style="text-align: center;"><strong>Terreno 3</strong></td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo 0</strong></td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">7</td>
<td style="text-align: center;">12</td>
<td style="text-align: center;">12</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo 1</strong></td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">15</td>
<td style="text-align: center;">7</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo 2</strong></td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">15</td>
<td style="text-align: center;">7</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Equipo 3</strong></td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">8</td>
</tr>
</tbody>
</table>
<h2>Modelamiento</h2>
<p>Dado que el modelo que desarrollamos anteriormente puede mantenerse en esencia, vamos a detallar las modificaciones que precisa:</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Crear la data del modelo de asignación
<span class="pln">capacidad = [
    [13, 7, 12, 12],
    [10, 13, 15, 7],
    [10, 13, 15, 7],
    [13, 10, 8, 8],
]
num_equipos = len(capacidad)
num_terrenos = len(capacidad[0])</span></code></pre>
</div>
<p>Evidentemente, la matriz del modelo varía de acuerdo al problema, y, en este caso, queremos renombrar las variables de acuerdo al problema planteado.</p>
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code># Función objetivo con criterio de optimización: maximizar
objective_terms = []
<span class="pln">for i in range(num_equipos):
    for j in range(num_terrenos):
        objective_terms.append(capacidad[i][j] * x[i, j])
solver.Maximize(solver.Sum(objective_terms))
</span></code></pre>
</div>
<p>Salvo ajustes de forma, vemos como en el anterior fragmento se modifica el criterio de la optimización: <em>Solver.Maximize</em>. Así entonces, el modelo estaría listo para entregar resultados.</p>
</div>
<p>El código completo, con algunas modificaciones de acuerdo al renombre de las variables y a los textos que acompañarán los resultados se presenta a continuación (también puedes encontrarlo en nuestro <em><a href="https://colab.research.google.com/drive/1wl4aU3Uu71QqNuIQvK89GqczXd6RKylq?usp=sharing">Colaboratory</a>)</em>:</p>
<div class="code-block code-block-2">
<div class="hcb_wrap">
<pre class="prism line-numbers lang-python" data-lang="Python"><code><span class="pln"># Importar la librería de Google OR-Tools
from ortools<span class="pun">.</span>linear_solver <span class="kwd">import</span> pywraplp

# Declarar el solucionador que abordará el modelo
solver = pywraplp.Solver.CreateSolver('SCIP')

# Data del modelo
capacidad = [ 
[13, 7, 12, 12], 
[10, 13, 15, 7], 
[10, 13, 15, 7],
[13, 10, 8, 8], 
] 
num_equipos = len(capacidad) 
num_terrenos = len(capacidad[0])

# Variables del modelo
x = {}
for i in range(num_equipos):
    for j in range(num_terrenos):
        x[i, j] = solver.IntVar(0, 1, '')

# Restricciones del modelo
for i in range(num_equipos):
    solver.Add(solver.Sum([x[i, j] for j in range(num_terrenos)]) &lt;= 1)

for j in range(num_terrenos):
    solver.Add(solver.Sum([x[i, j] for i in range(num_equipos)]) == 1)

# Función objetivo con criterio de optimización: maximizar
objective_terms = []
for i in range(num_equipos):
    for j in range(num_terrenos):
        objective_terms.append(capacidad[i][j] * x[i, j])
solver.Maximize(solver.Sum(objective_terms))

# Invoca el solucionador
status = solver.Solve()

# Configura los parámetros de impresión, las salidas del modelo
if status == pywraplp.Solver.OPTIMAL or status == pywraplp.Solver.FEASIBLE:
    print('Producción total = ', solver.Objective().Value(), '\n')
    for i in range(num_equipos):
        for j in range(num_terrenos):
            # Test if x[i,j] is 1 (con tolerancia de punto flotante)
            if x[i, j].solution_value() &gt; 0.5:
                print('Equipo %d asignado al terreno %d. Producción = %d' %
                      (i, j, capacidad[i][j]))
</span></code></pre>
</div>
<p><em>Ejecutamos el modelo:</em></p>
<p><img decoding="async" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_max_solucion.png" alt="asignacion_max_solucion" width="629" height="125" class="aligncenter size-full wp-image-26359" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_max_solucion.png 629w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/asignacion_max_solucion-300x60.png 300w" sizes="(max-width: 629px) 100vw, 629px" /></p>
<p>Podemos observar que se ha obtenido la misma respuesta que la lograda mediante el <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion/"><strong><em>método húngaro </em></strong>y <em><strong>programación lineal (WinQSB)</strong></em></a><em>.</em></p>
<hr />
<p>De esta manera se evidencia una de las ventajas del modelamiento haciendo uso de Google OR-Tools, puesto que, podemos pasar de modelar un problema a otro mediante la modificación mínima de los datos de entrada. Del mismo modo, podemos modificar criterios de optimización con suma facilidad, y realizar modificaciones de forma que en algunos casos pueden ser relevantes.</p>
<p>En próximos artículos abordaremos algunos <em>scripts </em>básicos en <em>Python </em>que permitan integrar el modelo de optimización con la data contenida en otros formatos; así mismo revisaremos algunas funciones que nos permiten organizar y exportar los resultados del solucionador.</p>
</div>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/">Problemas de asignación en Google OR-Tools</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>¿Qué es y para qué sirve Google OR-Tools?</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Wed, 14 Apr 2021 00:25:27 +0000</pubDate>
				<category><![CDATA[Actualidad]]></category>
		<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Google OR-Tools]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Programación de restricciones]]></category>
		<category><![CDATA[Programación lineal]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Solucionadores]]></category>
		<category><![CDATA[VRP]]></category>
		<guid isPermaLink="false">https://ingenieriaindustrialonline.com/?p=25340</guid>

					<description><![CDATA[<p>Google OR-Tools es un paquete de software portable de código abierto para resolución de problemas de optimización. Así mismo, cuenta con metaheurísticas que buscan encontrar la mejor solución a un problema entre un conjunto de posibles soluciones. Or-Tools es una herramienta potente, diseñada para abordar los problemas más difíciles del mundo en el enrutamiento de &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/">¿Qué es y para qué sirve Google OR-Tools?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><strong>
		<div id="que-es-google-or-tools-y-para-que-sirve" data-title="¿Qué es Google Or-Tools y para qué sirve?" class="index-title"></div>
	 </strong></p>
<p><strong>Google OR-Tools es un paquete de software portable de código abierto para resolución de problemas de optimización</strong>. Así mismo, cuenta con metaheurísticas que buscan encontrar la mejor solución a un problema entre un conjunto de posibles soluciones. Or-Tools es una herramienta potente, diseñada para abordar los problemas más difíciles del mundo en el enrutamiento de vehículos, los flujos, la programación de números enteros y lineales y la programación de restricciones.</p>
<p>Una de las ventajas potenciales de Google Or-Tools consiste en que, a pesar de estar desarrollado en lenguaje C++, permite el modelamiento de problemas en múltiples lenguajes de programación, del mismo modo, cuenta con múltiples solucionadores específicos para resolverlos, algunos comerciales y algunos de código abierto.</p>
<table width="422">
<tbody>
<tr>
<td width="102"></td>
<td style="text-align: center;" colspan="4" width="320">Lenguajes de programación</td>
</tr>
<tr>
<td style="text-align: center;" width="102">OR-Tools</td>
<td style="text-align: center;" width="80">Python</td>
<td style="text-align: center;" width="80">C++</td>
<td style="text-align: center;" width="80">Java</td>
<td style="text-align: center;" width="80">C#</td>
</tr>
<tr>
<td style="text-align: center;" width="102">Compatibilidad</td>
<td style="text-align: center;" width="80">✓</td>
<td style="text-align: center;" width="80"> ✓</td>
<td style="text-align: center;" width="80"> ✓</td>
<td style="text-align: center;" width="80"> ✓</td>
</tr>
</tbody>
</table>
<p>Fuente: <a href="https://developers.google.com/optimization"><em><strong>Google OR-Tools</strong></em></a></p>
<p>Centrémonos en la posibilidad de modelar un problema de optimización en un lenguaje de programación, puesto que las oportunidades son cada vez más interesantes. En la academia, <strong>la enseñanza de la <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-la-investigacion-de-operaciones/">investigación de operaciones</a>, el proceso de modelamiento y el mundo de los solucionadores, es tan a menudo, atomizado, que carece de practicidad</strong>.</p>
<p>La obtención, el procesamiento y la disposición de un volumen considerable de datos, su convergencia en un modelo de optimización y su eventual integración hacia un solucionador, son procesos complejos, que implican con regularidad la reunión de herramientas poco compatibles.</p>
<p>Google Or-Tools, al permitir el <strong>modelamiento en diversos lenguajes de programación</strong>, ofrece un sinfín de posibilidades de integración entre herramientas de obtención y tratamiento de información; así mismo, ofrece posibilidades ilimitadas de desarrollo de soluciones, como por ejemplo interfaces de usuario amigables, o integración con sistemas de integración de recursos, app’s, plataformas webs, dispositivos con un sistema de conexión a internet de las cosas (IoT), y mucho más.</p>
<p>Suponga, por ejemplo, que quiere desarrollar un modelo que le permita mejorar la eficiencia del proceso de recogida de paquetes, un típico problema de distribución y recolección de última milla, abordado dentro de la investigación de operaciones como un problema <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-vrp-con-google-or-tools/"><strong>VRP (Vehicle Routing Problem)</strong></a>. En su versión básica, requerirá de un conjunto de nodos y el costo que representa el arco que une a cada par (origen y destino). Sin embargo, asumamos que el criterio de optimización está basado en la distancia entre nodos, y, además, que se pretende, dado el volumen de datos requeridos, integrar una interfaz de programación externa que le permita obtener los tiempos entre nodos desde una plataforma como Google Maps.</p>
<p>Por otro lado, la <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-capacitados-cvrp-con-google-or-tools/"><strong>flota de vehículos disponibles tiene una capacidad limitada (CVRP)</strong></a>, y la información relacionada al número de vehículos y su capacidad volumétrica, se encuentra actualizada todos los días en archivo <a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="Los archivos CSV (del inglés comma-separated values) son un tipo de documento en formato abierto sencillo para representar datos en forma de tabla, en las que las columnas se separan por comas."><em>csv</em></a> accesible desde el servidor de la empresa.</p>
<p>La información relacionada con los destinos, o puntos de entrega, así mismo la dimensión volumétrica de los paquetes se puede obtener desde una solución desarrollada a la medida por la compañía, la cual mantiene la información actualizada en tiempo real y exporta diariamente estos datos en formato <a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="Documento de Microsoft Excel"><em>xls</em></a>.</p>
<p>Pretender integrar los datos de entrada, que a su vez son salidas desde plataformas diferentes, en formatos diferentes; algunos de ellos, información en tiempo real; procesar y organizar dicha información, integrarlos en un modelo de optimización y luego ejecutar un solucionador, haciendo uso de las herramientas típicas como Solver, Storm, Tora, WinQSB, Crystall Ball, etc., resultaría sumamente complejo, y en algunos casos imposible.</p>
<p>Sin embargo, haciendo uso, por ejemplo, de un lenguaje de programación como <a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="Lenguaje de programación interpretado cuya filosofía hace hincapié en la legibilidad de su código">Python</a>,</em> es posible desarrollar un código (<a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="En informática, un script, secuencia de comandos">script</a>)</em> que automatice las solicitudes hacia una <a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="La interfaz de programación de aplicaciones, conocida también por la sigla API">API</a></em> de Google Maps (<a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="HTML, siglas en inglés de HyperText Markup Language (‘lenguaje de marcado de hipertexto’), hace referencia al lenguaje de marcado para la elaboración de páginas web"><em>HTML</em></a>), decodifique sus respuestas (<a data-toggle="tooltip" data-placement="top" class="post-tooltip tooltip-top" title="JSON (acrónimo de JavaScript Object Notation, «notación de objeto de JavaScript») es un formato de texto sencillo para el intercambio de datos">JSON</a>),</em> permitiendo disponer de la información relacionada con la distancia más probable para una hora de programación de salida de la flota en particular.</p>
<p>Así mismo, es posible ejecutar un script básico que extraiga la información contenida en un archivo <em>csv</em> y un archivo <em>xls</em>. Posteriormente, y haciendo uso de las librerías de enrutamiento de Google Or-Tools, toda esta data puede integrarse en un modelo de programación basada en restricciones que ejecutará una metaheurística como solucionador.<img decoding="async" class="aligncenter wp-image-25346" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Or-Tools.jpg" alt="Or-Tools" width="481" height="627" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Or-Tools.jpg 652w, https://ingenieriaindustrialonline.com/wp-content/uploads/2021/04/Or-Tools-230x300.jpg 230w" sizes="(max-width: 481px) 100vw, 481px" /></p>
<p>Como resultado, diariamente es posible mediante la integración de información actualizada en tiempo real en múltiples plataformas, a través de múltiples servidores, poder ejecutar un modelo de programación basado en restricciones que me permita obtener las rutas más eficientes de acuerdo a la metaheurística y a los parámetros del solucionador.</p>
<p>Dicho de otro modo, es posible integrar al modelo y al solucionador cualquier desarrollo que se base en un lenguaje común, por ejemplo: Python.</p>
<p style="text-align: center;"><strong><em>¡Sí, las ventajas potenciales son asombrosas!</em></strong></p>
<h2><strong>¿Qué tipo de modelos se pueden resolver mediante Google OR-Tools?</strong>
		<div id="que-tipo-de-modelos-se-pueden-resolver-mediante-google-or-tools" data-title="<strong>¿Qué tipo de modelos se pueden resolver mediante Google OR-Tools</strong>?" class="index-title"></div>
	</h2>
<p>Google OR-Tools incluye solucionadores para:</p>

		<div class="checklist tie-list-shortcode">
<ul>
<li>Programación de restricciones</li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-en-google-or-tools/"><strong>Programación lineal</strong></a></li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/programacion-lineal-entera-con-google-or-tools/"><strong>Programación entera y mixta</strong></a></li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problemas-de-asignacion-en-google-or-tools/"><strong>Problemas de asignación</strong></a></li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/"><strong>Problemas de flujos</strong></a></li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-vrp-con-google-or-tools/"><strong>Problemas de enrutamiento de vehículos</strong></a></li>
<li>Algoritmos gráficos</li>
</ul>

		</div>
	
<h2><strong>¿Qué tan potente es el solucionador?</strong>
		<div id="que-tan-potente-es-el-solucionador" data-title="¿Qué tan potente es el solucionador?" class="index-title"></div>
	</h2>
<p>De acuerdo al MiniZinc Challenge 2020, competencia internacional de solucionadores de programación de restricciones, Google OR-Tools obtuvo tres medallas doradas en cuatro categorías de evaluación.</p>
<table width="433">
<tbody>
<tr>
<td style="text-align: center;" width="122"><strong>Categoría</strong></td>
<td style="text-align: center;" width="95"><strong>Oro</strong></td>
<td style="text-align: center;" width="105"><strong>Plata</strong></td>
<td style="text-align: center;" width="111"><strong>Bronce</strong></td>
</tr>
<tr>
<td style="text-align: center;"><strong>Búsqueda fija</strong></td>
<td style="text-align: center;">SICStus Prolog</td>
<td style="text-align: center;">JaCoP</td>
<td style="text-align: center;">Choco 4</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Búsqueda libre</strong></td>
<td style="text-align: center;"><strong>OR-Tools</strong></td>
<td style="text-align: center;">PicatSAT</td>
<td style="text-align: center;">Mistral 2.0</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Búsqueda paralela</strong></td>
<td style="text-align: center;"><strong>OR-Tools</strong></td>
<td style="text-align: center;">PicatSAT</td>
<td style="text-align: center;">Mistral 2.0</td>
</tr>
<tr>
<td style="text-align: center;" width="122"><strong>Búsqueda abierta</strong></td>
<td style="text-align: center;"><strong>OR-Tools</strong></td>
<td style="text-align: center;">sunny-cp—</td>
<td style="text-align: center;">PicatSAT</td>
</tr>
</tbody>
</table>
<p>Fuente: <a href="https://www.minizinc.org/challenge2020/results2020.html"><em><strong>MiniZinc Challenge (2020)</strong></em></a></p>
<h2><strong>¿Cómo puedo instalar Google OR-Tools?</strong>
		<div id="como-puedo-instalar-google-or-tools" data-title="<strong>¿Cómo puedo instalar Google OR-Tools?</strong>" class="index-title"></div>
	</h2>
<p>Con asombro descubrimos desde esta plataforma (Ingeniería Industrial Online), que en la actualidad muchas personas nos escriben solicitando información para instalar WinQSB en computadores de 64 bits. El asombro es aún mayor cuando descubrimos que algunas instituciones de formación aún utilizan WinQSB como solucionador de modelos de investigación de operaciones.</p>
<p>Pues bien, esas incompatibilidades tecnológicas, no solo se han sorteado con Google OR-Tools, existen además múltiples solucionadores perfectamente compatibles con gran parte de los sistemas operativos de la actualidad. Ahora bien, OR-Tools es compatible con sistemas Linux, Mac y Windows; sin embargo, su instalación varía de acuerdo al lenguaje de programación que quieran utilizar.</p>
<p>Una guía completa de instalación la pueden encontrar en el siguiente enlace:</p>
<p><center><a href="https://developers.google.com/optimization/install" target="_blank" class="shortc-button medium blue">Guía de Instalación de<strong> Google OR-Tools</strong></a></center>&nbsp;</p>
<h2>Ventajas de ser un software de código abierto
		<div id="ventajas-de-ser-un-software-de-codigo-abierto" data-title="Ventajas de ser un software de código abierto" class="index-title"></div>
	</h2>
<p>Una de la mayores ventajas que presenta Google OR-Tools consiste en que es un software de código abierto, de tal manera que su modelo de desarrollo se basa en la colaboración. Esto representa un beneficio potencial, dado que cientos de personas, día a día, se encuentran formulando modelos de optimización en diversos lenguajes de programación.</p>
<p>Una evidencia de ello es la colaboración entorno a la formulación de problemas de enrutamiento de vehículos, ya que a través de las contribuciones que pueden observarse en <a href="http://github.com/google/or-tools">GitHub</a> o un su foro de discusión en <a href="https://groups.google.com/g/or-tools-discuss">Google Groups</a>, se han desarrollado modelos para diversas variaciones del modelo base, dentro de las que podemos encontrar:</p>

		<div class="thumbup tie-list-shortcode">
<ul>
<li>TSP</li>
<li><strong><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-vrp-con-google-or-tools/">VRP</a></strong></li>
<li><a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-enrutamiento-de-vehiculos-capacitados-cvrp-con-google-or-tools/"><strong>CVRP (Problema de enrutamiento de vehículos con restricciones de capacidad)</strong></a></li>
<li>VRPTW (Problema de enrutamiento de vehículos con ventanas de tiempo)</li>
<li>Problemas de enrutamiento de vehículos con restricciones de recursos</li>
<li>Problemas de enrutamiento de vehículos con abandonos y penalizaciones</li>
<li>Problemas de enrutamiento de vehículos con recogidas y entregas</li>
</ul>

		</div>
	
<p>Y mucho más, como colaboraciones a problemas de optimización específicos, integraciones con API&#8217;s desarrolladas por Google, como Distance Matrix API de Google Maps.</p>
<h2>¿Los requerimientos técnicos son un limitante para su uso?</h2>

		<div id="los-requerimientos-tecnicos-son-un-limitante-para-su-aprendizaje" data-title="¿Los requerimientos técnicos son un limitante para su aprendizaje?" class="index-title"></div>
	
<p>De ninguna manera. Y le vamos a explicar la razón.</p>
<p>Si queremos utilizar Google OR Tools en nuestro ordenador, sí tenemos algunos requerimientos técnicos, ahora bien, todos gratuitos. Requerimos la instalación de <em>Python</em>, un entorno de desarrollo integrado (IDE), y de la instalación de su librería (or tools).</p>
<p>Sin embargo, para su aprendizaje y uso básico, podemos utilizar entornos virtuales, como por ejemplo: <a href="https://colab.research.google.com/"><em><strong>Colaboratory de Google</strong></em></a>. ¿En qué consiste?</p>
<p>Colaboratory, también llamado «Colab», te permite ejecutar y programar en Python en tu navegador con las siguientes ventajas:</p>

		<div class="thumbup tie-list-shortcode">
<ul>
<li>No requiere configuración</li>
<li>Da acceso gratuito a GPUs (Unidades de procesamiento)</li>
<li>Permite compartir contenido fácilmente</li>
</ul>

		</div>
	
<p>Es decir, podemos utilizar toda la potencia de las máquinas de Google para ejecutar nuestros modelos&#8230; <em>¡Genial! </em></p>
<p>De hecho, vamos a utilizar esta herramienta en cada modelo que desarrollemos en este portal, compartiremos nuestros códigos y esperamos que les sean de gran utilidad.</p>
<hr />
<p>En próximos artículos desarrollaremos modelos de optimización y metaheurísticas haciendo uso de Google OR-Tools, de tal manera que de la mano descubran esta gran herramienta.</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/">¿Qué es y para qué sirve Google OR-Tools?</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/que-es-y-para-que-sirve-google-or-tools/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Algoritmo de la ruta más corta</title>
		<link>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/</link>
					<comments>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/#respond</comments>
		
		<dc:creator><![CDATA[Bryan Salazar López]]></dc:creator>
		<pubDate>Wed, 12 Jun 2019 19:14:22 +0000</pubDate>
				<category><![CDATA[Investigación de operaciones]]></category>
		<category><![CDATA[Investigación de Operaciones]]></category>
		<category><![CDATA[Ruta más corta]]></category>
		<category><![CDATA[Teoría de redes]]></category>
		<category><![CDATA[WinQSB]]></category>
		<guid isPermaLink="false">http://contentlab.co/ingenieria/?p=1233</guid>

					<description><![CDATA[<p>Ya el nombre de este tipo de algoritmo es bastante sugestivo. El algoritmo de la ruta más corta  consiste, si es necesario decirlo, en una modalidad de problemas de redes, en la cual se debe determinar el plan de rutas que genere la trayectoria con la mínima distancia total, que una un nodo fuente con un nodo destino, sin importar el &#8230;</p>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/">Algoritmo de la ruta más corta</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Ya el nombre de este tipo de algoritmo es bastante sugestivo. El <strong>algoritmo de la ruta más corta</strong>  consiste, si es necesario decirlo, en una modalidad de <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/teoria-de-redes/"><em><strong>problemas de redes</strong></em></a>, en la cual se debe determinar el plan de rutas que genere la trayectoria con la mínima distancia total, que una un <em>nodo fuente</em> con un <em>nodo destino</em>, sin importar el número de nodos que existan entre estos.</p>

		<div class="box info  alignleft">
			<div class="box-inner-block">
				<span class="fa tie-shortcode-boxicon"></span>Esta modalidad de problemas puede solucionarse como un modelo de transbordo normal, sin embargo la principal sugerencia es la de establecer una oferta en el nodo fuente igual a una unidad (1) y establecer una demanda en el arco destino igual a una unidad (1).
			</div>
		</div>
	
<p>Vale la pena considerar, que en la práctica, es muy frecuente la utilización del algoritmo resultante con variaciones que consisten en la minimización de tiempos, no necesariamente de distancias.</p>
<h2>Algoritmo de la ruta más corta &#8211; Ejemplo</h2>
<h3>El caso</h3>
<blockquote class="aligncenter quote-simple "><p>Un minero ha quedado atrapado en una mina, la entrada a la mina se encuentra ubicada en el nodo 1, se conoce de antemano que el minero permanece atrapado en el nodo 9, para llegar a dicho nodo hay que atravesar una red de túneles que van conectados entre sí. El tiempo de vida que le queda al minero sin recibir auxilio es cada vez menor y se hace indispensable hallar la ruta de acceso al nodo 9 más corta. Las distancias entre nodos de la mina se encuentran en la siguiente gráfica dadas en cientos de metros. Formule un modelo de transbordo y resuelva mediante cualquier paquete de herramientas de investigación operativa que permita establecer la ruta más corta para poder así auxiliar al minero.</p></blockquote>
<p><img decoding="async" class="aligncenter size-full wp-image-2674" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-293.png" alt="" width="662" height="287" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-293.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-293-300x130.png 300w" sizes="(max-width: 662px) 100vw, 662px" /></p>
<div id="cc-m-6470947513" class="j-module n j-header ">
<h3 id="cc-m-header-6470947513" class=""><em>Variables de decisión</em></h3>
</div>
<div id="cc-m-6470947613" class="j-module n j-text ">
<p>El nombre de las variables en este caso poco importa, dado que de ser escogida para la solución básica eso significa simplemente que será empleada como ruta para ir a rescatar al minero, sin embargo nada tiene de malo el que se le pueda asociar con el envío de unidades desde la entrada de la mina hacia el minero, por ende puede sugerirse este como nombre de las variables. «<em>Cantidad de unidades enviadas desde el nodo i hacia el nodo j».</em></p>
<p><strong>X<sub>12</sub> =</strong> Cantidad de unidades enviadas desde el nodo 1, hacia el nodo 2</p>
<p><strong>X<sub>13</sub> =</strong> Cantidad de unidades enviadas desde el nodo 1, hacia el nodo 3</p>
<p><strong>X<sub>23</sub> =</strong> Cantidad de unidades enviadas desde el nodo 2, hacia el nodo 3</p>
<p><strong>X<sub>24</sub> =</strong> Cantidad de unidades enviadas desde el nodo 2, hacia el nodo 4</p>
<p><strong>X<sub>32</sub> =</strong> Cantidad de unidades enviadas desde el nodo 3, hacia el nodo 2</p>
<p><strong>X<sub>34</sub> =</strong> Cantidad de unidades enviadas desde el nodo 3, hacia el nodo 4</p>
<p><strong>X<sub>35</sub> =</strong> Cantidad de unidades enviadas desde el nodo 3, hacia el nodo 5</p>
<p><strong>X<sub>46</sub> =</strong> Cantidad de unidades enviadas desde el nodo 4, hacia el nodo 6</p>
<p><strong>X<sub>47</sub> =</strong> Cantidad de unidades enviadas desde el nodo 4, hacia el nodo 7</p>
<p><strong>X<sub>54</sub> =</strong> Cantidad de unidades enviadas desde el nodo 5, hacia el nodo 4</p>
<p><strong>X<sub>56</sub> =</strong> Cantidad de unidades enviadas desde el nodo 5, hacia el nodo 6</p>
<p><strong>X<sub>57</sub> =</strong> Cantidad de unidades enviadas desde el nodo 5, hacia el nodo 7</p>
<p><strong>X<sub>58</sub> =</strong> Cantidad de unidades enviadas desde el nodo 5, hacia el nodo 8</p>
<p><strong>X<sub>67</sub> =</strong> Cantidad de unidades enviadas desde el nodo 6, hacia el nodo 7</p>
<p><strong>X<sub>69</sub> =</strong> Cantidad de unidades enviadas desde el nodo 6, hacia el nodo 9</p>
<p><strong>X<sub>76</sub> =</strong> Cantidad de unidades enviadas desde el nodo 7, hacia el nodo 6</p>
<p><strong>X<sub>78</sub> =</strong> Cantidad de unidades enviadas desde el nodo 7, hacia el nodo 8</p>
<p><strong>X<sub>79</sub> =</strong> Cantidad de unidades enviadas desde el nodo 7, hacia el nodo 9</p>
<p><strong>X<sub>87</sub> =</strong> Cantidad de unidades enviadas desde el nodo 8, hacia el nodo 7</p>
<p><strong>X<sub>89</sub> =</strong> Cantidad de unidades enviadas desde el nodo 8, hacia el nodo 9</p>
<h3><em>Restricciones</em></h3>
<div id="cc-m-6470947813" class="j-module n j-text ">
<p style="padding-left: 40px;"><em><strong>Restricciones de Oferta y Demanda</strong></em></p>
<p>Hay que recordar que el objetivo de este modelo es la consecución de un plan de ruta que nos permita encontrar al minero lo más pronto posible al recorrer la distancia mínima posible, por ende la clave para plantear el modelo como si fuese de transbordo es establecer una demanda y oferta igual a la unidad (1).</p>
<p><strong>X<sub>12</sub> + X<sub>13</sub> =</strong> 1</p>
<p><strong>X<sub>69</sub> + X<sub>79</sub> + X<sub>89</sub> =</strong> 1</p>
<p style="padding-left: 40px;"><em><strong>Restricciones de Balance</strong></em></p>
<p><strong>X<sub>12</sub> + X<sub>32</sub> &#8211; X<sub>23</sub> &#8211; X<sub>24</sub> = 0</strong></p>
<p><strong>X<sub>13</sub> + X<sub>23</sub> &#8211; X<sub>32</sub> &#8211; X<sub>34</sub> &#8211; X<sub>3</sub><sub>5</sub> =</strong> 0</p>
<p><strong>X<sub>24</sub> + X<sub>34</sub> + X<sub>54</sub> &#8211; X<sub>46</sub> &#8211; X<sub>47</sub> =</strong> 0</p>
<p><strong>X<sub>35</sub> &#8211; X<sub>54</sub> &#8211; X<sub>56</sub> – X<sub>57</sub> – X<sub>58</sub> =</strong> 0</p>
<p><strong>X<sub>46</sub> + X<sub>56</sub> + X<sub>57</sub> &#8211; X<sub>67</sub> – X<sub>69</sub> =</strong> 0</p>
<p><strong>X<sub>67</sub> + X<sub>47</sub> + X<sub>57</sub> + X<sub>87</sub> – X<sub>76</sub> – X<sub>78</sub> – X<sub>79</sub> =</strong> 0</p>
<p><strong>X<sub>78</sub> + X<sub>58</sub> – X<sub>89</sub> =</strong> 0</p>
<p>En palabras sencillas: «Todo lo que entra a cada nodo es igual a lo que sale de él»</p>
</div>
<h3><em>Función objetivo</em></h3>
<div id="cc-m-6470948013" class="j-module n j-text ">
<p><strong>Z<sub>MIN</sub> = 4X<sub>12</sub> + 2X<sub>13</sub> + 2X<sub>23</sub> + 7X<sub>24</sub> + 4X<sub>32</sub> + 9X<sub>34</sub> + 6X<sub>35</sub> + 1X<sub>46</sub> + 5X<sub>47</sub> + 2X<sub>54</sub> + 4X<sub>56</sub> + 3X<sub>57</sub>+ 2X<sub>58</sub> + 1X<sub>67</sub> + 5X<sub>69</sub> + 4X<sub>76</sub> + 3X<sub>78</sub> + 5X<sub>79</sub> + 2X<sub>87</sub> + 7X<sub>89</sub></strong></p>
</div>
<h3>Ingresando los datos en WinQSB</h3>
<p><img decoding="async" class="aligncenter size-full wp-image-2675" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-294.png" alt="" width="1023" height="234" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-294.png 1023w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-294-300x69.png 300w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-294-768x176.png 768w" sizes="(max-width: 1023px) 100vw, 1023px" /></p>
<div id="cc-m-6470948313" class="j-module n j-header ">
<h3 id="cc-m-header-6470948313" class="">Solución obtenida mediante WinQSB</h3>
</div>
<div id="cc-m-6470948413" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><a rel="lightbox" data-href="https://image.jimcdn.com/app/cms/image/transf/dimension=origxorig:format=jpg/path/s075f076504dfea8d/image/i401a4f149a2639f4/version/1514941035/la-ruta-m%C3%A1s-corta.jpg" data-title="ingenieriaindustrialonline-60e499.ingress-alpha.easywp.com" data-index="0"><img decoding="async" class="aligncenter size-full wp-image-2676" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-295.png" alt="" width="552" height="635" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-295.png 552w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-295-261x300.png 261w" sizes="(max-width: 552px) 100vw, 552px" /></a><figcaption></figcaption></figure>
</div>
<div id="cc-m-6470948613" class="j-module n j-text ">
<p>La ruta más corta para rescatar al minero  tiene como distancia total 1600 metros (dado que las distancias estaban dadas en cientos de metros) y es tal como se muestra en la siguiente gráfica:</p>
<p><img decoding="async" class="aligncenter wp-image-2677 size-full" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-296.png" alt="La ruta más corta - Bryan Salazar López" width="670" height="291" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-296.png 670w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-296-300x130.png 300w" sizes="(max-width: 670px) 100vw, 670px" /></p>
<p>Sin embargo, WinQSB cuenta con una metodología mucho más sencilla de resolución de algoritmos de ruta más corta, metodología que explicaremos más adelante, de todas formas hemos encontrado cómo, aplicando debidamente la razón y un <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-transbordo/"><em><strong>algoritmo conocido como el de transbordo</strong></em></a>, podemos solucionar problemas distintos en teoría.</p>

		<div class="clearfix"></div>
		<hr style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid">
	
<h3 id="cc-m-header-5185600813" class="">Solución del algoritmo de la ruta más corta mediante WinQSB</h3>
<p>El módulo del WinQSB que permite la resolución del <strong>algoritmo de la ruta más corta</strong> es el <em>Network Modeling</em>, el cual utiliza una interfaz muy sencilla en forma de matriz en la cual hay que ingresar el valor de los ramales (dependiendo del contexto este valor puede representar distancias, tiempo, costos etc.) correspondiente a cada relación de un nodo con otro.</p>
<p><em><strong>Paso a paso</strong></em></p>
<p>Primero se debe ingresar al módulo Network Modeling del paquete WinQSB, una vez nos encontremos en este aparecerá el menú que se muestra en la siguiente gráfica, menú en el cual tendremos que seleccionar la opción <span class="tie-highlight tie-highlight-green"><strong><em>Shortest Path Problem </em>(Problema de la ruta más corta)<em>.</em></strong></span>
<div id="cc-m-5185811713" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><img decoding="async" class="aligncenter size-full wp-image-2678" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-297.png" alt="" width="626" height="460" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-297.png 626w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-297-300x220.png 300w" sizes="(max-width: 626px) 100vw, 626px" /><figcaption></figcaption></figure>
</div>
<div id="cc-m-5185812813" class="j-module n j-text ">
<p>Además en este menú emergente debemos de ingresar la cantidad de nodos que conforman la red del problema y tenemos la posibilidad de asignarle un nombre al mismo, en nuestro caso la cantidad de nodos de la red es igual a 9; clic en OK y aparecerá la siguiente ventana.</p>
</div>
<div id="cc-m-5185833013" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><img decoding="async" class="aligncenter size-full wp-image-2679" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-298.png" alt="" width="662" height="330" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-298.png 662w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-298-300x150.png 300w" sizes="(max-width: 662px) 100vw, 662px" /><figcaption></figcaption></figure>
</div>
<div id="cc-m-5185835713" class="j-module n j-text ">
<p>En esta ventana se debe ingresar la magnitud de cada ramal correspondiente a cada relación entre los nodos, tal como veremos a continuación.</p>
</div>
<div id="cc-m-5185848613" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><img decoding="async" class="aligncenter size-full wp-image-2680" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-299.png" alt="" width="660" height="324" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-299.png 660w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-299-300x147.png 300w" sizes="(max-width: 660px) 100vw, 660px" /><figcaption></figcaption></figure>
</div>
<div id="cc-m-5185855113" class="j-module n j-text ">
<p>Damos clic en <em>Solve and Analize</em> y tendremos un menú emergente en el cual tendremos que seleccionar el nodo fuente y el nodo destino, tal como se muestra en la siguiente gráfica.</p>
</div>
<div id="cc-m-5185869413" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><a rel="lightbox" data-href="https://image.jimcdn.com/app/cms/image/transf/dimension=origxorig:format=jpg/path/s075f076504dfea8d/image/i822f908d88f41cb6/version/1416592109/image.jpg" data-title="ingenieriaindustrialonline-60e499.ingress-alpha.easywp.com" data-index="0"><img decoding="async" class="aligncenter size-full wp-image-2681" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-300.png" alt="" width="429" height="350" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-300.png 429w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-300-300x245.png 300w" sizes="(max-width: 429px) 100vw, 429px" /></a><figcaption></figcaption></figure>
</div>
<div id="cc-m-5185872113" class="j-module n j-text ">
<p>Una vez efectuada la selección tendremos la opción de ver el tabulado final y la opción de ver un paso a paso gráfico; para el tabulado final click en <strong><em>SOLVE</em></strong> y para el paso a paso clic en <em><strong>SOLVE AND DISPLAY STEPS</strong></em>.</p>
</div>
<div id="cc-m-5185899013" class="j-module n j-imageSubtitle ">
<figure class="cc-imagewrapper cc-m-image-align-3"><a rel="lightbox" data-href="https://image.jimcdn.com/app/cms/image/transf/dimension=origxorig:format=jpg/path/s075f076504dfea8d/image/i17c2f4efb246e1b8/version/1416592119/image.jpg" data-title="ingenieriaindustrialonline-60e499.ingress-alpha.easywp.com" data-index="0"><img decoding="async" class="aligncenter size-full wp-image-2682" src="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-301.png" alt="" width="625" height="510" srcset="https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-301.png 625w, https://ingenieriaindustrialonline.com/wp-content/uploads/2019/06/Sin-título-301-300x245.png 300w" sizes="(max-width: 625px) 100vw, 625px" /></a></figure>
<p>Podemos cotejar la solución que obtuvimos al plantear este problema como un modelo de transbordo con esta solución. La eficiencia se encuentra determinada en escoger la herramienta adecuada para resolver el problema planteado.</p>
<hr />
<p><span>Le recomendamos revisar: <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/problema-de-la-ruta-mas-corta-en-google-or-tools/"><em><strong>Problema de la ruta más corta en Google OR-Tools</strong></em></a></span><em>. </em><span>Descubre esta poderosa herramienta de modelamiento y solución de problemas de optimización.</span></p>
</div>
</div>
</div>
<p>La entrada <a href="https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/">Algoritmo de la ruta más corta</a> se publicó primero en <a href="https://ingenieriaindustrialonline.com">Ingenieria Industrial Online</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ingenieriaindustrialonline.com/investigacion-de-operaciones/algoritmo-de-la-ruta-mas-corta/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
