Curso: Redes Neuronales Artificiales y Algoritmos Bioinspirados


Semestre: 2025 - 1


Trabajo #1: Optimización Heurística


Equipo #2:

Carlos José Quijano Valencia
Miller Alexis Quintero García
Kelly Yojana Ospina Correa
Mateo Sebastián Mora Montero
Stiven Julio Doval


Profesor: Juan David Ospina Arango
Monitor: Andrés Mauricio Zapata Rincón


Universidad Nacional de Colombia, Sede Medellín

Mayo 2 del 2025

Enlace a repositorio GitHub: https://github.com/Straver00/RNA_2025-1

Tabla de contenidos

  1. Introducción
  2. Parte 1: Optimización Numérica
  3. Parte 2: Optimización Combinatoria
  4. Conlusión Final
  5. Bibliografía
  6. Reporte de Contribución Individual

Introducción

A lo largo del camino de desarrollo e innovación tecnológica con base fundamental en las matemáticas, los problemas asociados a optimización han sido muy comunes, esto entendiendo que la humanidad siempre ha buscado la mejor solución posible, gastando la menor cantidad de recursos, ya que esto es importante para sostenibilidad y el progreso. Es en este contexto que se han desarrollado múltiples técnicas y algoritmos para optimizar procesos, los cuales por su naturaleza, no funcionan todos de igual forma en contextos diferentes, por ello es necesario poner a prueba esta clase de métodos de optimización.

Por ello este trabajo se centra en estudiar, analizar y comparar algoritmos diferentes, específicamente, uno clásico y famoso usado en múltiples situaciones con una base analítica, tal y como lo es el descenso por gradiente con condición inicial aleatoria; también otros de ingenio heurístico con inspiración en procesos observados en la naturaleza, tales como los algoritmos genéticos (evolutivos), los enjambres de partículas, la evolución diferencial y las colonias de hormigas. Para dicho fin, este trabajo se ha centrado en 2 ejes:

El objetivo será entonces por medio de estos ejes principales, comprender la importancia, aplicabilidad y ventajas de estos algoritmos, reconociendo su papel fundamental en aplicaciones tecnológicas modernas como la inteligencia artificial, la logística y la ingeniería de sistemas complejos.


Parte 1: Optimización Numérica

Funciones de Prueba

Se seleccionaron las siguientes funciones de prueba para evaluar los diferentes algoritmos de optimización:

La función de Rastrigin se define a continuación con la ecuación (1) según ‘Wikipedia contributors; Rastrigin function’:

\[ f(\mathbf{x}) = An + \sum_{i=1}^{n} \left[ x_i^2 - A \cos(2\pi x_i) \right]\, \forall\,\, x_i \in [-5.12,\ 5.12]\qquad (1) \]

donde \(A=10\) y \(x_i \in [-5.12, 5.12]\). Dicha función (1) cuenta con su mínimo global en \(X = [0,0,...,0]\) con un valor de \(f(\mathbf{x}) = 0\).

Luego, la de Rosenbrock se define como describe ahora la ecuación (2), tomada de ‘Wikipedia contributors; Rosenbrock function’:

\[ f(\mathbf{x}) = \sum_{i=1}^{n-1} \left[ 100(x_{i+1} - x_i^2)^2 + (1 - x_i)^2 \right]\, \forall\,\, x_i \in [-\infty,\ \infty]\qquad (2) \] Y esta función (2) cuenta con su mínimo global en \(X = [1,1,...,1]\) con un valor de \(f(\mathbf{x}) = 0\).

Ahora bien, la función de Rastrigin tiene un dominio limitado, mientras la de Rosenbrock tiene como dominio los reales, sin embargo, ambas las vamos a trabajar en el espacio de búsqueda de Rastrigin, entendiendo que ambos mínimos globales están en dicho espacio, y ampliarlo es incrementar el costo computacional innecesariamente sin motivo alguno para el ejercicio de examinar los métodos de optimización.

Veamos ahora gráficamente como es la función de Rastrigin con entrada y salida 2D → 3D.

Fig.1 Superficie de Rastrigin


Igualmente un mapa de calor con curvas de nivel para entender desde otra perspectiva la función.

***Fig.2** Heatmap de Rastrigin*

Fig.2 Heatmap de Rastrigin


Ahora un gráfico de curvas de nivel simple para la función de Rastrigin.

***Fig.3** Levelplot de Rastrigin*

Fig.3 Levelplot de Rastrigin


Hacemos lo mismo con la función de Rosenbrock para tener una idea general.

Fig.4 Superficie de Rosenbrock


***Fig.5** Heatmap de Rosenbrock*

Fig.5 Heatmap de Rosenbrock

***Fig.6** Levelplot de Rosenbrock*

Fig.6 Levelplot de Rosenbrock


Con las funciones presentadas y teniendo claro su topografía, además del espacio de búsqueda; procedemos con las optimizaciones.

Optimización

NOTA: Para las funciones 3D → 4D no se hacen representaciones, debido a la abstracción fuera de nuestra percepción geométrica tridimensional.

Descenso por gradiente

El descenso de gradiente es un algoritmo de optimización determinista que busca minimizar (o maximizar) una función ajustando iterativamente sus variables en la dirección del gradiente negativo. Es ampliamente utilizado en matemáticas, ingeniería y aprendizaje automático como un método clásico. Tiene hiperparámetros como:

  • Punto inicial: Coordenadas desde donde comienza la búsqueda.
  • Tasa de aprendizaje (learning rate): Determina el tamaño del paso en cada iteración.
  • Número de iteraciones: Máximo de pasos antes de detenerse.
  • Criterio de convergencia: Umbral para detenerse si el cambio es muy pequeño.
  • Gradiente: Derivadas parciales de la función respecto a cada variable.

En esta parte se realizará la optimización de las anteriores funciones en dos y tres dimensiones, usando el método de descenso por gradiente, el cual consiste en recorrer la función dando pasos pequeños en la dirección en donde la función decrece más rápido.

En este caso debido a la complejidad de las funciones de prueba se realizaron algunas modificaciones al método simple, por ejemplo; si la distancia entre el punto y la actualización crece demasiado o el método no converge con cierto número de iteraciones, se reinicia el algoritmo con un nuevo punto aleatorio o el mejor punto encontrado hasta el momento, respectivamente. Esto hace que el método sea menos propenso a quedarse en mínimos locales y tenga más chances de llegar hasta el mínimo global.

Rastrigin

Comenzamos con la solución para 2 dimensiones base, o sea f(x,y):

## Reinicio número: 1 Iniciando desde: -3.22680565834045, 2.07231012821198 Valor: 24.26904 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## Reinicio número: 2 Iniciando desde: -1.02371988293758, 0.0575919115306958 Valor: 1.809776 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## Reinicio número: 3 Iniciando desde: -1.01329607602703, -0.00972459521248048 Valor: 1.0804 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## Reinicio número: 4 Iniciando desde: -1.01329607602703, -0.00972459521248048 Valor: 1.0804 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## Reinicio número: 5 Iniciando desde: -1.01329607602703, -0.00972459521248048 Valor: 1.0804 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## No se logró la convergencia después de 5 reinicios.
## La mejor solución hallada está en (x, y) = (-1.01329607602703, -0.00972459521248048).
## Con un valor óptimo de 1.08040041990205.

Veamos como se ve la solución hallada en un gráfico de curvas de nivel, donde la solución óptima real está representada como un punto verde y la del método de optimización con un punto rojo:

***Fig.7** GD Rastrigin*

Fig.7 GD Rastrigin

Se puede ver que a pesar de todos los reinicios e iteraciones del método, este se estancó en un mínimo local.

Veamos ahora para 3 dimensiones base, o sea f(x,y,z):

## Reinicio número: 1 Iniciando desde: 3, 3, 3 Valor: 27 
## No hay mejora significativa en 5000 iteraciones. Reiniciando desde el mejor punto encontrado.
## Reinicio número: 2 Iniciando desde: 4.58437385466581e-05, 4.58437385466581e-05, 4.58437385466581e-05 Valor: 1.250851e-06 
## Convergió en 6113 iteraciones (reinicio 1 )
## La mejor solución hallada está en:
## (x, y, z) = (4.58437385466581e-05, 4.58437385466581e-05, 4.58437385466581e-05)
## Con un valor óptimo de 1.25085121283064e-06

Ese valor óptimo hallado en 3D, si analizamos las coordenadas (x, y, z) vemos que se aproxima al mínimo global, por lo que en esta ocasión el método lo hizo bien.


Rosenbrock

Aplicamos ahora si la optimización en 2D.

## Reinicio número: 1 Iniciando desde: -3.22680565834045, 2.07231012821198 Valor: 6973.367 
## La distancia es Inf. Reiniciando con un nuevo punto de inicio aleatorio.
## Reinicio número: 2 Iniciando desde: 0.733263348229229, -3.31948079634458 Valor: 1487.836 
## Convergió en 19788 iteraciones (reinicio 1 )
## La mejor solución hallada está en (x, y) = (0.99988826932461, 0.999776104001655)
## Con un valor óptimo de 1.25037364638396e-08

Veamos gráficamente el resultado para este caso, siguiendo la convención anterior para el óptimo real y el óptimo del método.

***Fig.8** GD Rosenbrock*

Fig.8 GD Rosenbrock

En este caso si convergió bien el método del gradiente con un punto inicial aletorio, posiblemente la topografía de la función de Rosenbrock no atrapa tan fácilmente con un mínimo local, o el punto inicial favoreció el proceso.

Ejecutemos ahora el caso 3D.

## Reinicio número: 1 Iniciando desde: -3.22680565834045, 2.07231012821198, 0.750861668586731 Valor: 8230.232 
## La distancia es Inf. Reiniciando con un nuevo punto de inicio aleatorio.
## Reinicio número: 2 Iniciando desde: -3.31948079634458, 4.43839338840917, 4.43474958650768 Valor: 27661.62 
## La distancia es Inf. Reiniciando con un nuevo punto de inicio aleatorio.
## Reinicio número: 3 Iniciando desde: -3.70841023279354, 3.33448815625161, -0.319814844988286 Valor: 23964.93 
## La distancia es Inf. Reiniciando con un nuevo punto de inicio aleatorio.
## Reinicio número: 4 Iniciando desde: 0.499837417155504, 0.526740669738501, -2.6110524055548 Valor: 842.4896 
## Convergió en 17095 iteraciones (reinicio 3 )
## La mejor solución hallada está en:
## (x, y, z) = (0.999954281647075, 0.999908391106169, 0.999816354226281)
## Con un valor óptimo de 1.05044371078583e-08

Según los resultamos numéricos, nuevamente convergió bien el método.

NOTA: Para los siguientes métodos heurísticos que se basan en poblaciones e iteraciones, estos parámetros serán iguales para todos; 50 miembros y 100 iteraciones.

Algoritmos evolutivos

Los algoritmos evolutivos (AE) son métodos de optimización inspirados en los mecanismos de la evolución biológica. Funcionan con una población de posibles soluciones que evolucionan a lo largo de varias generaciones mediante operaciones como la selección, cruzamiento, mutación y elitismo. La función ga de la librería GA la implementa y cuenta con parámetros como:

  • Población inicial (popSize): Número de individuos (soluciones candidatas) generadas al inicio.
  • Función de aptitud (fitness): Evalúa qué tan buena es una solución. Si el problema es de minimización, se suele usar el valor negativo de la función.
  • Cruce o recombinación (pcrossover): Combina dos soluciones para crear nuevas.
  • Mutación (pmutation): Modifica aleatoriamente una solución para mantener diversidad.
  • Elitismo (elitism): Número de mejores soluciones que se copian directamente a la siguiente generación.
  • Generaciones máximas (maxiter): Número total de generaciones.
  • Criterio de parada temprana (run): Número de generaciones sin mejora antes de detenerse.
  • Optimización local opcional (optim): Puede afinar soluciones localmente.

El objetivo es encontrar una solución óptima o cercana al óptimo para un problema, especialmente cuando los métodos tradicionales no son eficaces debido a la complejidad del espacio de búsqueda.

Para la implementación en código R de GA se tomó como guía la documentación dispuesta en ‘Scrucca, L. (2025). GA: Genetic Algorithms. CRAN’.

Rastrigin

Comenzamos con el caso 1D.

## Mejor solución encontrada:
## x = 0
## Con un valor óptimo de 0 en 100 iteraciones
***Fig.9** GA Rastrigin 1D*

Fig.9 GA Rastrigin 1D

Vemos que no supone mayor dificultad para el método hallar una solución exacta en baja dimensionalidad.

Procedemos con la de Rastrigin 2D, considerando diversos argumentos de la función.

## Mejor solución encontrada:
## (x, y) = (0, 0)
## Con un valor óptimo de 0 en 100 iteraciones
***Fig.10** GA Rastrigin 2D*

Fig.10 GA Rastrigin 2D

Nuevamente, la solución hallada es exacta para este caso de 2 dimensiones de entrada.

Veamos que tal con la versión 3D.

## Mejor solución encontrada:
## (x, y, z) = (0, 0, 0)
## Con un valor óptimo de 0 en 100 iteraciones

De nuevo, solución perfectamente exacta.

Rosenbrock

Es momento de evaluar ahora que tal le va al método con la de Rosenbrock, si logrará la misma exactitud que con Rastrigin. Comenzado de nuevo entonces con el caso 1D.

## Mejor solución encontrada:
## x = 0.999999501247124
## Con un valor óptimo de -9.97504772334171e-11 en 100 iteraciones
***Fig.11** GA Rosenbrock 1D*

Fig.11 GA Rosenbrock 1D

Se obtiene una solución que a términos prácticos es exacta.

Luego, para el caso 2D de Rosenbrock.

## Mejor solución encontrada:
## (x, y) = (0.999821444661359, 0.9996430029726)
## Con un valor óptimo de -3.18826775556571e-08 en 100 iteraciones
***Fig.12** GA Rosenbrock 2D*

Fig.12 GA Rosenbrock 2D

Continua siendo muy buena la solución sin problema alguno.

Ahora el de Rosenbrock 3D.

## Mejor solución encontrada:
## (x, y, z) = (0.999902313108414, 0.999805319634613, 0.999610896342258)
## Con un valor óptimo de -4.74947456265742e-08 en 100 iteraciones

Según los resultados númericos, se aproxima bastante bien.

Enjambres de partículas

Se basa en las ideas de comportamientos de poblaciones como grupos de aves, que en búsqueda de alimento, no solo se mueven a cierta velocidad, sino que buscan en base a lo que observan del entorno, y lo que observan en sus compañeros. Para aplicar la optimización por enjambre de partículas se empleó la función psoptim de la librería pso (particle swarm optimization). Esta permite modificar diversos parámetros como por ejemplo:

  • Máximo número de iteraciones.
  • Tamaño del enjambre.
  • Magnitud del vector de velocidad.
  • Constante de exploración local (cognitivo).
  • Constante de exploración global (social).

Entre muchos otros parámetros adicionales, pero, para este caso particular de comparación de métodos de optimización, solo se modificará el máximo número de iteraciones y población, los demás parámetros se dejarán por defecto.

Para la implementación en código R de PSO se tomó como guía la documentación dispuesta en ‘Clerc, M., & Coelho, L. S. (2022, Octubre 14). pso: Particle Swarm Optimization (Version 1.0-5) [Manual de referencia]. The R Project for Statistical Computing.’.

Rastrigin

## La mejor solución hallada es:
## x = 1.73824685829077e-05, con f(x) = 6.00024101515828e-08.
## Usando 100 iteraciones.
***Fig.13** PSO Rastrigin 1D*

Fig.13 PSO Rastrigin 1D

La solucion númerica se aproxima lo suficientemente bien para ser gráficamente perfecta.

Vemos ahora para el caso 2D a 3D.

## La mejor solución hallada es:
## x = 1.73824685829077e-05, y = 5.41619145005576e-07, con f(x,y) = 6.00024101515828e-08.
## Usando 100 iteraciones.

Veamos en una gráfica de curvas de nivel la solución hallada:

***Fig.14** PSO Rastrigin 2D*

Fig.14 PSO Rastrigin 2D

Continuamos con una solución óptima del método, suficientemente buena gráficamente, con un error despreciable.

Finalizamos Rastrigin con PSO mirando el caso 3D.

## La mejor solución hallada es:
## x = 0.00795172284832642, y = -0.000250197075416108, con f(x,y) = 0.0126238210993428.
## Usando 100 iteraciones.

Se sigue aproximando decentemente, pero ya empieza a resultarle más complicado al método.


Rosenbrock

## La mejor solución hallada es:
## x = 0.00795172284832642, con f(x) = 0.0126238210993428.
## Usando 100 iteraciones.
***Fig.15** PSO Rosenbrock 1D*

Fig.15 PSO Rosenbrock 1D

No le cuesta mucho en 1D aproximarse bien, pero de primeras no logra el mismo nivel de exactitud numérico que los algoritmos evolutivos.

Ahora veamos el resultado de optimizar la de Rosenbrock 2 variables.

## La mejor solución hallada es:
## x = 1.00096511939299, y = 1.0018622281904, con f(x,y) = 1.40675608269388e-06.
## Usando 100 iteraciones.
***Fig.16** PSO Rosenbrock 2D*

Fig.16 PSO Rosenbrock 2D


Ahora veamos el resultado de optimizar la de Rosenbrock con variables x,y,z.

## La mejor solución hallada es:
## x = 0.898746387671411, y = 0.814071484869055, z = 0.66124977362888, con f(x,y,z) = 0.0490379825415431.
## Usando 100 iteraciones.

Vemos que ya en 3D el método de enjambre de partículas, le cuesta obtener resultados decentes con la función de Rosenbrock, se evidencia principalmente en la 3ra coordenada, con \(z\approx 0.6612\).

Evolución diferencial

La evolución diferencial (DE) es un algoritmo evolutivo específico, que se diferencia por su metodología para generar variación entre individuos por medio de diferencias entre ellos, modulada por un factor de escala F, a parte de tener las otras operaciones de los algortimos evolutivos convencionales. Los parámetros principales de este algoritmo incluyen:

  • Tamaño de población.
  • Factor de escala.
  • Probabilidad de cruce.
  • Número de generaciones.

DE es adecuado para problemas con espacios de búsqueda no lineales y multidimensionales, y es menos propenso a quedar atrapado en óptimos locales en comparación con otros algoritmos de optimización.

El código en R para está subsección de DE se desarrolló según documentación Mullen, K., Ardia, D., Gil, D., Windover, D., & Cline, J. (2025). DEoptim: Global Optimization by Differential Evolution. CRAN.

Rastrigin

Iniciamos esta sub-sección con Rastrigin 1D.

## Valor óptimo Rastrigin 1D → f* = 1.04805053524615e-13.
## Obtenido en x = 2.2989377927243e-08.
## Usando 100 iteraciones.
***Fig.17** DE Rastrigin 1D*

Fig.17 DE Rastrigin 1D

Empieza bien, pero no precisamente con la mayor de las exactitudes de todos los métodos.

Proseguimos optimizando la función en 2D.

## Valor óptimo Rastrigin 2D → f* = 0.00154272911834141.
## Obtenido en (x,y) = (0.00172512919681201, 0.00219093500219472).
## Usando 100 iteraciones.
Evaluemos la calidad de la solución visualmente con una gráfica de contornos de nivel.
***Fig.18** DE Rastrigin 2D*

Fig.18 DE Rastrigin 2D

Aquí ya se aprecia ligeramente mayor desviación en la solución hallada.

Veamos ahora como le va para el caso de tres variables de entrada.

## Valor óptimo Rastrigin 3D → f* = 0.191570036875426.
## Obtenido en (x,y,z) = (0.0223378538065587, 0.0214201641130212, 0.00305181153790221).
## Usando 100 iteraciones.

Como era de esperar, cada vez le resulta más costoso mejorar la solución en Rastrigin al aumentar la dimensionalidad.

Rosenborck

Continuamos ahora con la optimización por evolución diferencial para la de Rosenbrock en 1D.

## Valor óptimo Rosenbrock 1D → f* = 3.34126966787017e-27.
## Obtenido en x = 1.
## Usando 100 iteraciones.
***Fig.19** DE Rosenbrock 1D*

Fig.19 DE Rosenbrock 1D

Vemos que la evolución diferencial arranca teniendo un resultado exacto en el argumento que minimiza a la función de Rosenbrock.

Ahora bien, veamos si el buen desempeño se mantiene con una entrada 2D.

## Valor óptimo Rosenbrock 2D → f* = 9.87537269674006e-21.
## Obtenido en (x,y) = (1.00000000006936, 1.00000000014584).
## Usando 100 iteraciones.
***Fig.20** DE Rosenbrock 2D*

Fig.20 DE Rosenbrock 2D

El método continua produciendo un muy buen resultado con esta función.

Veamos que tal con entrada 3D (x, y, z).

## Valor óptimo Rosenbrock 3D → f* = 2.00531391570777e-11.
## Obtenido en (x,y,z) = (0.99999829525011, 0.999996667066797, 0.999993567646815).
## Usando 100 iteraciones.

A términos generales, la evolución diferencial le va especialmente bien con la función de Rosenbrock

Generación de los GIF

Para poder generar los gifs, es necesario activar el monitoreo o trazabilidad de los miembros de las poblaciones, para cada iteración, por lo cual se volverán a correr todos los métodos de optmización pero con parámetros específicos.

Observación: Las celdas de producción de GIFs solo se ejecutaron una vez para generar los archivos, y ya luego se etiquetaron con HTML. Para la ejecución del .Rmd cambiar “eval = FALSE” a “eval = TRUE”, o en su defecto borrar ese parámetro del chunk.

Descenso por gradiente

Comenzamos produciendo el GIF para Rastrigin.

Gif.1 Proceso Gradiente Rastrigin


Se aprecia inmediatamente que el método convergió muy rápido, pero a un mínimo local.

Miramos el GIF de Rosenbrock con gradiente.

Gif.2 Proceso Gradiente Rosenbrock


Recordando que acorde al código, debido a la enorme cantidad de iteraciones del método de gradiente, se tomaron iteraciones en pasos grandes; se puede observar que aunque son muchas iteraciones, logra alcanzar el mínimo global. Esto sumado a las ejecuciones iniciales de experimentación, denota que la función de Rosenbrock, tiene una topografía que no perjudica tanto al método, si lo comparamos con la de Rastrigin por ejemplo.

NOTA: Para los GIF de los métodos heurísticos, se emplean 20 miembros de población y 100 iteraciones.

Algoritmos evolutivos

Para poder guardar los puntos de cada población en cada iteración con la librería GA fue necesario crear previamente una función de monitoreo, y luego pasarla a los argumentos de la función ga.

Gif.3 Proceso Evolutivo Rastrigin


Se aprecia que hay puntos que llegan al mínimo global, pero al final de las iteraciones, varios miembros permanecieron en mínimos locales, aquí la ventaja de obtener la buena solución es por el almacenamiento de las coordenadas que dan el mejor valor detectado al final del algoritmo.

Veamos ahora para Rosenbrock con GA.

Gif.4 Proceso Evolutivo Rosenbrock


Se aprecia que los miembros de la población a medida que pasan las iteraciones, se van acercando muy suavemente al óptimo, sin dejar de existir puntos que realizan exploraciones para no sesgar la población en un camino evolutivo.

Enjambres de partículas

Para poder activar el monitoreo que trae la función de librería, es necesario setear varios argumentos,los cuales son:

  • trace = 1.
  • REPORT = 1.
  • trace_stats = TRUE.

Si alguno de estos no se tiene activado, el monitoreo solo tomará datos de cada cierto número de iteraciones, o tomará solo los valores de las soluciones y no los puntos.

Gif.5 Proceso Enjambre Rastrigin


El método converge bastante bien, sin dejar de tener individuos en búsqueda constante.

Produzcamos ahora el GIF para Rosenbrock.

Gif.6 Proceso Enjambre Rosenbrock


Particularmente en la función de Rosenbrock, aunque el método de enjambre va convergiendo, varios miembros se acumulan alrededor del óptimo, por cuestiones de la naturaleza cognitiva de los miembros del algoritmo.

Evolución diferencial

Esta función solo requiere setear los argumentos storepopfrom y storepopfreq para guardar los puntos de población en cada iteración, y se accede a ellos por medio del atributo $member del objeto.

Gif.7 Proceso Diferencial Rastrigin


Por ejemplo en este GIF se aprecia la importancia del tamaño de población, sin individuos suficientes en constante búsqueda, el algoritmo se estancaría fácilmente en un óptimo local. Aquí también se aprecia como miembros de la población van migrando a diversos mínimos de la función, haciendo uso del distintivo del algoritmo, que es la comparación por diferencias.

Finalizamos los GIFs con el de Rosenbrock.

Gif.8 Proceso Diferencial Rosenbrock


Se resalta la alta, rápida y organizada convergencia del método de evolución diferencial con la función de Rosenbrock, bajo los hiperparámetros estándar establecidos.

Heuristícos VS Descenso por gradiente

Para tener una herramienta previa al análisis final, se va establecer una métrica comparativa (entendiendo las simplificaciones que esto implica por la diversidad de métodos), en la que se va a medir el error en optimización promedio de cada método, para cada función, y este error se va a ponderar en función de la cantidad media de evaluaciones de la F.O e iteraciones del método. Más explicitamente como indica la ecuación (3):

\[ \text{Métrica de ineficiencia} = \overline{|{\text{error}}|} \times \overline{\text{iteraciones}} \times \overline{\text{evaluaciones}} \qquad (3) \] Para esto vamos a correr en un ciclo definido for, los 3 métodos heurísticos con 100 semillas diferentes de aleatoriedad, recopilaremos los resultados, obtendremos el promedio de cada factor, y con ellos calcularemos la métrica. La prueba será con las funciones a 3 variables de entrada, para mayor exigencia; los parámetros estarán establecidos en 50 miembros de población y 100 iteraciones límite.

Rastrigin

Ejecutemos la simulación para la función de Rastrigin dada por la ecuación (1) y veamos los resultados:
***Fig.21** Comparación Rastrigin*

Fig.21 Comparación Rastrigin

Rosenbrock

Ahora lo mismo, pero para la función de Rosenbrock descrita en la ecuación (2):
***Fig.22** Comparación Rosenbrock*

Fig.22 Comparación Rosenbrock

Fue necesario poner esta gráfica con el logaritmo base euler de los valores, ya que la métrica de ineficiencia de algoritmos evolutivos GA era mucho mayor a las demás.

Discusión

Tras las ejecuciones realizadas en materia de experimentaciones iniciales, ejecuciones oficiales en el reporte técnico, los análisis a partir de los GIF en complemento con la teoría conocida del curso y las discusiones de clase, se puede concluir lo siguiente:

  • El método de optimización por descenso de gradiente es muy dependiente de sus hiperparámetros y de la topografía del cuerpo matemático (función) a optimizar, siendo los argumentos más relevantes:

    • Punto inicial: Es clave, pues un punto inicial muy bueno o conveniente puede significar la convergencia al óptimo global y un menor costo computacional por iteraciones. Esta dependencia crítica del punto de partida ha sido documentada extensivamente en la literatura de optimización (Auger et al., 2010).

    • Learning Rate: Según la naturaleza del problema, puede implicar alejarse del óptimo global buscado, y dificultar su convergencia debido a “movimientos bruscos” en el espacio de búsqueda. También puede implicar un descenso demasiado lento, lo cual conlleva más iteraciones y un alto riesgo de estancarse en un óptimo local. Korniienko et al. (2024) demuestran cómo la selección inadecuada de la tasa de aprendizaje puede comprometer significativamente el rendimiento del algoritmo en funciones benchmark estándar.

  • El punto inicial en el descenso por gradiente puede tener dos facetas. En ejercicios didácticos o de revisión, elegir un punto conveniente puede percibirse como hacer trampa. En cambio, en ejercicios complejos con fines de investigación, puede basarse en un conocimiento profundo sobre la naturaleza del problema, permitiendo seleccionar un punto inicial con criterios robustos.

  • Una diferencia clave que explica el éxito de los métodos heurísticos por encima del descenso por gradiente es la implementación de poblaciones, ya que estas ofrecen diferentes frentes de búsqueda de óptimos, minimizando el riesgo de quedar atrapados en óptimos locales o de requerir iteraciones extensivas. Los miembros de la población se respaldan y guían entre sí. Esta ventaja poblacional es particularmente evidente en funciones multimodales, como demuestran Auger et al. (2010) en sus comparaciones experimentales de algoritmos libres de derivadas.

  • Los algoritmos bioinspirados demuestran ser una estrategia computacional versátil y potente para abordar problemas de optimización numéricos, especialmente cuando la información del gradiente no está disponible o el espacio de búsqueda exhibe una naturaleza extensa y no lineal que dificulta obtener una expresión analítica. Esta versatilidad ha sido confirmada por estudios comparativos que evalúan múltiples criterios de efectividad (Korniienko et al., 2024); y se evidenciará más adelante en este reporte con la Parte 2, donde aplicaremos uno de estos métodos a un problema de optimización combinatoria del mundo real.

  • La fortaleza puntual de los algoritmos evolutivos está en la exploración de sus individuos potenciada por la combinación de sus atributos, mediante cruce y la introducción de diversidad a través de mutación. Kozlov et al. (2024) validan esta característica al mostrar el desempeño superior de GA en ciertas funciones benchmark comparadas con los métodos tradicionales.

  • Equiparar o hacer comparaciones extensas entre los propios métodos heurísticos representa una tarea complicada, ya que poseen hiperparámetros muy distintos entre sí, los cuales no se pueden igualar de forma simple. Ni siquiera la cantidad de iteraciones máximas puede compararse directamente, dado que una iteración en un algoritmo no tiene el mismo peso o significado que en otro. Además, de la dependencia del estado aleatorio de inicialización de la población en menor medida. Esta problemática de comparación equitativa ha sido abordada metodológicamente por Auger et al. (2010), quienes proponen marcos de evaluación más robustos para algoritmos de optimización sin derivadas.

  • A pesar de la dificultad para equipararlos de forma detallada; con las simulaciones realizadas se pudo evidenciar, bajo algunos casos estándar, ciertas virtudes de unos métodos sobre otros:

    • Por ejemplo, la evolución diferencial logra resultados eficientes con la función de Rosenbrock, pues fue la de menor métrica de ineficiencia, mientras que a los algoritmos evolutivos les costó dramáticamente dicha función. Este comportamiento diferencial entre algoritmos según la función objetivo es consistente con los hallazgos de Kozlov et al. (2024), quienes reportan variaciones significativas en el rendimiento relativo de GA, PSO y DE dependiendo de las características específicas de cada función benchmark.

    • Por otro lado y para contraste, los algoritmos evolutivos fueron los mejores para Rastrigin, mientras que los enjambres de partículas resultaron ser los más ineficientes para esta función. Esto deja a la evolución diferencial para estas simulaciones estándar, como una opción altamente equilibrada. La función Rastrigin, conocida por su alta multimodalidad, representa un caso de estudio clásico en la evaluación de algoritmos de optimización global, como se documenta en los estudios comparativos de Auger et al. (2010).

  • Como mostró Korniienko et al. (2024), el mejor enfoque es combinar ambos mundos: usar descenso de gradiente donde la topografía es suave y unimodal, y metaheurísticos cuando se espera alta multimodalidad o cuando no se dispone de derivadas analíticas. Esta recomendación de enfoque híbrido se basa en criterios múltiples de efectividad que consideran tanto la precisión como la eficiencia computacional de los algoritmos evaluados.


Parte 2: Optimización Combinatoria

Teniendo ya claras las bondades de los algoritmos heurísticos para problemas de optimización numérica, veamos ahora algo un poco más complejo y de naturaleza diferente, como lo es un problema de optimización combinatoria.

En esta sección, se aborda la optimización del recorrido de un vendedor que debe visitar 13 ciudades principales de Colombia: Bogotá, Cali, Medellín, Barranquilla, Cartagena, Cúcuta, Bucaramanga, Pereira, Santa Marta, Ibagué, Pasto, Manizales y Neiva. El objetivo central es determinar la ruta que minimice el costo total del viaje, empleando dos metaheurísticas: Optimización por Colonias de Hormigas (ACO) y Algoritmos Genéticos (GA). El costo total considera factores como el salario del conductor durante el viaje, el consumo de combustible, las distancias, tiempos de viaje y peajes.

Descripción del problema del vendedor

El problema se modela como una instancia del Problema del Viajante de Comercio (TSP), un clásico desafío de optimización combinatoria. Se busca encontrar la ruta cíclica más económica que visite cada una de las 13 ciudades exactamente una vez y regrese a la ciudad de origen. El costo asociado al desplazamiento entre cualquier par de ciudades se desglosa en tres componentes principales:

La suma de todos estos costos con las ecuaciones (5), (6) y (7), nos da el costo total de desplazamiento entre dos ciudades, que se calcula con la siguiente ecuación (8): \[ \text{Costo_total} = \text{Costo_salario} + \text{Costo_gasolina} + \text{Costo_peaje} \qquad (8) \] Este problema se modela como un Problema del Viajante de Comercio (TSP, por sus siglas en inglés), en el cual buscamos la ruta más corta o económica para visitar todas las ciudades.

Algoritmos utilizados

En este caso, se usan dos algoritmos de optimización para encontrar la mejor ruta, uno por metahuerística hecho para problemas combinatorios (siendo diferente a los usados en la Parte 1 de este trabajo), y otro que sí es de la Parte 1, para demostrar su flexibilidad y dar algo de continuidad. Así pues, estos algoritmos son:

1. Algoritmo de Colonias de Hormigas (ACO): Este algoritmo se inspira en el comportamiento colectivo de las hormigas al buscar caminos. Emplea feromonas artificiales para marcar las rutas: las rutas más cortas (de menor costo) acumulan más feromonas, incrementando la probabilidad de que sean elegidas por hormigas futuras, guiando así la búsqueda hacia soluciones prometedoras.

2. Algoritmo Genético (GA): Usado y descrito en la anterior sección, está basado en los principios de la evolución biológica y la selección natural, operando en este caso sobre una población de posibles rutas (cromosomas). Mejorando la solución mediante operadores como la selección (favoreciendo las rutas de menor costo), el cruce (combinando partes de rutas existentes para crear nuevas) y la mutación (introduciendo pequeñas alteraciones aleatorias), la población evoluciona a lo largo de generaciones, convergiendo hacia soluciones de alta calidad.

Código en R para la optimización

Consultamos las distancias viales entre las ciudades y tiempos de viaje ofrecidos por la API de Google. Adicionalmente cargamos los costos de peajes, los nombres de las ciudades y el número de ciudades:

Agregamos datos de longitud y latitud consultados, que son necesarios para graficar la ruta:

Definimos parámetros de costos iniciales para el salario, consumo de combustible y precio de gasolina:

salario_hora <- 6189  # COP/hora
consumo_combustible <- 60  # km/galón
precio_gasolina <- 15827  # COP/galón

Calculamos el costo de la gasolina, el tiempo de los viajes y el costo del salario:

df_costo_gasolina <- (df_distancias/consumo_combustible) * precio_gasolina
df_costo_salario <- df_tiempos_viajes * salario_hora

Ahora calculamos el costo total de desplazamiento entre las ciudades:

##                Bogota     Cali Medellin Barranquilla Cartagena   Cucuta
## Bogota            0.0 360590.8 262801.3    584958.49 614429.16 301580.2
## Cali         355163.3      0.0 326036.3    775439.42 779410.09 568120.2
## Medellin     263263.4 324893.7      0.0    461422.75 455893.68 348652.0
## Barranquilla 574073.2 780633.3 466788.7         0.00  92832.16 367961.3
## Cartagena    616164.9 786913.4 456281.2     88884.96      0.00 432499.8
## Cucuta       300307.1 572050.2 346942.3    403768.13 434741.81      0.0
## Bucaramanga  241914.1 447184.2 222078.0    377208.23 408181.90 124860.9
## Pereira      258162.1 145041.6 179733.6    659836.70 617707.38 510756.9
## Santa Marta  512089.2 677709.0 452601.1     84446.54 182653.85 305965.2
## Ibague       167062.6 181450.1 263124.0    572802.62 602273.56 391831.3
## Pasto        528045.6 192435.7 498818.8    957599.88 955491.61 830502.8
## Manizales    145456.2 196528.6 156440.1    636543.27 613013.95 381257.9
## Neiva        226773.2 177060.5 355117.3    677274.50 706745.44 496301.5
##              Bucaramanga   Pereira Santa Marta    Ibague    Pasto Manizales
## Bogota          243534.3 262895.81   524274.80 166012.75 529982.2  145187.5
## Cali            441396.8 144669.36   691750.49 175824.73 193402.1  197079.8
## Medellin        221926.8 178658.22   460532.10 256115.61 498698.5  155357.4
## Barranquilla    343144.3 665098.16    84774.56 561326.98 963982.9  641795.3
## Cartagena       407683.1 625233.30   183681.86 603418.63 963972.6  620532.5
## Cucuta          123176.5 509005.25   322028.65 390971.89 845929.2  379630.7
## Bucaramanga          0.0 386840.97   295468.74 268806.15 723765.0  257466.7
## Pereira         386733.5      0.00   637746.06  78723.52 318846.4   53797.0
## Santa Marta     281148.3 617377.82        0.00 499330.97 954301.8  487989.8
## Ibague          267807.9  83655.03   512192.86      0.00 371554.9  153487.5
## Pasto           706479.3 317451.88   956833.01 364907.26      0.0  369862.3
## Manizales       257232.5  53837.13   521152.63 147594.52 370335.4       0.0
## Neiva           372278.1 206342.42   616590.81 125757.65 244654.1  259875.1
##                 Neiva
## Bogota       226788.6
## Cali         193993.1
## Medellin     353562.1
## Barranquilla 665290.5
## Cartagena    707382.2
## Cucuta       494934.0
## Bucaramanga  372769.7
## Pereira      201391.9
## Santa Marta  603294.5
## Ibague       126590.7
## Pasto        244429.6
## Manizales    253962.9
## Neiva             0.0

Ahora, dado este dataset de costos, utilizaremos tanto el algoritmo de colonia de hormigas y un algoritmo evolutivo para encontrar el orden correcto.

Algoritmo de Optimización por Colonia de Hormigas (ACO)

La Optimización por Colonia de Hormigas (ACO) es “una técnica probabilística para solucionar problemas computacionales que pueden reducirse a buscar los mejores caminos o rutas en grafos”, como se describe en la literatura (Wikipedia, s.f.). Su funcionamiento se inspira directamente en cómo las hormigas encuentran eficientemente fuentes de alimento: depositan feromonas en sus trayectos, y las rutas más efectivas acumulan mayores concentraciones de esta sustancia, atrayendo a más hormigas.

El algoritmo ACO simula este comportamiento mediante:

Hormigas Artificiales: Agentes que construyen soluciones candidatas (rutas).

Feromonas Virtuales: Información asociada a los componentes de la solución (e.g., las conexiones entre ciudades) que se actualiza según la calidad de las soluciones encontradas. Rutas de mejor calidad refuerzan más intensamente sus componentes.

Selección Probabilística: Las hormigas eligen los siguientes pasos basándose tanto en la intensidad de la feromona como, opcionalmente, en información heurística (e.g., la distancia directa), favoreciendo caminos prometedores pero permitiendo la exploración.

Se usa la función ant_colony_optimization_r_con_historial desarrollada en R para implementar el algoritmo de optimización por colonia de hormigas. Esta función está diseñada para resolver el Problema del Viajante de Comercio (TSP), buscando la ruta de menor costo total. Además de encontrar la mejor ruta, registra el historial de la mejor solución encontrada en cada iteración, lo cual es útil para analizar la convergencia y visualizar el proceso.

Ahora, ejecutamos la función ant_colony_optimization_r_con_historial con un conjunto de parámetros definidos. Se establece una semilla (set.seed) para garantizar la reproducibilidad de los resultados, dado el componente probabilístico del algoritmo.

Adicional, en la Figura 23 se observa como el algoritmo va obtendiendo mejores resultados al optimizar la función.

## Iter 1, Hormiga 1: Nueva mejor longitud = 6206438.02
## Iter 1, Hormiga 3: Nueva mejor longitud = 6098850.75
## Iter 1, Hormiga 4: Nueva mejor longitud = 3625671.95
## Iter 9, Hormiga 14: Nueva mejor longitud = 3502405.67
## --- Fin Iteración 10: Mejor costo hasta ahora = 3502405.67 ---
## --- Fin Iteración 20: Mejor costo hasta ahora = 3502405.67 ---
## Iter 26, Hormiga 12: Nueva mejor longitud = 3311065.03
## Iter 28, Hormiga 22: Nueva mejor longitud = 3247900.85
## --- Fin Iteración 30: Mejor costo hasta ahora = 3247900.85 ---
## --- Fin Iteración 40: Mejor costo hasta ahora = 3247900.85 ---
## --- Fin Iteración 50: Mejor costo hasta ahora = 3247900.85 ---
## --- Fin Iteración 60: Mejor costo hasta ahora = 3247900.85 ---
## --- Fin Iteración 70: Mejor costo hasta ahora = 3247900.85 ---
## Iter 80, Hormiga 14: Nueva mejor longitud = 2855248.12
## --- Fin Iteración 80: Mejor costo hasta ahora = 2855248.12 ---
## --- Fin Iteración 90: Mejor costo hasta ahora = 2855248.12 ---
## --- Fin Iteración 100: Mejor costo hasta ahora = 2855248.12 ---
## ACO: Optimización finalizada. Mejor longitud encontrada = 2855248.12
## [1] "--- Resultado Final - Ejecución ACO ---"
## [1] "Mejor costo encontrado: 2,855,248"
## [1] "Mejor ruta (ciudades): Pasto -> Neiva -> Ibague -> Manizales -> Pereira -> Bogota -> Bucaramanga -> Cucuta -> Barranquilla -> Cartagena -> Santa Marta -> Medellin -> Cali"

Para entender mejor cómo el algoritmo refina la solución, generamos una animación que muestra la mejor ruta encontrada hasta cada iteración. Dicha animación se ilustra con la Figura 24 mostrando la mejor ruta encontrada por ACO en cada iteración, hasta converger a una solución de bajo costo en 100 iteraciones.

Algoritmo de Evolución Genética (GA)

Como se demostró en la Parte 1 para funciones numéricas, los Algoritmos Genéticos (AG) son una poderosa herramienta de optimización. Ahora, adaptaremos este mismo enfoque, basado en la evolución de una población de rutas, para abordar el problema combinatorio del viajante de comercio.

El enfoque se basa en mantener y evolucionar una población de soluciones candidatas al problema. En nuestro contexto del TSP, cada ‘individuo’ de la población es una ruta o secuencia específica de visita a las ciudades. Cada ruta se representa mediante una codificación, análoga a un cromosoma (usualmente, una permutación de los índices o nombres de las ciudades).

El algoritmo opera en ciclos llamados generaciones. En cada generación, se simula el proceso evolutivo mediante los siguientes pasos clave [inspirados en conceptos descritos por Wikipedia (s.f.) y otros]:

  1. Evaluación: Se calcula la aptitud (fitness) de cada ruta en la población. Para el TSP, una mayor aptitud corresponde a un menor costo total del recorrido (usualmente se define como el inverso del costo o alguna función decreciente del costo).

  2. Selección: Se seleccionan las rutas ‘padres’ que contribuirán a la siguiente generación. Las rutas con mayor aptitud tienen una mayor probabilidad de ser elegidas, simulando la “supervivencia del más apto”.

  3. Cruzamiento (Crossover): Se combinan pares de rutas padres para crear nuevas rutas ‘hijas’. Este proceso mezcla segmentos o características de las rutas progenitoras, buscando generar nuevas combinaciones potencialmente mejores.

  4. Mutación: Se aplican pequeñas alteraciones aleatorias a algunas de las nuevas rutas (o a veces a individuos existentes). En el TSP, esto podría implicar intercambiar la posición de dos ciudades en la secuencia. La mutación introduce diversidad genética en la población y ayuda a evitar que el algoritmo se estanque prematuramente en soluciones subóptimas.

  5. Reemplazo: Se forma la población para la siguiente generación, decidiendo qué individuos (padres, hijos) sobreviven. Existen diversas estrategias, como reemplazar a los menos aptos o mantener a los mejores (‘elitismo’).

A través de la aplicación repetida de estos operadores genéticos (selección, cruzamiento, mutación) durante muchas generaciones, la población tiende a converger hacia rutas de muy alta calidad (bajo costo).

Se definió una función en R que implementa el Algoritmo Genético diseñado para resolver nuestro problema del viajante.

## GA Gen 1: Mejor Costo (Gen actual) = 3437924.92
## GA Gen 20: Mejor Costo (Gen actual) = 2996061.42
## GA Gen 40: Mejor Costo (Gen actual) = 2713085.75
## GA Gen 60: Mejor Costo (Gen actual) = 2588016.66
## GA Gen 80: Mejor Costo (Gen actual) = 2559428.70
## GA Gen 100: Mejor Costo (Gen actual) = 2434204.28
## GA Gen 120: Mejor Costo (Gen actual) = 2434204.28
## GA Gen 140: Mejor Costo (Gen actual) = 2385208.69
## GA Gen 160: Mejor Costo (Gen actual) = 2385208.69
## GA Gen 180: Mejor Costo (Gen actual) = 2385208.69
## GA Gen 200: Mejor Costo (Gen actual) = 2385208.69
## [1] "Ejecución GA finalizada."
## [1] "Historial GA guardado para 200 generaciones."
## [1] "--- Resultados Finales GA ---"
## [1] "Mejor costo GA: 2,385,209"
## [1] "Mejor ruta GA (índices): 11 -> 2 -> 8 -> 12 -> 3 -> 5 -> 4 -> 9 -> 6 -> 7 -> 1 -> 10 -> 13"
## [1] "Mejor ruta GA (ciudades): Pasto -> Cali -> Pereira -> Manizales -> Medellin -> Cartagena -> Barranquilla -> Santa Marta -> Cucuta -> Bucaramanga -> Bogota -> Ibague -> Neiva"

## [1] "Datos de historial GA procesados para 200 generaciones."

En la figura 25 se nota como obtiene mejores resultados en especial en el “Best” a medida que el algoritmo avanza en las generaciones.

Creamos la animación para los algortimos geneticos:

La animación resultante (Figura 26) ilustra cómo la mejor ruta encontrada por el algoritmo AG evoluciona a lo largo de las iteraciones, tendiendo a estabilizarse a medida que el algoritmo converge hacia una solución de bajo costo.

Ahora, comparando ambos algoritmos:

## [1] "GA encontró una mejor solución (Costo: 2,385,209 )"
## [1] "  (Costo ACO: 2,855,248 )"
## [1] "Mejor ruta encontrada por Algoritmo Genético (GA) :"
## [1] "Pasto -> Cali -> Pereira -> Manizales -> Medellin -> Cartagena -> Barranquilla -> Santa Marta -> Cucuta -> Bucaramanga -> Bogota -> Ibague -> Neiva"

En conclusión, el GA superó al ACO en ≈ 16 % de ahorro de costo, lo que indica que, con la configuración actual, el GA exploró el espacio de soluciones de forma más efectiva para este problema de rutas entre 13 ciudades.

La mejor ruta hallada por el GA cubre primero el suroccidente (Pasto → Cali → Pereira → Manizales → Medellín) y luego “salta” a la costa Caribe (Cartagena → Barranquilla → Santa Marta), para finalmente recorrer el nororiente y centro (Cúcuta → Bucaramanga → Bogotá → Ibagué → Neiva).
Ese orden evita retornos largos y, al juntar tramos geográficamente cercanos, reduce tanto distancias como tiempo de conducción.

En contraste, el ACO depende fuertemente de los parámetros de feromonas (α, β, tasa de evaporación ). Si la evaporación es alta o la influencia heurística (β) es baja, las hormigas pueden dispersarse demasiado y tardar más en reforzar la mejor ruta. Con los valores actuales, el ACO siguió mejorando pero no alcanzó la calidad que el GA obtuvo antes de que se cumpliera su criterio de parada.

Conlusión Final

Este estudio sobre optimización numérica y combinatoria confirma un principio fundamental: no existe un algoritmo universalmente superior, siendo una validación experimental del Non Free Lunch Theorem. Se demostró que la efectividad no reside en un método único, sino en la selección de la herramienta adecuada para la naturaleza específica del problema, ya sea la topografía multimodal de una función o la complejidad de un desafío logístico del mundo real.

El análisis comparativo también subrayó desafíos metodológicos, como la necesidad de múltiples simulaciones para obtener conclusiones fiables ante la variabilidad estocástica de los algoritmos (puntos iniciales, semillas de inicialización). Finalmente, se reafirma que la optimización es un pilar en incontables dominios modernos, desde la ingeniería y las finanzas hasta el corazón del aprendizaje automático que mejora optimizando parámetros y funciones de pérdida.

Así, el estudio de los algoritmos bioinspirados nos enseña a ver la optimización como un conjunto de estrategias diversas, recordándonos que las soluciones más eficientes a menudo provienen de abstraer los principios de un sistema que ha estado en constante perfeccionamiento durante millones de años: la propia naturaleza.

Bibliografía

Reporte de Contribución Individual

Carlos José Quijano Valencia: Responsable de la Parte 2 (Optimización combinatoria), levantamiento de supuestos de costo (salario, peajes, combustible, vehículo de referencia), construcción de la matriz de distancias y tiempos con la Google Distance Matrix API, implementación en R de los Algoritmos Genéticos (GA) y Colonia de Hormigas (ACO) para el TSP de las 13 ciudades, análisis comparativo de costos GA vs ACO y redacción de conclusiones en esa sección, generación de la animación GIF con el recorrido óptimo sobre el mapa de Colombia, documentación del código y subida al repositorio Git.

Miller Alexis Quintero García: Optimización por enjambre de partículas con pso, definición de funciones estándar simplifcadas para la generación de: gráficos 3D, curvas de nivel, gráficos 2D, y generación de GIFs de procesos de optmización. Rotulación de gráficas con las opciones de las celdas código R y con HTML; integración y estandarización de todas las secciones de la parte 1 en base al trabajo del equipo, simulaciones múltiples y gráficos comparativos de los métodos heurísticos, formulación de observaciones y conclusiones de la comparación de métodos heurísticos y descenso por gradiente, finalizando con bibliografía de la parte 1.

Kelly Yojana Ospina Correa: Optimización por algoritmos evolutivos, creación de workspace de trabajo en R, definición en código de funciones a optimizar y espacio de búsqueda, implementación de la función de monitoreo personalizada para la función ga la librería GA y su procesamiento de datos para el GIF. Formulación de conclusiones y análisis alrededor de los algoritmos evolutivos en la parte 1, y revisión de estilos/visuales del trabajo.

Mateo Sebastián Mora Montero: Implementación manual y personalizada del algoritmo de descenso por gradiente, añadiendo los atributos y métodos para el monitoreo, definición de funciones especifícas en código para los gradientes de las funciones. Revisión ortográfica y gramática del trabajo en general.

Stiven Julio Doval: Implementación de optimización por evolución diferencial con la función DEoptim de la librería de mismo nombre, incluyendo creación del objeto de control y algoritmo de monitoreo para la recolección de información para el GIF. Revisión ortográfica y de rotulación de elementos visuales. Adición de bibliografía basada en papers científicos.