Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Escu
ela
Po
lité
cn
ica S
up
erio
r de
Ja
én
UNIVERSIDAD DE JAÉN Escuela Politécnica Superior de Jaén
Trabajo Fin de Grado
jsEO2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en
JavaScript
Alumno: Javier Guzmán García Tutor: Víctor Manuel Rivas Santos Dpto: Departamento de Informática
Septiembre, 2017
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 1
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 2
Universidad de Jaén
Escuela Politécnica Superior de Jaén
Departamento de Informática
Don Víctor Manuel Rivas Santos , tutor del Trabajo Fin de Grado titulado: jsEO2: Uso
y ampliacion de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript, que
presenta Javier Guzmán García, autoriza su presentación para defensa y evaluación
en la Escuela Politécnica Superior de Jaén.
Jaén, Septiembre de 2017
El alumno: Los tutores:
Javier Guzmán García Víctor Rivas Santos
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 3
Índice
Resumen .................................................................................................................................. 5
1. Introducción....................................................................................................................... 6
1.1. Motivación .................................................................................................................. 6
1.2. Objetivos .................................................................................................................... 8
1.3. Metodología ............................................................................................................... 8
1.4. Planificación ............................................................................................................. 10
1.5. Presupuesto ............................................................................................................. 11
1.6. Tecnologías y librerías utilizadas .............................................................................. 13
2. Algoritmos Genéticos ...................................................................................................... 21
2.1. Ventajas y Desventajas ........................................................................................... 23
2.2. Cuando se puede usar un Algoritmo Genético ........................................................ 23
2.3. Función Objetivo ..................................................................................................... 24
2.4. Selección................................................................................................................. 24
2.5. Cruce ...................................................................................................................... 26
2.6. Mutación ................................................................................................................. 29
2.7. Modelos .................................................................................................................. 32
3. Algoritmos Multi-objetivo ................................................................................................. 34
3.1. Algoritmo NSGA-II ................................................................................................... 35
3.1.1. Estructura ......................................................................................................... 36
4. Frameworks para Algoritmos Evolutivos .......................................................................... 39
4.1. Frameworks en JavaScript ...................................................................................... 39
4.2. Frameworks en otros lenguajes ............................................................................... 43
4.3. jsEO ........................................................................................................................ 48
4.3.1. ¿Qué es jsEO? ................................................................................................. 48
4.3.2. Ventajas e Inconvenientes ............................................................................... 48
4.3.3. Estructura ......................................................................................................... 49
4.3.4. ¿Cómo funciona? ............................................................................................. 50
4.3.5. Problemas ya desarrollados en jsEO................................................................ 51
5. Resolución del trabajo ..................................................................................................... 53
5.1. Análisis, diseño e implementación ............................................................................ 53
5.1.1. Análisis............................................................................................................. 53
5.1.2. Diseño .............................................................................................................. 58
5.1.3. Implementación ................................................................................................ 67
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 4
5.2. Pruebas ................................................................................................................... 83
6. Conclusión ...................................................................................................................... 88
7. Anexos ............................................................................................................................ 89
Anexo I Instalación de Node.js ....................................................................................... 89
Anexo II Instalación de MongoDB ................................................................................... 97
Anexo III Código fuente del controlador del servidor Node y de los modelos para la base
de datos con MongoDB .................................................................................................... 102
Anexo IV Código fuente de los ficheros comunes utilizados para el desarrollo de los
problemas 105
Anexo V Tablas con resultados para configuración del algoritmo .................................. 108
Bibliografía ........................................................................................................................... 109
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 5
Resumen
Español:
Este proyecto está basado en el uso y desarrollo de una biblioteca de Algoritmos
Bioinspirados escrita en JavaScript. Para ello, se ha realizado un estudio previo de los
algoritmos, operadores y codificaciones existentes, para implementar nuevos problemas,
incluyendo sus operadores y sus codificaciones, con el fin de resolverlos mediante los
algoritmos ya desarrollados en esta biblioteca. También se ha desarrollado un nuevo
algoritmo multi-objetivo, y al igual que antes, todos sus operadores y su codificación, así como
cualquier otra mejora que se haya encontrado oportuna para tal fin.
Se ha implementado un servidor junto con una base de datos para utilizar un modelo
de islas y ver el funcionamiento de los algoritmos al intercambiar datos entre los distintos
usuarios que han utilizado la aplicación. Por último, se ha hecho un estudio sobre cómo se
comporta el algoritmo y cuan eficaz es a la hora de resolver los problemas desarrollados.
English:
This project is based on the use and development of a library of Bioinspirational Algorithms written in JavaScript. To this end, a previous study of the existing algorithms, operators and codifications has been carried out, to a posteriori, to implement new problems, including their operators and their encodings, in order to solve them by the algorithms already developed in this library. A new multi-objective algorithm has been developed, as well as before, all its operators and their codification, as well as any other improvements that have been found suitable for this purpose. A server has been implemented along with a database to use an island model and to see the operation of the algorithms when exchanging data between the different users who have used the application. Finally, a study has been done on how the algorithm behaves and how effective it is when solving the problems developed.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 6
1. Introducción
1.1. Motivación
El lenguaje JavaScript fue introducido en Netscape Navigator en 1995 y fue adoptado
rápidamente por otros navegadores web. En poco tiempo, se propuso como estándar por
ECMA International, al que llamo ECMAScript. Este lenguaje interpretado por varios
navegadores web, les dio a estos la capacidad para realizar cualquier otro cálculo u operación
aparte de la estrictamente necesaria para procesar el código HTML.
Ilustración 1. Navegador Netscape 2.02
Aunque se diseñó principalmente para operar sobre el Document Object Model (DOM)
de una web, se ha convertido en el lenguaje más popular, ya que utilizando un único lenguaje
de programación, y gracias a Node.js, se puede crear una aplicación web cliente-servidor
completa.
Ilustración 2. Document Object Model
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 7
JavaScript tiene varias características bastante buenas para ser usado junto con la
computación evolutiva. La primera tiene que ver con el propio intérprete, ya que la mayoría de
las aplicaciones web hoy en día se ejecutan dentro de los navegadores web. Esto le da a
JavaScript la oportunidad de construir aplicaciones web que incorporen soluciones evolutivas,
como si se tratase de una aplicación de escritorio.
Además, como los navegadores web hoy en día se pueden encontrar en la mayoría de
los sistemas operativos y dispositivos existentes en el mercado, ya sean tablets,
smartphones, etc. permite la posibilidad de crear una multiplataforma que pueda operar con el
menor esfuerzo posible o ninguno.
La segunda característica, tiene que ver con su naturaleza comunicativa que viene
intrínseca en los navegadores web, es decir, estos fueron diseñados principalmente para
actuar como clientes y recibir y enviar datos a los servidores web. Esto facilita bastante la
tarea de ejecutar cualquier tipo de algoritmo de manera colaborativa, pudiendo hacer uso de
diferentes hardware y software.
Ilustración 3. Comunicación con Node.js
También está el hecho de que los usuarios sin ningún conocimiento técnico en la
materia, pueden hacer usarlo a través de la interfaz web. En este sentido, usando el
navegador web para ejecutar este tipo de aplicaciones hace que no se diferencie mucho de la
forma en que la gente, por ejemplo, normalmente lee el periódico o hace las compras.
Es por ello que JavaScript se convierte así en una buena forma de obtener la
participación de tantos usuarios como sea posible, ya que en este sentido el uso de los
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 8
navegadores tiene muchas ventajas. Por ejemplo, los usuarios no tienen la necesidad de
tener que descargar ninguna aplicación especial.
Dado que JavaScript nos proporciona varias facilidades en todos los sentidos
anteriormente comentados, considero que es una buena forma de probar algoritmos
genéticos pudiendo obtener la mayor participación posible de usuarios con respecto a una
aplicación de escritorio, así como poder hacer un estudio mucho más exhaustivo de los
resultados obtenidos y todo ello sin necesidad de instalar ningún tipo de aplicación especial.
1.2. Objetivos
El fin de este TFG pretende resolver una serie de hitos:
Comprender el funcionamiento de la biblioteca de algoritmos evolutivos jsEO Dominar el uso de la plataforma pública de gestión de repositorios GitHub
Demostrar que mediante el uso de esta librería, se han resuelto problemas de optimización combinatoria tales como:
o TSP (Viajante de comercio) o N-Reinas
Desarrollar nuevas clases y algoritmos del ámbito del soft computing, como los algoritmos multi-objetivo, que permitan la evolución de cromosomas complejos.
Realizar un estudio comparativo del algoritmo o algoritmos implementados sobre al menos uno de los problemas implementados
1.3. Metodología
La metodología utilizada para desarrollar este proyecto ha sido la siguiente:
1. Análisis de la biblioteca jsEO existente en el repositorio de GitHub:
o Realización de un fork de la versión actual
o Ejecución de los algoritmos de ejemplo ya desarrollados
o Documentar, de manera breve, las clases que componen a esta
biblioteca
o Implementar todos los elementos y/o componentes necesarios para
resolver, el o los problemas de optimización combinatoria que se hayan
propuesto
2. Análisis de requerimientos del sistema, diseño e implementación de alguna de
las siguientes mejoras en la biblioteca:
o Inclusión de un nuevo tipo de algoritmo, como los algoritmos multi-
objetivo
o Inclusión de nuevos tipos de cromosomas, distintos a los ya
desarrollados, así como los operadores que se le apliquen a estos
o Cualquier otra mejora que se pueda desarrollar durante la realización
del proyecto
3. Diseñar y elaborar un conjunto de experimentos que muestren cómo funciona
el algoritmo o los algoritmos implementados
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 9
o Documentar estos experimentos y hacer un análisis comparativo de los
resultados obtenidos
En la primera fase, básicamente, lo que se ha hecho ha sido un estudio de cómo
funcionaba la biblioteca, estudiando el código de esta y ejecutándola para ver los resultados
obtenidos. Para ello, se ha realizado un fork de la biblioteca en GitHub, cuya definición en
inglés, “significa bifurcación, y lo que hace es la creación de un proyecto en una dirección
distinta de la principal u oficial tomando el código fuente del proyecto ya existente.”
(Wikipedia)
Ilustración 4. Repositorio para jsEO donde se ha realizado el fork
Como podemos ver, en este fork se han realizado un total de 83 commits para el
desarrollo del proyecto.
Después de esto y de haber documentado las clases de la biblioteca, se pasó a la
elección de los problemas que iban a ser resueltos por estos algoritmos ya desarrollados. En
nuestro caso los problemas elegidos para ser resueltos han sido el TSP o Viajante de
Comercio y el problema de las N-Reinas.
Cuando ya tenemos implementados estos problemas, pasamos a la segunda fase. En
esta fase, no se sigue el orden establecido en la metodología, sino que, primero se creó la
nueva representación de los cromosomas para los problemas propuestos así como sus
operadores.
Una vez hemos desarrollado por completo los problemas y comprobar que funcionan
completamente, se pasó al desarrollo de un nuevo algoritmo. En este caso se optó por
desarrollar un algoritmo multi-objetivo para resolver el problema del TSP. En este caso el
algoritmo multi-objetivo desarrollado ha sido el NSGA-II, explicado en el apartado 3.5., para el
cual a parte de sus operadores de cruce y mutación, también desarrollamos más operadores,
también explicados en el apartado 3.5.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 10
Por ultimo en esta fase, una vez completamos los dos apartados anteriores,
implementamos todas las mejores necesarias para un correcto funcionamiento de estos.
Y en la tercera y última fase, hemos probado todo lo implementado anteriormente con
diferentes configuraciones, es decir, variando las probabilidades de mutación y cruce, el
número de individuos y generaciones, etc.
A partir de los resultados obtenidos, hemos realizado un estudio comparativo de como
mejora el algoritmo modificando los parámetros de entrada, es decir, las probabilidades de
cruce y mutación, el número de individuos, el número de generaciones, etc.
También se han obtenido resultados para ver como mejora el algoritmo utilizando el
modelo de islas, en función del número de dispositivos que intercambien soluciones entre sí,
es decir, como mejoran los algoritmos desarrollados en función de la ejecución de un solo
dispositivo o varios dispositivos conectados a la vez.
1.4. Planificación
Atendiendo a la metodología anterior, y siguiendo los objetivos anteriormente
descritos, se han establecido los siguientes plazos para el desarrollo del proyecto:
Semana Descripción Tiempo (h)
1 Estudio de la biblioteca jsEO 12
2 Elección de los problemas a desarrollar 6
3 Selección de los operadores a usar para cada problema 8
4 y 5 Implementación de los problemas a resolver 15
6 y 7 Comprobación y corrección de errores de los problemas desarrollados
12
8 Implementación del algoritmo multi-objetivo NSGA-II 12
9 y 10 Implementación del problema a resolver mediante el algoritmo multi-objetivo, así como sus operadores
16
11 y 12 Implementación de métodos para la mejora de la biblioteca
20
13 Diseño del servidor 8
14 y 15 Implementación del servidor 15
16 y 17 Pruebas y corrección de errores del servidor 12
18 Diseño de la interfaz web 9
19 y 20 Implementación de la interfaz web 14
21 y 22 Reestructuración del sistema de ficheros 6
23 y 24 Realización de experimentos con la biblioteca 20
25 a 28 Elaboración de la documentación 116
Total horas 301 Tabla 1. Asignación de los tiempos según las tareas
El total de horas dedicadas a la realización de este proyecto es de 301 horas.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 11
1.5. Presupuesto
El coste de un proyecto se basa en la mano de obra necesitada para la realización de
este, todo el material hardware utilizado, el software usado y la instalación y la configuración
de lo que se ha desarrollado.
El presupuesto del desarrollo del proyecto se basa en las horas trabajadas, que son
301, y cuyo desglose se puede ver en el apartado de planificación. El importe por hora es de
16.50€/h.
Como nuestra biblioteca está escrita en JavaScript y solo necesitamos un navegador
web, independientemente del dispositivo que lo ejecute, el coste hardware de este proyecto
es de 0€.
También sabemos que al solo tener que utilizar el navegador web,
independientemente del sistema operativo en el que se esté ejecutando y que todas las
librerías y tecnologías utilizadas son de código abierto, el coste software de este proyecto es
de 0€.
En nuestro caso hemos tenido que instalar Ubuntu y los paquetes necesarios para
poder ejecutar nuestra aplicación desde este. Por ello, se necesitarían unas 3 horas, cada
una por un importe de 100 €/h. En cuanto a la configuración, no es necesaria ninguna en
especial, aunque en caso de que el usuario así la requiriera, esta supondría un trabajo de
aproximadamente otras 2 horas adicionales, por el mismo importe que la instalación.
En la siguiente hoja de presupuesto queda todo más detallado:
PRESUPUESTO Proyecto: jsEO2
Desarrollador: Javier Guzmán García
Jaén
NIF: 77370889Q
Cliente: Universidad de Jaén Descripción:
Domicilio: Campus Las Lagunillas, s/n, 23071 Jaén Uso y ampliación de una biblioteca de algoritmos bioinspirados, escrita en JavaScript
Ciudad: Jaén
Código Artículo Unidades Precio Un. Subtotal % IVA Total con IVA
1 Coste Hardware 1 0,00 € 0,00 € 21,00% 0,00 €
2 Coste Software 1 0,00 € 0,00 € 21,00% 0,00 €
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 12
3 Mano de obra Grad. Informático 255 18,00 € 4.590,00 € 21,00% 5.553,90 €
4 Mano de obra Diseño Web 46 12,50 € 575,00 € 21,00% 695,75 €
5 Mano de obra Inst. Sistema 3 20,00 € 60,00 € 21,00% 72,60 €
6
Presupuesto Subtotal 5.225,00 €
Descuento
0,00 €
Base Imponible 5.225,00 €
I.V.A. 21,00%
0,00 € 0,00 € 1.097,25 € 1.097,25 €
Recargo Equivalencia 0,00 €
TOTAL PRESUPUESTO 6.322,25 €
Tabla 2. Hoja de presupuesto
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 13
1.6. Tecnologías y librerías utilizadas
JavaScript es un lenguaje de programación interpretado, basado en el estandar
ECMAScript. Esta orientado a objetos y se utiliza sobre todo en el lado del cliente,
incorporado como parte de los navegadores web, consiguiendo asi una mejora en la interfaz
de usuario y paginas web dinamicas.
Tambien existe una parte de JavaScript en el lado del servidor. Se utiliza sobre todo
para aplicaciones externas a la web en la que nos encontramos. Tambien se le ha dado uso
en aplicaciones de escritorio, mayormente widgets.
Tiene una sintaxis parecida a C, pero utiliza nombres procedentes del lenguaje Java.
Aunque, tanto Java como JavaScript tienen semánticas y propósitos totalmente diferentes.
Todos los navegadores modernos de hoy en día interpretan el código JavaScript que
esta integro en las páginas web e interactúan con esta mediante una implementación del
Document Object Model (DOM).
Hasta no hace mucho, se ha estado utilizando este lenguaje en páginas web para
realizar operaciones en el lado del cliente. Hoy en día, se usó se ha expandido bastante para
enviar y recibir información del servidor con ayuda de tecnologías como AJAX. Este se
interpreta al mismo tiempo que se va descargando la información junto con el código HTML.
(Wikipedia)
En nuestro caso, hemos utilizado JavaScript para programar esta biblioteca de
algoritmos bioinspirados.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 14
Node es un intérprete JavaScript del lado del servidor que cambia la noción de cómo
debería trabajar un servidor. Su objetivo es permitir desarrollar aplicaciones altamente
escalables y escribir código que pueda manejar varias conexiones simultáneas en una sola
máquina. Hoy en día, Node es el mejor intérprete para un servidor de JavaScript.
En lenguajes como Java y PHP, cada conexión genera un nuevo de 2 MB de memoria.
En un sistema que tiene 8 GB de RAM, esto da un número de conexiones concurrentes de
cerca de 4.000 usuarios.
Node soluciona esto cambiando la forma en que se realiza una conexión con el
servidor. En lugar de generar un nuevo hilo para cada conexión, cada una de estas dispara un
evento dentro del proceso del motor de Node. Esto garantiza que nunca se quedara
bloqueado por llamadas de E/S. Lo que significa que es un servidor escalable que nos
permitirá soportar decenas de miles de conexiones concurrentes.
Con respecto a cómo funciona Node, este ejecuta JavaScript en el servidor. Un motor
basado en JavaScript interpreta el código y lo ejecuta.
Como he dicho anteriormente, Node está basado en la programación orientada por
eventos. Es decir, quizás no estemos pulsando botones o ingresando texto en campos, pero
esto no implica que no estén sucediendo eventos. Con cada conexión, con cada dato que se
envía o cuando dejamos de recibirlos, se produce un evento.
El hecho de que Node utilice JavaScript es muy simple. JavaScript permite utilizar
funciones de forma anónima y además, la sintaxis que utiliza es parecía para cualquier
usuario que ya haya programado alguna vez. Las llamadas a estas funciones podemos
hacerlas cuando el evento sucede y podemos escribirlas en el mismo punto en el que este
sucede. (IBM)
Para poder utilizar el modelo de islas, hemos creado un servidor escrito en JavaScript
para el cual hemos necesitado usar Node.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 15
MooTools es un “framework web orientado a objetos para JavaScript, de código
abierto, compacto y modular”.
El objetivo de MooTools es poder desarrollar JavaScript sin importar qué navegador se
utilice y de una manera sencilla. MooTools tiene una API bastante documentada orientada
sobre todo a objetos. Mootools facilita el tema de la herencia, o lo que es lo mismo, la
jerarquía de clases.
Esta API, posee clases orientadas a objetos en JavaScript, lo que le da muchas
funcionalidades tales como el trabajo con capas, efectos diversos, Ajax, etc.
Con MooTools podemos crear cualquier script en el cliente fácilmente y de manera
rápida sin tener que preocuparnos del navegador que estemos utilizando. MooTools se usa
sobre todo para programar scripts complejos, que nos llevarían mucho tiempo si tuviésemos
que escribirlos desde cero.
Sus principales ventajas son:
Ligero: ocupa poco espacio y no sobrecarga mucho el navegador.
Modular: Está compuesto por varios módulos y podemos elegir aquellos que
más nos interesen para nuestra página web, sin tener que utilizar el resto,
ahorrando tiempo de descarga y procesamiento.
Libre de errores: es un sistema fiable ya que no emite errores en tiempo de
ejecución.
Soportado por una amplia comunidad: hay muchos desarrolladores que han
creado componentes adicionales listos para ser usados, como bien pueden ser
calendarios, editores de texto, etc. (Wikipedia)
Haciendo uso de MooTools, hemos podido crear clases y permitir la herencia de estas
para poder desarrollar nuestros problemas.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 16
MongoDB es un sistema de base de datos NoSQL, orientado a documentos,
desarrollado en código abierto.
MongoDB guarda estructuras de datos en documentos parecidos a JSON con un
esquema dinámico, logrando así que la integración de los datos en ciertas aplicaciones sea
más fácil y rápida.
Las características principales de MongoDB son:
Consultas Ad hoc
Indexación
Replicación
Balanceo de carga
Almacenamiento de archivos
Agregación
Ejecución de JavaScript del lado del servidor
Sin embargo MongoDB, también presenta una serie de desventajas:
No implementa las propiedades ACID
Problemas de consistencia
Bloqueo a nivel de documento
Las escrituras no son durables ni verificables
Problemas de escalabilidad
MongoDB guarda la estructura de los datos en documentos con un esquema dinámico
parecido a JSON, llamado BSON. Los elementos de los datos se conocen como documentos
y se guardan en colecciones. Una colección puede tener un número indeterminado de
documentos. Con respecto a una base de datos relacional, MongoDB se diferencia en que en
una colección puede haber documentos que contengan distintos campos, ya que no hay un
esquema fijo. (Wikipedia)
MongoDB nos ha permitido guardar los datos de nuestros problemas de manera que la
recuperación de estos sea lo más rápido y fácil posible.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 17
jQuery es una biblioteca basada en JavaScript, que simplifica la forma en que
podemos interactuar con los documentos HTML, manipular el DOM, manejar eventos, crear
animaciones, etc.
jQuery es software libre y de código abierto. Tiene Licencia MIT y Licencia Publica
General de GNU v2, por lo que se permite su uso en proyectos libres y privados.
jQuery ofrece unas funcionalidades desarrolladas todas ellas en JavaScript que de no
ser así, tendríamos que generar mucho más código.
Sus principales características son:
Selección de elementos DOM
La posibilidad de interactuar y modificar el árbol DOM
Eventos
Manipulación de hojas de estilo CSS
Efectos y animaciones, ya sean personalizadas o no
AJAX
Se le pueden agregar extensiones
Utilidades varias.
Compatibilidad con casi todos los navegadores
jQuery utiliza un único fichero en formato JavaScript, que contiene todas las
funcionalidades para manejar el DOM, eventos, efectos y AJAX. Aunque ya se han citado sus
características principales, queda decir que la más importante de todas ellas, es la de poder
modificar el contenido de una página web sin tener que recargar dicha página. (Wikipedia)
jQuery nos ha hecho falta a la hora de diseñar nuestra interfaz web, ya que era
necesaria para poder ejecutar ciertos scripts y estilos necesarios.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 18
Bootstrap es un framework de código abierto utilizado sobre todo para el diseño de
sitios y aplicaciones web.
Este contiene plantillas de diseño para tipografía, formularios, botones, paneles,
menús de navegación y otros elementos utilizados para el diseño HTML y CSS.
Bootstrap es modular y está formado por una serie de hojas de estilo LESS. Esta
incluye los componentes de las hojas de estilo. Los desarrolladores pueden configurar ese
mismo archivo seleccionando los componentes que quieren utilizar.
Entre sus características principales cabe destacar que es compatible con casi todos
los navegadores web. Tiene clases incorporadas para realizar un diseño responsive
directamente sin necesidad de que el desarrollador tenga que hacerlo.
Para poder utilizar este framework es necesario el uso de jQuery, ya que sin este
muchas de las funcionalidades no servirían.
Bootstrap, es el proyecto más popular en la plataforma GitHub y es utilizado por
organizaciones internacionalmente conocidas como la NASA. (Wikipedia)
Bootstrap nos ha permitido mejorar nuestra interfaz web y dotarla de un diseño
adaptativo, dotando a esta de una mejora sustancial con respecto a la anterior.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 19
Vis.js es una librería de visualización para proyectos web, diseñada para gestionar
grandes cantidades de datos de manera dinámica, así como la posibilidad de interactuar con
estos.
Vis.js nos permite crear varios gráficos, entre los que destacan:
Redes
Líneas temporales
Gráficos en 2D
Gráficos en 3D
Vis.js se puede ejecutar en casi todos los navegadores, incluidos los de los
dispositivos móviles, ya que incorpora funciones táctiles para teléfonos, tablets, etc. (Esteso)
Con Vis.js hemos podido, o al menos intentarlo, recrear un grafo para interpretar mejor
las soluciones que nuestro algoritmo nos ha dado.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 20
Chart.js es una librería Open Source bajo licencia MIT escrita en JavaScript, que sirve
para dibujar diferentes tipos de gráficos.
Chart.js nos deja mezclar diferentes tipos de gráficos y trazar datos sobre fechas,
logaritmos o escalas personalizadas. Esta biblioteca también incorpora animaciones que
pueden aplicarse, como al modificar los datos de manera dinámica. (Benitez)
Para mostrar la evolución de nuestras soluciones obtenidas a partir de nuestros
algoritmos, hemos utilizado unas gráficas para lo cual hemos necesitado usar Chart.js.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 21
2. Algoritmos Genéticos
Los algoritmos genéticos son “algoritmos de optimización, búsqueda y aprendizaje
inspirados en los procesos de evolución natural y evolución genética”. (Gacto Colorado,
2016)
Una definición bastante completa de un algoritmo genético es la propuesta por John
Koza:
5. Componentes de un Algoritmo Genético
El problema de la teoría de Darwin es que en la naturaleza, los individuos de una
población compiten entre sí. Aquellos individuos que sobreviven y atraen a otros individuos,
tienen mayor probabilidad de generar más descendientes.
Por otro lado, los individuos menos aptos darán lugar a un menor número de
descendientes. Esto quiere decir que los genes de los individuos mejor adaptados tendrán
más probabilidades de propagarse durante varias generaciones.
“Es un algoritmo matemático altamente paralelo que transforma un
conjunto de objetos matemáticos individuales con respecto al tiempo
usando operaciones modeladas de acuerdo al principio Darwiniano de
reproducción y supervivencia del más apto, y tras haberse presentado
de forma natural una seria de operaciones genéticas de entre las que
destaca la recombinación sexual. Cada uno de estos objetos
matemáticos suele ser una cadena de caracteres (letras o números)
de longitud fija que se ajusta al modelo de las cadenas de
cromosomas, y se les asocia con una cierta función matemática que
refleja su aptitud.” (Koza, 1992)
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 22
La reproducción entre los mejores individuos de una población puede producir
“superindividuos”. De esta manera, las especies progresan cada vez más obteniendo mejores
características.
Los Algoritmos Genéticos se utilizan de una forma muy parecida al comportamiento de
las especies reales. Estos operan sobre una población de individuos, donde cada una es una
posible solución. Cada individuo tiene un valor o fitness, que dependerá en como de buena
sea esa solución.
Cuanto mejor se adapte un individuo al problema presentado, más fácil será que sea
elegido para reproducirse, cruzándose así con otro individuo seleccionado. Este cruce dará
lugar a nuevos individuos, los cuales tendrán algunos genes iguales a los de sus padres.
De esta forma, se obtiene una nueva población, la cual sustituye a la anterior y refleja
que esta nueva población tiene mejores soluciones que la anterior.
6. Estructura AGs
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 23
Su pseudocodigo sería el siguiente:
𝐼𝑛𝑖𝑐𝑖𝑜
𝑡 = 0;
𝑖𝑛𝑖𝑐𝑖𝑎𝑙𝑖𝑧𝑎𝑟 𝑃(𝑡);
𝑒𝑣𝑎𝑙𝑢𝑎𝑟 𝑃(𝑡);
𝑀𝑖𝑒𝑛𝑡𝑟𝑎𝑠 (𝑛𝑜 𝑠𝑒 𝑐𝑢𝑚𝑝𝑙𝑎 𝑙𝑎 𝑐𝑜𝑛𝑑𝑖𝑐𝑖ó𝑛 𝑑𝑒 𝑝𝑎𝑟𝑎𝑑𝑎)ℎ𝑎𝑐𝑒𝑟
𝑡 = 𝑡 + 1;
𝑠𝑒𝑙𝑒𝑐𝑐𝑖𝑜𝑛𝑎𝑟 𝑃(𝑡) 𝑑𝑒𝑠𝑑𝑒 𝑃(𝑡 − 1)
𝑟𝑒𝑐𝑜𝑚𝑏𝑖𝑛𝑎𝑟 𝑃(𝑡)
𝑚𝑢𝑡𝑎𝑐𝑖𝑜𝑛 𝑃(𝑡)
𝑒𝑣𝑎𝑙𝑢𝑎𝑟 𝑃(𝑡)
𝐹𝑖𝑛𝑀𝑖𝑒𝑛𝑡𝑟𝑎𝑠
𝐹𝑖𝑛
2.1. Ventajas y Desventajas
Las principales ventajas de estos algoritmos son:
Operan a la vez con varias soluciones.
Cuando se usan para problemas de optimización, maximizar una función
objetivo, no les afectan tanto los máximos locales o falsas soluciones.
Son fáciles de ejecutar en las nuevas arquitecturas paralelas.
En cuanto a las posibles desventajas, se encuentran:
Usan operadores probabilísticos, lo cual puede llevar a no encontrar en
muchas ocasiones el máximo global.
Pueden converger muy rápidamente o no, en función de sus parámetros, como
bien pueden ser el tamaño de la población, el número de generaciones, etc.
2.2. Cuando se puede usar un Algoritmo Genético
El uso más común de los algoritmos genéticos ha sido la solución de problemas de
optimización, donde se ha comprobado que suelen ser muy eficientes y fiables. Sin embargo,
no siempre se pueden aplicar a todos los problemas, y por eso se recomienda tener en
cuenta las siguientes pautas antes de aplicar esta técnica:
Su espacio de búsqueda debe estar dentro de un rango.
Debe poder definirse una función de aptitud que nos indique como de buena o
mala es nuestra solución.
Las soluciones deben codificarse para que el ordenador pueda interpretarlas
fácilmente.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 24
El espacio de búsqueda es bastante importante, por lo que es aconsejable intentar
resolver problemas cuyo espacio de búsqueda sea discreto aunque sea bastante grande. Sin
embargo, se pueden utilizar espacios de búsqueda continuos, mientras que el rango de este
sea lo más pequeño en la medida de lo posible.
La función de aptitud es lo que nosotros conocemos como función objetivo. El
algoritmo genético únicamente maximiza, aunque también puede minimizar haciendo una
función inversa a la función encargada de maximizar, siempre y cuando que no se genere una
división por cero. Esta función, se encarga de “castigar” a las malas soluciones, y
“recompensar” a las buenas, así consigue que las buenas soluciones se extiendan más
fácilmente.
La codificación más utilizada es la de cadenas binarias, aunque también se utilizan
números enteros, reales y letras. La primera de estas codificaciones es la más popular ya que
es la que propuso originalmente Holland (investigador de la Universidad de Michigan que
desarrollo los Algoritmos Genéticos), siendo también la más fácil de implementar.
2.3. Función Objetivo
Dos aspectos muy importantes en los Algoritmos Genéticos son la elección de una
función objetivo y la codificación que utilicemos.
Principalmente nos interesa desarrollar funciones objetivo que comprueben que dos
individuos que se encuentren cerca en el espacio de búsqueda, deben ser parecidos. Por otra
parte, un posible inconveniente es el de que haya una gran cantidad de óptimos locales, o
que el óptimo global se encuentre muy aislado.
La metodología que usemos para construir una buena función objetivo es que esta
debe expresar el valor del individuo lo más real posible, aunque en muchos problemas de
optimización combinatoria, una gran parte del espacio de búsqueda contiene individuos no
válidos.
En el caso en el que los individuos tienen restricciones, hay una posible solución. La
primera seria aquella en la que los individuos que no verifican las restricciones, no se
consideran como tales, y por tanto, se siguen realizando cruces y mutaciones hasta conseguir
individuos que si sean válidos, o bien, se les asigna un valor de fitness igual a cero.
2.4. Selección
Una posible clasificación de métodos de selección podría ser:
Métodos de selección dinámicos, en los que las probabilidades de selección
varían de una generación a otra generación. Por ejemplo, la selección
proporcional a la función objetivo.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 25
Métodos de selección estáticos, en los que las probabilidades no varían en
absoluto. Por ejemplo, la selección basada en rangos.
Si todos los individuos tienen una probabilidad de selección distinta de cero, el método
de selección se denomina preservativo. En caso contrario, se le llama extintivo.
La selección se puede hacer de varias formas. Por ejemplo, se pueden seleccionar al
azar usando algún método que dé prioridad a los individuos con mayor fitness.
La función de selección más utilizada, es la función de selección proporcional, en la
que cada individuo tiene una probabilidad de ser elegido como padre.
Una de las formas de solucionar el problema de la rápida convergencia debido a la
existencia de superindividuos, es ejecutar la selección en proporción al rango del individuo, de
manera que así se produce un reparto más uniforme a la hora de seleccionar.
Un esquema de selección, introducido por Brindle, es el llamado muestreo
estocástico, en el cual se asigna una probabilidad de selección o una puntuación a cada
individuo de la población, en función, de su valor de fitness.
Hay muchos tipos de muestreo estocástico. Cuando se trata de algoritmos genéticos,
se usan generalmente 4 tipos:
Por sorteo: la puntuación asignada a cada individuo es como la probabilidad
de este para ser elegido, y se realizan k ensayos de una variable aleatoria con
dicha distribución de probabilidades.
Por restos: a cada individuo se le asigna una probabilidad 𝑝𝑖 ∗ 𝑘 puestos en la
muestra. Después, los individuos de la población se reparten el resto de
puestos que quedan libres, en función de sus puntuaciones. Este reparto de
puestos se suele hacer por sorteo.
Universal o por ruleta: es parecido al muestreo por sorteo, solo que en este
solo se genera un único numero aleatorio, y con este se asignan todas las
muestras de forma similar a como gira una ruleta.
Por torneo: cada individuo de la muestra se toma eligiendo el mejor de un
conjunto de individuos z, tomados de forma aleatoria de la población. Este
procedimiento se repite hasta que la muestra se completa. El tamaño de z,
normalmente suele estar comprendido entre un valor de 2 o 3. En este
muestreo, no se le asignan puntuaciones a los individuos.
En este último método, si cambiamos el número de individuos que participan en cada
torneo se puede modificar la presión de selección. Cuando participan muchos individuos, los
peores individuos apenas se reproducen.
Un caso concreto es el elitismo global. El cual es un torneo en el que participan todos
los individuos de la población. Cuando el tamaño del torneo es menor que el tamaño de la
población, los peores individuos lo tienen más fácil para ser seleccionados.
El método descrito anteriormente se basa en la ruleta sesgada. Según esta, los
mejores individuos tendrás más facilidades para ser elegidos y varias veces en cada
generación, mientras que los peores, puede que se seleccionen alguna vez que otra.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 26
Finalmente, después del proceso de selección y una vez elegidos dos padres, se
combinan los cromosomas de ambos, utilizando el operador de cruce y después, los
mutamos, mediante el operador de mutación.
2.5. Cruce
El operador de cruce, utiliza los dos padres seleccionados y utiliza sus cadenas de
cromosomas para crear cuatro subcadenas, dos para cada cromosoma. Después se
intercambian las subcadenas finales, produciéndose dos nuevos cromosomas, que heredaran
los genes de sus padres.
Normalmente, el operador de cruce no se usa con todos los pares de individuos que
han sido elegidos, sino que se aplica de manera aleatoria, casi siempre con una probabilidad
entre 0.5 y 1.0. En el caso en que el operador de cruce no se llegue a utilizar, la
descendencia se consigue duplicando a los padres.
En los Algoritmos Genéticos, podemos usar una amplia variedad de operadores de
cruce, incluso pudiendo utilizar varios a la vez.
Uno de ellos, podría ser el operador de cruce basado en un punto, en el cual los
dos individuos seleccionados, se combinan en función de un punto de corte aleatorio e
intercambian los genes de cada individuo, que se encuentran a la derecha de dicho punto.
Dicho de otro modo, teniendo un cromosoma X y otro Y, de tamaño N, seleccionamos
un punto de corte comprendido entre 0 y N. Una vez elegido este punto de corte, se generan
dos descendientes, uno en función de cada padre, los cuales mantienen los k genes
comprendidos entre 0 y el punto de corte, utilizando los genes comprendidos entre el punto de
corte y N del otro padre.
7. Operador de cruce basado en un punto
De Jong investigo el operador de cruce basado en múltiples puntos, del cual dijo
que era una mejora del algoritmo anterior. La ventaja de este operador, es que el espacio de
búsqueda puede ser explorado más fácilmente.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 27
En este operador, los cromosomas son como un circulo, en el que se eligen
aleatoriamente dos puntos, tal y como se indica en la siguiente figura.
8. Operador de cruce basado en dos puntos
El operador de cruce uniforme, genera cada gen de los hijos a partir del
correspondiente gen de uno de los dos padres, que se elige a partir de una “mascara de
cruce”. Cuando tenemos un 1, el gen se coge del primer padre, en caso contrario, el gen se
coge del segundo padre.
9. Operador de cruce uniforme
Como podemos ver, este tipo de operadores están basados en codificación binaria,
excepto el cruce uniforme que sirve para cualquier tipo de codificación. Para codificación real,
existen otros tales como la recombinación aritmética, que crea un descendiente dados dos
padres, donde cada gen del hijo viene a ser una media de la suma de los genes de ambos
padres.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 28
10. Operador de cruce aritmético
Otro operador de cruce para codificación real es el BLX-α, en el que teniendo dos
cromosomas, este operador crea dos descendientes 𝐶1 = (𝑐11, … , 𝑐1𝑛) y 𝐶2 = (𝑐21, … , 𝑐2𝑛) a
partir de estos de manera que:
𝐻𝑘 = (ℎ𝑘1, … , ℎ𝑘𝑖 , … , ℎ𝑘𝑛), 𝑘 = 1, 2
donde ℎ𝑘𝑖 se genera de forma aleatoria en el intervalo:
[𝐶𝑚𝑖𝑛 − 𝐼 ∗ α, 𝐶𝑚𝑎𝑥 + 𝐼 ∗ 𝛼],
Siendo:
𝐶𝑚𝑎𝑥 = max{𝑐1𝑖 , 𝑐2𝑖}
𝐶𝑚𝑖𝑛 = min{𝑐1𝑖 , 𝑐2𝑖}
𝐼 = 𝐶𝑚𝑎𝑥 − 𝐶𝑚𝑖𝑛
𝛼 ∈ [0,1]
Sería algo así como lo siguiente:
11. Operador de cruce BLX-α
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 29
Para representaciones de orden, tenemos el operador de cruce OX, el cual toma una
subsecuencia del padre e intenta conservar el orden correspondiente de los fenotipos lo más
parecido posible de la madre.
12. Operador de cruce OX
2.6. Mutación
El operador de mutación se aplica a cada hijo generado por dos padres, y consiste en
modificar aleatoriamente uno o varios genes del cromosoma. Aunque en principio parezca
que el operador de cruce tiene más relevancia que el operador de mutación, este último se
asegura que todo el espacio de búsqueda será examinado al menos una vez, ya que asegura
la convergencia de los Algoritmos Genéticos.
En biología se consideran 2 tipos de mutaciones, aquellas que se heredan, conocidas
como generativas, y aquellas que no se heredan, conocidas como somáticas.
La mutación es considerada un operador básico, que añade un poco de aleatoriedad
en el entorno de los individuos de la población. Aunque se considera que el operador de cruce
se encarga de recorrer el espacio de búsqueda, también parece que se desvincula de varios
experimentos realizados por investigadores, lo cual le ha dado bastante relevancia al
operador de mutación, el cual ha ido ganando importancia cada vez que la población
convergía. Este estudio fue realizado por Lawrence Davis (1991).
J. David Schaffer y col. (1989) demostraron que el efecto que el operador de cruce
tenía a la hora de recorrer el espacio de búsqueda era menor del que se esperaba. A esto lo
llamaban evolución primitiva, en la cual solo existía la selección y la mutación. Decían que en
este tipo de evolución, se superaba con creces una evolución basada únicamente en la
selección y el cruce. Otra conclusión que sacaron de su investigación, fue que calcular el
valor óptimo para la probabilidad a la hora de hacer la mutación era mucho más importante
que el valor relativo a la probabilidad que tenía el cruce.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 30
En la mayoría de las implementaciones de Algoritmos Genéticos que nos podemos
encontrar, podemos apreciar que tanto la probabilidad de cruce como la de mutación se
mantienen fijas, aunque muchos autores basándose en sus resultados experimentales, han
apreciado mejoras significativas al aumentar la probabilidad de mutación conforme
aumentaba también el número de generaciones.
Debido a todo esto, podemos suponer que los principales motivos para utilizar este
operador, son los siguientes:
Desbloqueo del algoritmo: en caso de que el algoritmo se bloquee en algún
punto, por ejemplo al encontrar un óptimo local, una mutación puede sacarlo de
dicho punto añadiendo nuevos fenotipos de otras zonas del espacio de
búsqueda.
Acabar con poblaciones degeneradas: puede suceder que no encontremos
inicialmente con un óptimo local, lo cual nos impediría recorrer el espacio de
búsqueda, dando lugar a que la población acabe teniendo los mismos
fenotipos. Al introducir la mutación, esta introduce nuevos genes que facilitan el
poder seguir explorando este espacio.
Incrementar el número de saltos evolutivos: esto es más conocido como
escapar de un óptimo local. Con la mutación, se permite explorar nuevas
soluciones, por lo que si el espacio de búsqueda es bueno, se producirá un
salto evolutivo cada vez que se utilice la mutación, que dará lugar a que el
espacio de búsqueda se agrande de manera exponencial.
Enriquecer la diversidad genética: esto viene a decir que en caso de que se
tenga una población con poca diversidad genética, la mutación ayuda a
prevenir que esto sea así.
Bien es cierto que si nuestra probabilidad de mutación es bastante alta, aparezca la
conocida como deriva genética. Esta significa que los individuos menos frecuentes, tienden a
desaparecer, logrando así que encontrásemos individuos muy parecidos y por tanto,
tuviésemos poca diversidad.
Es por ello, que normalmente al principio del algoritmo se utiliza una probabilidad alta
para la mutación y así aumentar la diversidad, y luego ir disminuyéndola en cada generación,
hasta llegar a tener una probabilidad baja cuando el algoritmo finaliza, de manera que se
consigue así la convergencia.
Entre los operadores de mutación existentes, podemos encontrar varios en función del
tipo de representación que estemos usando. Por ejemplo para representaciones basadas en
cadenas binarias, tenemos la mutación de bit.
Esta dice que solo hay una sola probabilidad de que se produzca una mutación de
algún bit, y en caso de que suceda, se elige uno de manera aleatoria, y lo invierte.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 31
13. Operador de mutación de un solo bit
También, para representación binaria, tenemos el operador de mutación multibit.
Este operador es idéntico al anterior, solo que se aplica a varios bits a la vez.
La mutación de gen, simétrica a la mutación de bit, con la única diferencia de que en
vez de aplicarse a un bit se aplica a un gen. Puede hacerlo sumando un valor aleatorio,
constante o sustituyéndolo por otro gen nuevo, también de manera aleatoria. La mutación
multigen, es igual que la mutación multibit, solo que sustituye un conjunto de genes.
Para representaciones reales, se suele utilizar una distribución Gaussiana o Normal
𝑁(0, 𝜎), es decir, alterando los valores mediante un valor aleatorio, donde
0 es la media
σ es la desviación típica
𝑥′𝑖 = 𝑥𝑖 + N(0, 𝜎𝑖)
para cada parámetro.
También existe lo que se conoce como mutación de intercambio, esta consiste en
seleccionar 2 genes aleatoriamente e intercambiarlos.
14. Mutación de intercambio
Y por último tenemos la mutación por barajado, la cual nos sirve tanto para
representación de cadenas binarias como de orden, y funciona mediante una probabilidad
que indica si se produce o no la mutación. De producirse, elige 2 bits o genes de forma
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 32
aleatoria, en función de la representación usada, y “baraja” los genes que haya entre los 2
elegidos.
2.7. Modelos
Dentro de los Algoritmos Genéticos, podemos encontrarnos con que hay dos modelos
a seguir: estacionario y generacional.
El modelo estacionario se encarga de elegir dos individuos de la población, o padres,
en función del método de selección utilizado, y les aplica los operadores genéticos ya
comentados.
Una vez estos generan el/los descendiente/s, estos reemplazan a uno/dos
cromosoma/s de la población inicial. Generalmente los individuos sustituidos suelen ser los
que peor valor fitness tienen dentro de dicha población.
Este modelo produce una presión de selección muy elevada cuando reemplaza a los
peores individuos de la población, lo cual puede llevarle a tener una convergencia rápida.
15. Modelo estacionario
El modelo generacional se encarga en cada iteración de generar una población
completa con nuevos individuos. Esta nueva población siempre reemplaza a la antigua.
Se diferencia del modelo estacionario, en que en vez de sustituir a los peores
individuos de cada población durante cada iteración, va guardando el mejor de cada una de
ellas y los compara, quedándose así con el mejor de todos los individuos generados por
alguna población. A esto se le conoce como elitismo.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 33
Este modelo suele ir modificando la probabilidad de cruce y/o mutación para elevar y
disminuir la convergencia.
16. Modelo generacional
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 34
3. Algoritmos Multi-objetivo
La mayoría de los problemas reales se caracterizan por la existencia de varias
medidas de acción, que deberían ser optimizadas, o por lo menos deben cumplirse
simultáneamente. Como por ejemplo, cuando queremos tener la máxima cobertura en nuestro
teléfono móvil utilizando el menor número de antenas. Si variamos el número de antenas el
problema pasa de monoobjetivo a multi-objetivo.
Muchos de los problemas multi-objetivo pueden estipularse como problemas
monoobjetivos convirtiendo todos los objetivos excepto uno en limitaciones. En el ejemplo
anterior, podríamos fijar un número de antenas determinado y obtener la mayor cobertura
posible optimizando las posiciones. En caso de que no llegásemos al umbral de cobertura,
aumentaríamos el número de antenas y volveríamos a empezar con el proceso de
optimización.
En la optimización monoobjetivo, existe una función que evalúa la calidad de la
solución X, 𝑓 ∶ 𝑋 → 𝑌, 𝑑𝑜𝑛𝑑𝑒 𝑌 ⊆ ℝ. El valor de Y evalúa la calidad de la solución. De
manera, que para evaluar dos soluciones 𝑥1 y 𝑥2 se basara en función de los valores 𝑦1 y 𝑦2,
donde 𝑦1 = 𝑓(𝑥1) 𝑦 𝑦2 = 𝑓(𝑥2). En caso de buscar un máximo, la solución 𝑦1 es mejor que
𝑦2 𝑠𝑖 𝑦1 > 𝑦2.
En caso de que la función de evaluación, 𝑓, sea multidimensional entonces 𝑌 ⊆
ℝ𝑘 con k > 1. En este caso, decidir si una solución es mejor que otra es más complicado.
Un término que se ha difundido bastante es el de dominancia de Pareto. Este dice que
una solución 𝑦1 domina a otra solución 𝑦2, 𝑦1 ≻ 𝑦2, si ningúna componente de 𝑦1 es mas
pequeña que la componente 𝑦2 y al menos una componente es mayor. Si 𝑦1 ≻ 𝑦2, entonces
𝑥1 domina a 𝑥2, 𝑥1 ≻ 𝑥2. Por lo tanto una solución optima seria aquella que no es dominada
por ninguna otra solución.
En ese caso podemos asumir que pueden existir varias soluciones optimas, y que
estás estarán dentro de lo que se conoce como conjunto de Pareto, que es el conjunto de
𝑋∗ ⊆ 𝑋 y frente de Pareto a su igual en el espacio de soluciones 𝑌∗ ⊆ 𝑌.
Generar un conjunto de Pareto, es un trabajo bastante arduo para un ordenador, y
normalmente no se consigue generar dada la gran dificultad que el problema presenta.
Por tanto la finalidad de un MOEA (Multi-Objective Evolving Algorithm) es dar a las
personas un cumulo de soluciones muy cercanas al frente de Pareto, y que sean lo más
diversas posibles.
En este aspecto, hay tres tipos de estrategias para asignar el fitness:
Basada en agregación: En esta estrategia, las funciones objetivos se unen en
una sola función objetivo. Esta, estará totalmente parametrizada, e ira
modificando estos parámetros durante la ejecución del algoritmo para poder
encontrar soluciones no dominadas. La principal desventaja de este método es
que puede encontrar un frente de Pareto parcial.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 35
Basada en criterio: En esta, la función objetivo se modifica durante la fase de
selección. Cada vez que se elige a un individuo, una función objetivo diferente
decidirá si este pasa a la siguiente generación.
Basados en Pareto: En esta estrategia se utilizan distintos criterios. En
algunos casos se utiliza lo que se conoce como ranking de dominancia, que es
el número de individuos que dominan a dicho individuo. En otros, se utiliza el
concepto de profundidad de dominancia, que sirve para cuando la población
está dividida en frentes indicarnos a que frente pertenece dicho individuo.
3.1. Algoritmo NSGA-II
El algoritmo NSGA-II (Elitist Non-Dominated Sorting Genetic Algorithm) fue
propuesto por Deb y sus estudiantes en el año 2000. En este algoritmo, la población
descendiente Q, con un tamaño predefinido N, es creada usando la población de padres P,
con el mismo tamaño N. Después, las dos poblaciones se unen en una sola para formar una
población R de tamaño 2N.
Una vez unidas, y mediante un ordenamiento no dominado, se clasifica la población R en
diferentes frentes de Pareto. Cuando este proceso termina, la nueva población es generada a
partir de los frentes no dominados. Es decir, empieza a ser construida por el primer frente no
dominado, 𝐹1, y asi sucesivamente.
17. Frente de Pareto
Dado que la población tiene un tamaño N, y que esta población tiene el doble de
tamaño, no todos los frentes entraran dentro de la población, y por tanto, aquellos que no
consigan entrar se desecharan.
Cuando se está en el último frente que puede entrar en la población, es posible que no
todas sus soluciones consigan entrar en esta debido a que eso excedería del tamaño N, por
lo que se utiliza una estrategia, para seleccionar aquellos individuos mejor adaptados dentro
de dicho frente.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 36
18. Proceso de selección por frentes de no dominancia de NSGA-II
Durante las primeras generaciones esta estrategia es poco relevante, pero a medida
que se incrementa el número de generaciones, puede llegar el caso de que la mayoría de los
individuos pertenezcan al primer frente, llegando a tener este frente más de N individuos en
él, por lo que es sumamente importante que se utilice algún método para garantizar la
diversidad dentro del frente de Pareto.
3.1.1. Estructura
El algoritmo NSGA-II sigue los siguientes pasos:
Combina las poblaciones de padres, P, e hijos, Q, para crear 𝑅𝑡 = 𝑃𝑡 ∪ 𝑄𝑡.
Realiza un ordenamiento no dominado a R e identifica los frentes 𝐹𝑖 , 𝑖 =
1, 2, … , 𝑒𝑡𝑐.
Crear una población nueva 𝑃𝑡+1 = ∅, 𝑒 𝑖 = 1. Y mientras que |𝑃𝑡+1| + |𝐹𝑖| < N,
incorporar dicho frente a la población e incrementar i. |𝑃𝑡+1| = |𝑃𝑡+1| +
|𝐹𝑖| 𝑒 𝑖 = 𝑖 + 1.
Cuando está en el último frente, se utiliza el operador (𝐹𝑖′ < 𝐶), que ordena los
individuos por lo que se conoce como distancia de Crowding. Esta distancia es
la medida del espacio de búsqueda que se encuentra alrededor de i que no
está ocupado por otra solución de la población. De manera que una vez
ordenados los individuos de dicho frente por medio de este operador, incluimos
en 𝑃𝑖 los 𝑁 − |𝑃𝑡+1| individuos de dicho frente.
Por último, crear la población de hijos 𝑄𝑖+1 a partir de 𝑃𝑖+1 aplicando la
selección por torneo, el operador de cruce y el operador de mutacion.
El pseudocodigo del ordenamiento no dominado, que es el operador que nos organiza
la población en frentes, sería el siguiente:
𝑓𝑜𝑟 𝑒𝑎𝑐ℎ 𝑝 ∈ 𝑃
𝑆𝑝 = ∅
𝑛𝑝 = 0
𝑓𝑜𝑟 𝑒𝑎𝑐ℎ 𝑞 ∈ 𝑃
𝑖𝑓(𝑝 ≺ 𝑞)𝑡ℎ𝑒𝑛
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 37
𝑆𝑝 = 𝑆𝑝 ∪ {𝑞}
𝑒𝑙𝑠𝑒 𝑖𝑓 (𝑞 ≺ 𝑝)𝑡ℎ𝑒𝑛
𝑛𝑝 = 𝑛𝑝 + 1
𝑖𝑓 (𝑛𝑝 = 0)𝑡ℎ𝑒𝑛
𝑝𝑟𝑎𝑛𝑘 = 0
𝐹⊥ = 𝐹⊥ ∪ {𝑝}
𝑖 = 0
𝑤ℎ𝑖𝑙𝑒 𝐹𝑖 ≠ ∅
𝑄 = ∅
𝑓𝑜𝑟 𝑒𝑎𝑐ℎ 𝑝 ∈ 𝐹𝑖
𝑓𝑜𝑟 𝑒𝑎𝑐ℎ 𝑞 ∈ 𝑆𝑝
𝑛𝑞 = 𝑛𝑞 − 1
𝑖𝑓 (𝑛𝑞 = 0) 𝑡ℎ𝑒𝑛
𝑞𝑟𝑎𝑛𝑘 = 𝑖 + 1
𝑄 = 𝑄 ∪ {𝑞}
𝑖 = 𝑖 + 1
𝐹𝑖 = 𝑄
El operador (𝐹𝑖′ < 𝐶), utiliza un valor llamado rango de no dominancia y el valor de
distancia de Crowding de cada individuo. Esta distancia se calcula de la siguiente forma:
∀ 𝑘 ∈ 𝐹𝑖 , 𝐶𝑟𝑜𝑤𝑑𝑖𝑛𝑔(𝑘) = (𝑘 + 1(𝑥𝑗)) − (𝑘 − 1(𝑥𝑗)) donde j es el número del objetivo
analizado e i es el número del frente.
19. Distancia de Crowding
El operador de selección por torneo, en este caso sería binario, es decir, solo se eligen
2 individuos para realizar el cruce y generar descendientes y a estos se les aplicara el
operador de mutación.
El pseudocodigo de este algoritmo es:
𝐼𝑛𝑖𝑡𝑖𝑎𝑙𝑖𝑧𝑒(𝑃0)
𝑔𝑒𝑛𝑒𝑟𝑎𝑡𝑖𝑜𝑛 = 0
𝐸𝑣𝑎𝑙𝑢𝑎𝑡𝑒(𝑃0)
𝑤ℎ𝑖𝑙𝑒(𝑛𝑜𝑡 𝑐𝑟𝑖𝑡𝑒𝑟𝑖𝑎𝑆𝑡𝑜𝑝)𝑑𝑜
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 38
𝑅 = 𝑃 ∪ 𝑄
𝐹 = 𝑆𝑜𝑟𝑡𝑖𝑛𝑔 𝑁𝑜𝑡 − 𝐷𝑜𝑚𝑖𝑛𝑎𝑡𝑒𝑑(𝑅)
𝑃𝑡+1 = ∅
𝑖 = 1
𝑤ℎ𝑖𝑙𝑒 |𝑃𝑡+1| + |𝐹𝑖| ≤ 𝑁
𝐶𝑟𝑜𝑤𝑑𝑖𝑛𝑔𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒(𝐹𝑖)
𝑃𝑡+1 = 𝑃𝑡+1 ∪ 𝐹𝑖
𝑖 + +
𝑆𝑜𝑟𝑡𝑖𝑛𝑔𝐵𝑦𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒(𝐹𝑖)
𝑃𝑡+1 = 𝑃𝑡+1 ∪ 𝐹𝑖(1: 𝑁 − |𝑃𝑡+1|)
𝑄 = 𝑆𝑒𝑙𝑒𝑐𝑡𝑖𝑜𝑛 𝑎𝑛𝑑 𝑅𝑒𝑝𝑟𝑜𝑑𝑢𝑐𝑡𝑖𝑜𝑛(𝑃𝑡+1)
𝑔𝑒𝑛𝑒𝑟𝑎𝑡𝑖𝑜𝑛 + +
𝑃𝑔𝑒𝑛𝑒𝑟𝑎𝑡𝑖𝑜𝑛 = 𝑃𝑡+1
𝑟𝑒𝑡𝑢𝑟𝑛 𝑏𝑒𝑠𝑡𝑆𝑜𝑙𝑢𝑡𝑖𝑜𝑛
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 39
4. Frameworks para Algoritmos Evolutivos
4.1. Frameworks en JavaScript
En cuanto a frameworks evolutivos que estén desarrollados en JavaScript, es decir,
aquel que ejecuta un algoritmo genético, podemos encontrarnos con el siguiente en
http://rednuht.org/genetic_cars_2/ (ver Ilustración 20):
Ilustración 20. Algoritmo Genético de Coches en Rednhut
Como podemos aprecia este algoritmo crea una población de coches cuyo objetivo es
llegar cuanto más lejos posible. El que más lejos llegue se considera como el mejor individuo.
Como podemos apreciar en la ilustración 20, el mejor individuo es el que más lejos ha llegado
( véase Ilustración 21).
Ilustración 21. Mejor individuo de Genetic Cars
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 40
En este otro algoritmo (Ilustración 22) podemos encontrarnos con una población de
bípedos, que es como los denomina el algoritmo, cuyo objetivo es muy parecido al del
ejemplo anterior. Este algoritmo también pertenece a la plataforma Rednhut
http://rednuht.org/genetic_walkers/.
En este caso, los individuos tienen que dar la mayor de cantidad de pasos posibles de
una manera correcta. Como parámetro, ellos reciben una variable que les indica la distancia
que hay de su cabeza a sus pies y cuánto debe avanzar mientras se mantiene erguido. El
mejor individuo será el que mayor pasos logre dar correctamente.
Ilustración 22. Algoritmo genético para caminantes
Como podemos apreciar los mejores individuos son de color gris claro mientras que el
resto son todos de color azul. Los individuos más claros son los mejores individuos de la
población, ya que son los que más pasos logran dar de manera correcta.
Ilustración 23. Caminante moviéndose
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 41
Otro algoritmo que también he encontrado interesante (Ilustración 24) es el siguiente.
Se trata de una población de comensales. Estos comensales, las figuras de color rojo, tienen
como objetivo comerse el mayor número de plantas posibles, que son las figuras de color
verde. Este algoritmo se puede encontrar en
http://math.hws.edu/eck/jsdemo/jsGeneticAlgorithm.html.
Una generación en este algoritmo simula un “año” como si tuviese 250 días durante
los cuales esa población se come el mayor número de plantas. Una vez terminada esa
generación se crea una nueva población de “comensales,” la cual puede ser incluso mejor
que la anterior. Si se ejecuta con los parámetros por defecto, se podrá observar como los
individuos cada vez más mejorando más, hasta el punto de que cada día que pasa quedan
menos “plantas”.
Ilustración 24. Comensales moviéndose y comiendo plantas en la primera generación.
Después de unas cuantas generaciones, podemos observar (Ilustración 25) a simple
vista que el comportamiento de los comensales ha mejorado y que el nivel de plantas ha
descendido considerablemente.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 42
Ilustración 25. Comensales después de 87 generaciones.
Se puede apreciar a simple vista (Ilustración 26) que los comensales se han distribuido
mejor por el espacio de búsqueda, mejorando por tanto su comportamiento llegando a
comerse más plantas. En cada generación el espacio cambia, pero si nos fijamos en la
siguiente fotografía veremos notablemente la diferencia entre cómo se encuentra el espacio el
primer día de la generación 88, con respecto al último día.
Ilustración 26. Primer día de la generación 92.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 43
Ilustración 27. Ultimo día de la generación 92
Se aprecia que el entorno (Ilustración 27) se modifica cada día que pasa porque las
plantas también vuelven a crecer, pero si nos fijamos con atención, la mayoría de los
comensales del ultimo día no se encuentran en la misma posición que el primero, ya que han
evolucionado y con ellos tanto sus movimientos como la zona por la que se mueven.
4.2. Frameworks en otros lenguajes
Dentro de los diferentes lenguajes de programación existentes, hay muchos de ellos
que tienen frameworks diseñados específicamente para este tipo de algoritmos. Por ejemplo
en Java™ hay varios frameworks para el estudio de los algoritmos evolutivos, entre ellos
caben destacar JGAP, ECJ, EpochX y WatchMaker. En ANSI-C++, cabe destacar EO.
JGAP, Java Genetic Algorithms Package (http://jgap.sourceforge.net/), tiene todo lo
necesario para implementar un algoritmo genético. Se diseñó para que fuese muy intuitivo,
altamente modular y configurable, dando más facilidad a la creación de nuevos y
personalizados operadores genéticos.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 44
28. Diagrama de Clases de JGAP
Se puede observar a simple vista que vienen implementados casi todos los
operadores y tipos de genes más comunes. Este framework, ejecuta dichos operadores en
cada ejecución, devolviendo el individuo con mayor fitness. (Lopez)
ECJ, Evolutionary Computation Toolkit (https://cs.gmu.edu/~eclab/projects/ecj/), es un
sistema de investigación gratuito para el cálculo evolutivo, escrito en Java. Este framework
soporta una gran variedad de técnicas, como los algoritmos genéticos, programación
genética, estrategias de evolución, coevolución, optimización de enjambres de partículas y
evolución diferencial, etc.
El framework imita las iteraciones de estos procesos evolutivos usando una serie
pipelines de manera que se puedan conectar una o más subpoblaciones de individuos con
operadores como el de selección, cruce y mutación. Este framework es de código abierto y se
distribuye bajo la Academic Free License.
Sus características principales son:
Implementación del modelo de islas asíncrono a través del protocolo
TCP/IP.
Evaluación del tipo maestro/esclavo sobre múltiples procesadores.
Implementación de algoritmos genéticos estacionarios o
generacionales, con o sin elitismo.
Arquitectura de reproducción muy sensible.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 45
Gran variedad de operadores de selección.
Múltiples subpoblaciones y especies.
Intercambios entre subpoblaciones.
Lectura de poblaciones desde archivo.
Coevolución de una o varias poblaciones.
Optimización Multiobjetivo SPEA2.
Optimización de un enjambre de partículas.
Implementación de evolución diferencial.
Implementación de algoritmos evolutivos incorporados espacialmente.
Fuente: (Wikipedia)
EpochX (http://www.epochx.org/) es un framework de programación genética y de
código abierto. Está diseñado sobre todo para analizar la programación evolutiva de manera
automática, por lo que este framework es muy utilizado por investigadores que necesitan un
sistema ampliable para poder estudiar los efectos de los nuevos operadores o métodos.
Sus características principales son:
Soporte completo para 3 representaciones populares:
o STGP (Strongly-Typed tree Genetic Programming)
o CFG-GP (Context-Free Grammar Genetic Programming)
o GE (Grammatical Evolution)
Configuración dinámica que permite que podamos modificar algo en
mitad de su ejecución.
Modelos integrados para muchos problemas comunes (multiplexores,
hormigas, regresión simbólica, etc.).
Una gran selección de operadores.
Posibilidad de implementar de manera muy simple nuevos operadores
de cruce y/o mutación.
Acceso a estadísticas en tiempo real sobre cómo se está desarrollando
una ejecución.
Una amplia documentación, incluyendo manuales y JavaDoc completos.
Fuente: (Epochx)
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 46
WatchMaker (https://watchmaker.uncommons.org/) es un framework extensible, de
alto rendimiento, orientado a objetos para implementar algoritmos evolutivos/genéticos
independientemente de la plataforma que se utilice, escrito en Java. Este framework
proporciona evolución de tipo seguro para tipos arbitrarios a través de una API no invasiva. Es
de código abierto, y está sujeto a los términos de Apache Software License, Versión 2.0.
Sus características principales son:
Motor de evolución multihilo – Utiliza el paralelismo para improvisar
ejecuciones multi-núcleo y maquinas con varios procesadores.
No invasivo – Los objetos pueden evolucionar sin que la clase evolutiva tenga
que implementar una interfaz en concreto o heredar de una clase base común.
Esto significa que no hay restricciones en la implementación del tipo evolutivo y
no hay dependencia en ninguna clase del framework. El tipo de evolución está
totalmente desacoplado.
Estrategias de selección conectables – Selección por ruleta, selección por
torneo, etc. . También nos permite crear nuestro propio método de selección.
Esquemas flexibles de evolución – Se puede realizar todo en un solo paso o
tener varios operadores combinados secuencialmente y/o con ramificación.
Podemos utilizar los operadores ya incluidos en el framework, crear los
nuestros propios o incluso combinarlos.
Operadores reutilizables – Se proporcionan implementaciones de operadores
de cruce y mutación para ser usados con distintos tipos de datos, ya sean
cadenas de bits, cadenas de caracteres matrices o listas.
Evolución mediante el modelo de islas – Permite desarrollar múltiples
poblaciones en paralelo con la migración periódica de individuos entre islas.
Evolución en estado estacionario – Desarrolla un miembro de la población a
la vez.
Estrategias de evolución – Apoyo para estrategias de evolución del tipo (μ +
λ) y (μ, λ).
Algoritmos evolutivos interactivos – El apoyo a la selección guiada por el
usuario hace que el framework sea adecuado para aplicaciones en las que sea
difícil definir una función adecuada de fitness, como el arte evolutivo y la
música evolutiva.
Procesos Distribuidos – Soporte para evaluaciones de fitness distribuidas
utilizando Hadoop o Terracotte.
Librería de componentes reutilizables– Componentes reutilizables para
simplificar la creación de interfaces gráficas de usuario para programas
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 47
evolutivos. Incluye un componente genérico Evolution Monitor, que proporciona
información sobre un programa evolutivo en ejecución. (WatchMaker)
EO (eodev.sourceforge.net) es una biblioteca evolutiva, escrita en ANSI-C++ basada
en plantillas, que ayuda al usuario a escribir sus propios algoritmos de optimización
estocástica de forma insana.
Con la ayuda de EO, se pueden diseñar fácilmente algoritmos evolutivos que
encontraran soluciones a prácticamente todo tipo de problemas de optimización duros, desde
los continuos hasta los combinatorios.
Ilustración 29. EO
Sus características principales son:
Diseño flexible que permite crear prácticamente cualquier algoritmo.
Representación de soluciones para problemas continuos y combinatorios.
Varios algoritmos paradigmáticos: Estrategias de evolución, algoritmos
genéticos, estimación de distribución, optimización de enjambre de partículas,
etc.
Varios operadores de selección y reemplazo: Basados en rango,
deterministas o estocásticos, ruleta, elitismo, etc.
Operadores de variaciones: Inicializador uniforme, mutación gaussiana,
Subtree de cruce, etc.
Fácil combinación de varios operadores: combinación proporcional, llamada
secuencial, etc.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 48
Herramientas de paralelización: Desbloqueo de bucles de memoria
compartida, parámetros de paso de mensajes, archivos de parámetros legibles
y portátiles, cargar población desde archivo, etc.
Posicionamiento y registro versátiles: Volcado de archivos, medidas
estadísticas, señales de captura, etc. (EO)
4.3. jsEO
4.3.1. ¿Qué es jsEO?
jsEO es una biblioteca para computación evolutiva que se ejecuta en cualquier
navegador web, debido a que está escrita en el lenguaje de programación JavaScript. Esta
biblioteca permite el desarrollo rápido de algoritmos evolutivos, ya que es modular, flexible,
orientada a objetos y facilita la colaboración entre todos los clientes a través del
almacenamiento y difusión de individuos en un servidor web.
Esta biblioteca hace uso de la herencia mediante los mecanismos proporcionados por
MooTools. Partiendo del concepto de objeto evolutivo, utilizado previamente en librerías como
EO, jsEO se basa en la idea de que cualquier objeto al que se pueda asignar algún tipo de
valor de fitness se convierte en un candidato potencial para realizar evolución.
Puede descargarse desde github.com/vrivas/jsEO (versión original) o
github/Lividjsk/jsEO (versión actual), y está desarrollada bajo la licencia GNU GPLv2.
4.3.2. Ventajas e Inconvenientes
Las principales ventajas de esta librería son:
Al estar escrita en JavaScript, cuya máquina virtual está incluida en casi todos
los navegadores web, permite que pueda ser ejecutada en miles de millones
de ordenadores y/o cualquier dispositivo (smartphone, tablet pc, etc.) que
soporte dicha tecnología.
Al ser lenguaje interpretado por un navegador, no necesita ser instalado para
ser ejecutado.
Los principales inconvenientes que presenta son:
Al utilizar JavaScript, este lenguaje es interpretado y no compilado, esto
significa que su ejecución puede ser más lenta de lo deseado.
No tiene acceso a los recursos del dispositivo, entiéndase por esto, su
sistema de archivos, memoria o dispositivos de E/S.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 49
Su ejecución puede ser detenida en cualquier momento si esta consume
muchos recursos. 1
4.3.3. Estructura
La estructura de esta biblioteca se basa en el siguiente diagrama de clases.
30. Diagrama de clases de jsEO
Como podemos observar, hay una clase principal que es abstracta llamada jsEO, que
es la que representa a cualquier objeto al que se le puede asignar un valor de fitness, lo que
indica también que este pueda ser comparado con cualquier otro objeto.
A partir de esta clase, existe otra clase también abstracta, jsEOIndividual, la cual
hereda de jsEO, que representa a los individuos de la población, y permite que estos puedan
ser evaluados mediante una función de evaluación.
1 A modo de observación, decir que la mayoría de los navegadores web tienen un límite para la
ejecución de scripts, donde cuando se supere dicho límite emitirá una advertencia para indicar que se detendrá la ejecución de dicho script.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 50
jsEOPopulation es otra clase que está compuesta por jsEOIndividual, es decir, esta
clase representa la población que va a ser evaluada y que está compuesta por individuos.
Esta clase se utiliza para añadir, eliminar o reemplazar individuos de una población, los
cuales pueden estar ordenados en función de su valor de fitness, o incluso, se puede obtener
un subconjunto de individuos a partir de dicha población.
A la hora de construir lo que viene a ser el algoritmo en sí, tenemos que tener bien
definidos nuestros operadores. Todos los operadores heredan de la clase abstracta
jsEOOperator, a la cual se le pasa como parámetro una población inicial, le aplica dicho
operador y devuelve una población, que ya puede ser la misma o distinta, dependiendo de los
parámetros que hayamos considerado en nuestro operador. Estos operadores pueden
trabajar sobre un único individuo, o evaluando la población entera.
Dentro de esta clase destacan dos operadores, jsEOGetIndividual y
jsEOSendIndividual. Estos operadores se utilizan en el modelo de islas, el cual permite la
interacción de varias ejecuciones del mismo algoritmo en distintas máquinas para poder
intercambiar los mejores individuos de cada una de sus poblaciones.
Así pues, jsEOGetIndividual manda una petición al servidor el cual le responde
enviándole el mejor individuo que hay dentro del servidor de entre todos los que se han
enviado, es decir, un máximo global. Mientras que jsEOSendIndividual, lo que hace es enviar
el mejor individuo de una población obtenido por un dispositivo concreto con respecto a su
población, es decir, envía un máximo local.
También tenemos la clase abstracta jsEOAlgorithm, la cual es una agregación formada
por una población y varios operadores. Esto quiere decir, que esta clase trabaja sobre una
población, aplicándole los operadores que se le proporcionan.
La clase jsEOUtils, contiene datos para facilitar la depuración, almacenar y mostrar la
información importante, así como otros métodos de utilidad variada. La clase
jsEOOperatorsWheel es una clase que se encarga de almacenar todos aquellos operadores
de cruce, mutación y/o cualquier otro que creemos. Esta última clase facilita el uso de los
operadores dentro del algoritmo.
Por ultimo tenemos la clase jsEOGA, que implementa un Algoritmo Genético Estándar.
Este algoritmo usa la selección por torneo para crear varias subpoblaciones sobre las cuales
se aplican los operadores de cruce y mutación y así evolucionar dicha población. Esta clase
también hereda de la clase abstracta jsEOAlgorithm.
4.3.4. ¿Cómo funciona?
Una vez abordado nuestro problema y desarrollado nuestro algoritmo, solo nos queda
enlazarlo todo. De manera que habrá una clase que contendrá nuestro algoritmo la cual
heredara de jsEOAlgorithm, esta clase tiene varios métodos:
Initialize - Sirve para inicializar dicha clase con unos parámetros de entrada.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 51
Run - Este método es el principal de la clase. En él se crean los individuos de la
población, se ordenan dentro de esta, se añaden los operadores que se van a
utilizar y por último se llama a otro método, privaterun.
PrivateRun – Este método es el que lleva a cabo el proceso generacional del
algoritmo evolutivo. Se le pasa como parámetros el número de generaciones, la
función de evaluación que se va a utilizar y el número de individuos que se van a
mostrar una vez finalizado este. Este método se desarrolla en la clase jsEOGA, de
la cual hereda nuestro algoritmo.
El procedimiento es simple, pero para poder utilizar nuestro algoritmo debemos
desarrollar el problema en un fichero JavaScript, el cual debe incluir todos los atributos
necesarios para su ejecución, así como la función de evaluación que vamos a utilizar.
Una vez tenemos todo esto implementado, debemos crear un fichero HTML, en el cual
vamos a incluir todos los scripts necesarios, ya sean de JavaScript, MooTools o los de
nuestra librería, porque así es como podremos comprobar que nuestro algoritmo funciona
perfectamente.
4.3.5. Problemas ya desarrollados en jsEO
Antes de explicar con detalle los problemas planteados y resueltos, voy a explicar los
problemas que ya había desarrollados en jsEO previamente.
Royal Road 253
En primer lugar tenemos el problema de Royal Road 256. Este utiliza cadenas de 256
bits para codificar las soluciones. Dicha solución será mejor cuantas más combinaciones de
“0000” y/o “1111” se encuentren.
Estas soluciones se crean aleatoriamente por lo que el número de unos y ceros ira
variando en función de los operadores que se le apliquen a estas.
Para ello hace uso de las Royal Road Functions. Estas funciones fueron desarrolladas
por Melanie Mitchell y Stephanie Forrest en 1993. (Whitley, 1993)
Este problema utiliza un operador de selección por torneo, un operador de cruce
multipunto y un operador de mutación de bit.
Ecuación de 128 términos
En segundo lugar tenemos el problema de la ecuación de 128 términos. Este
problema nos da una ecuación con 128 términos, para los cuales debemos encontrar un valor
adecuado que nos permita resolver dicha ecuación.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 52
Para ello, se utilizan unos coeficientes para cada término. Estos, coeficientes se le
suman a cada termino correspondiente de la solución que nos da nuestro algoritmo para
poder así comprobar si la ecuación está resuelta.
En este problema, se vuelven a utilizar los mismos operadores que en el anterior,
excepto el de mutación, que en este caso se utiliza un operador de mutación de gen.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 53
5. Resolución del trabajo
5.1. Análisis, diseño e implementación
5.1.1. Análisis
5.1.1.1. Servidor
Para que los usuarios pudiesen intercambiar información entre ellos, necesitábamos
de la creación de un servidor junto con el uso de alguna base de datos para almacenar dicha
información.
Este nos permite que, al ejecutar todos los usuarios lo mismo pero con la diferencia de
que cada uno pueda obtener soluciones distintas al mismo problema, se reciba en un servidor
la solución de cada usuario, de manera que de todas las que se reciben solo almacenamos la
mejor de todas, para que así, cuando un usuario solicite dicha solución al servidor, este le
mande la mejor encontrada hasta el momento y de esta forma, poder tener la posibilidad de
mejorar más aun la solución obtenida en el dispositivo de cada cliente.
Ilustración 31. Modelo Cliente-Servidor
Para ello hemos diseñado un diagrama de casos de uso. Mediante la utilización de estos, podemos observar cuales son las diferentes situaciones en las que nuestro servidor es requerido y que funciones o necesidades debe cumplir este en nuestra aplicación.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 54
Ilustración 32. Diagrama de casos de uso
Cada caso de uso tiene su funcionalidad propia, la cual voy a pasar a explicar para un mejor entendimiento.
Identificador CU01 Nombre Solicita Individuo
Descripción El usuario ejecuta el algoritmo para resolver un problema y este utiliza el operador Get para solicitar el mejor individuo al servidor
Actor Principal Usuario
Actores Secundarios Servidor
Relaciones Recibir el mejor individuo obtenido por cualquier usuario al ejecutar el algoritmo y haber sido almacenado en la base de datos de nuestro servidor
Precondiciones Debe de estar el servidor iniciado, así como tener un individuo del tipo solicitado almacenado
Condición final con éxito El usuario recibe el individuo solicitado y lo incorpora a la población de su algoritmo
Condición final con fracaso El usuario no recibe el individuo solicitado y por tanto retorna la misma población que tenía antes de hacer la petición
Secuencia Normal Acción
1 El usuario ejecuta un problema e inicia el algoritmo
2 El algoritmo hace uso del operador Get y hace la petición al servidor
3 El servidor recibe la petición y busca en la base de datos el individuo solicitado
4 El servidor envía el individuo solicitado
5 El usuario recibe el individuo y lo incorpora a su población
Frecuencia Aleatoria
Importancia Necesario
Prioridad Corto plazo
Comentarios El servidor envía el individuo en formato JSON
Tabla 3. Especificación CU Solicita Individuo
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 55
Este proceso se mantiene fijo ya que es necesario para poder obtener al mejor
individuo de todas las ejecuciones realizadas por distintos usuarios. Para un correcto
funcionamiento de este, el individuo también debe mandar los datos de la petición en formato
JSON.
Identificador CU02
Nombre Envía Individuo
Descripción El usuario ejecuta el algoritmo para resolver un problema y durante cada generación de este algoritmo, el usuario envía el mejor individuo obtenido al servidor
Actor Principal Usuario Actores Secundarios Servidor
Relaciones Enviar el mejor individuo obtenido por cualquier usuario al ejecutar el algoritmo para su almacenamiento en la base de datos del servidor
Precondiciones Debe de estar el servidor iniciado, así como el individuo enviado ser mejor que el que actualmente se encuentra en la base de datos
Condición final con éxito El servidor recibe el individuo y lo almacena en la base de datos
Condición final con fracaso El servidor no almacena el individuo en la base de datos
Secuencia Normal Acción
1 El usuario ejecuta un problema e inicia el algoritmo
2 El algoritmo hace uso del operador Send y manda la petición al servidor
3 El servidor recibe la petición y busca en la base de datos el individuo solicitado
4 El servidor comprueba que el individuo recibido es mejor que el que hay en la base de datos y lo almacena o no.
5 El servidor envía el resultado de la consulta al usuario
Frecuencia Aleatoria
Importancia Necesario
Prioridad Corto plazo Comentarios El usuario debe enviar el individuo en
formato JSON Tabla 4. Especificación CU Envía Individuo
Este proceso también se mantiene fijo. El servidor recupera los datos del individuo
mandados en la consulta del usuario y comprueba que este sea mejor que el que tiene
almacenado. De ser así, actualiza la base de datos y envía una confirmación al usuario. En
caso contrario, mantiene el individuo de la base de datos y envía un mensaje al usuario
indicándole que el individuo no ha sido insertado en la base de datos.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 56
5.1.1.2. Cliente
Para un mejor entendimiento de cómo funciona jsEO, procederé a explicar primero las
partes comunes de cada problema desarrollado en este TFG para después comentar cada
problema de manera más específica.
Todos los problemas desarrollados deben poder resolverse mediante el uso de los
algoritmos implementados en esta biblioteca, por lo que debemos estudiar cual es el
problema que queremos resolver, de qué manera vamos a codificar las soluciones, que
operadores vamos a utilizar, etc.
En este TFG, se han desarrollado 3 problemas:
TSP o Problema del Viajante de Comercio, el problema de las N-Reinas o el problema
del TSP haciendo uso de un algoritmo multi-objetivo.
5.1.1.2.1. TSP(Travelling Salesman Problem)
El TSP o el problema del Viajante de Comercio en español, trata de que, dada una
lista de ciudades y las distancias que hay entre cada una de ellas, encontrar la el camino más
corto posible, el cual recorra todas y cada una de ellas, pasando una única vez, y regresando
a la ciudad de origen. Este problema es un NP-completo, ya que el número de caminos
posibles es de N!, donde N es el número de ciudades.
Ilustración 33. TSP simétrico
5.1.1.2.2. Problema de las N-Reinas
Este problema trata de colocar N reinas sobre un tablero de ajedrez de tamaño NxN,
sin que ninguna de estas se “coman” entre sí. Una reina se come a otra, siempre y cuando
ambas estén en la misma fila, columna o diagonal.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 57
Ilustración 34. Problema de las N-Reinas con 5 reinas
En la ilustración 34, podemos observar cómo sería una solución óptima al problema de
las N-Reinas. Para poder explicar esto mejor, los movimientos posibles de una reina serían
los siguientes:
Ilustración 35. Posibles movimientos de una reina sobre un tablero de ajedrez
Las casillas marcadas con una X verde, significan todas aquellas a las que nos
podemos mover con nuestra reina desde la posición en la que se encuentra en el tablero.
Estas, serían las posiciones que las demás reinas no podrían tener ya que se “amenazarían”
entre sí.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 58
5.1.1.2.3. TSP Multi-Objetivo
Este problema, sigue las mismas pautas que el explicado en el apartado 5.1.1.2.1. con
la diferencia, de que ahora no solo se buscar encontrar el camino más corto para recorrer
todas las ciudades, sino que también se ha de hacer en el menor tiempo posible. Para ello, el
problema nos dota no solo de las distancias que haya entre cada una, sino también del
tiempo que se tarda en recorrer dicha distancia de una ciudad a otra.
5.1.2. Diseño
5.1.2.1 Servidor
Para crear nuestro servidor hemos instalado Node y MongoDB, cuyo proceso de
instalación se puede ver en el Anexo I y II, respectivamente. Con Node hemos utilizado varios
módulos para un correcto funcionamiento con nuestra aplicación.
Entre estos módulos se encuentran:
Express
Body-parser
Mongoose
Mongoose-sequence
Node-stringify
El modulo express, nos proporciona una infraestructura de aplicaciones web en
Node.js mínima y flexible que suministra un conjunto de características para las aplicaciones
web y móviles. Este también nos permite crear una API sólida, de manera rápida y fácil.
Además tiene una delgada capa de características de aplicación web básicas, que no
ocultan ninguna de las características de Node.js (Express)
El módulo body-parser analiza el contenido del parámetro que se le pasa antes de que
este sea manipulado por cualquier otro método. El contenido del parámetro está en la
propiedad body de nuestra consulta, es decir, si nuestra consulta se llamase request, para
acceder al contenido de esta deberíamos utilizar request.body.
Este solo es capaz de manejar el contenido de una consulta, para ello habría que
instalar otros módulos como pueden ser multiparty y connect-multiparty.
Este módulo, es capaz de analizar el contenido de una consulta en los siguientes
formatos:
JSON
Datos sin procesar, o sin formato
Texto plano
Contenido de formularios codificados por URL
El módulo mongoose es un módulo para Node.js el cual trabaja con bases de datos de
MongoDB. Este, nos permite conectarnos a nuestra base de datos de MongoDB, e interactuar
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 59
con esta, ya sea para insertar, actualizar o borrar documentos de cualquiera de las
colecciones que tengamos en esta. También nos permite crear diferentes Schemas, que nos
indican el formato en el que se guardaran nuestros datos en cada colección. Para utilizar
estos Schemas, crearemos un modelo al cual le pasaremos el Schema creado.
Para poder almacenar todas las soluciones recibidas en el servidor, hemos creado un
Schema para cada problema, tanto para los que había previamente en jsEO antes de la
realización de este trabajo como para los que se han desarrollado a lo largo de este.
Entre estos Schemas, hay varios que comparten la misma estructura, con 2
diferencias. La primera, que cada uno está asignado a un modelo y a una colección distinta y
la segunda, la codificación utilizada para la solución de cada problema. También hay otro
Schema distinto, el cual se ha desarrollado para el caso del algoritmo multi-objetivo.
La estructura de los Schemas para resolver con nuestro algoritmo genético, seria:
𝑣𝑎𝑟 𝑠𝑐ℎ𝑒𝑚𝑎 = 𝑛𝑒𝑤 𝑆𝑐ℎ𝑒𝑚𝑎(
_𝑖𝑑: {𝑡𝑦𝑝𝑒: 𝑁𝑢𝑚𝑏𝑒𝑟},
𝑆𝑜𝑙𝑢𝑡𝑖𝑜𝑛: {𝑡𝑦𝑝𝑒: 𝐴𝑟𝑟𝑎𝑦 𝑜𝑟 𝑆𝑡𝑟𝑖𝑛𝑔},
𝐹𝑖𝑡𝑛𝑒𝑠𝑠: {𝑡𝑦𝑝𝑒: 𝑁𝑢𝑚𝑏𝑒𝑟}
}, {𝑐𝑜𝑙𝑙𝑒𝑐𝑡𝑖𝑜𝑛: 𝐶𝑜𝑙𝑙𝑒𝑐𝑡𝑖𝑜𝑛});
La variable Solution corresponde a la codificación de la solución. En el ejemplo
anterior hemos puesto Array or String, ya que en el problema de Royal Road 256 (apartado
4.3.5) la solución se codifica como String, mientras que en el resto de problemas se ha
utilizado un Array de enteros, floats o permutaciones.
El Schema del problema multi-objetivo sería:
𝑣𝑎𝑟 𝑠𝑐ℎ𝑒𝑚𝑎 = 𝑛𝑒𝑤 𝑆𝑐ℎ𝑒𝑚𝑎(
_𝑖𝑑: {𝑡𝑦𝑝𝑒: 𝑁𝑢𝑚𝑏𝑒𝑟},
𝑆𝑜𝑙𝑢𝑡𝑖𝑜𝑛: {𝑡𝑦𝑝𝑒: 𝐴𝑟𝑟𝑎𝑦},
𝑂𝑏𝑗𝑒𝑐𝑡𝑖𝑣𝑒𝑠: {𝑡𝑦𝑝𝑒: 𝐴𝑟𝑟𝑎𝑦}
}, {𝑐𝑜𝑙𝑙𝑒𝑐𝑡𝑖𝑜𝑛: 𝐶𝑜𝑙𝑙𝑒𝑐𝑡𝑖𝑜𝑛});
En este caso la variable Fitness se ha sustituido por otra llamada Objectives, la cual es
un array el cual contendrá todos los valores de los objetivos a satisfacer por nuestro
algoritmo.
Para utilizar estos modelos, se ha desarrollado un controlador, que administra las
peticiones que se realizan al servidor, y en función de la consulta, este llama a un modelo u a
otro.
Mongoose-sequence es un módulo para Node y utilizado por mongoose como si se
tratase de un plugin de este, el cual nos proporciona un autoincremento para el campo que
nosotros indiquemos. En caso de no indicar ninguno y de querer modificar el campo _id, que
es el que utiliza por defecto MongoDB para asignar un identificador al objeto, se le asignaría
el valor a este. También podemos indicarle de que forma queremos que haga el
autoincremento. Si no se indica cómo, lo hace de 1 en 1. Para poder utilizar este plugin, es
necesario tener instalado el módulo mongoose en su versión 4.0.0. o superior.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 60
El módulo node-stringify es un módulo de Node el cual nos permite pasar cualquier
valor a un objeto en formato JSON.
En cuanto al diseño de la interfaz, se ha diseñado está haciendo uso del framework
Bootstrap, que se describió en el apartado 1.5., la cual nos permite dotar a nuestra interfaz de
un diseño responsive o adaptativo. El resultado de nuestra interfaz principal, sería el
siguiente:
Ilustración 36. Interfaz para jsEO (1)
Ilustración 37. Interfaz para jsEO (2)
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 61
5.1.2.2. Cliente
Para el diseño de los problemas a desarrollar, podemos observar el diagrama de
clases de jsEO en el apartado 4.3.3., aun así, se explicara el diagrama de clases de cada
problema desarrollado, así como las medidas adoptadas para la resolución de cada uno.
Todos los problemas desarrollados se crean inicialmente con una población aleatoria
para aportar así más diversidad a la hora de encontrar soluciones permitiendo así esquivar
una convergencia prematura hacia alguna solución óptima.
5.1.2.2.1. TSP(Travelling Salesman Problem)
Para resolver el problema del Viajante de Comercio, tuvimos varias consideraciones
en cuenta. Una de ellas fue la codificación de la solución. Para esto se ha utilizado una
representación de orden, la cual viene a ser un Array de enteros, cuyo índice indica el orden
en que se han recorrido las ciudades, y el valor de este que ciudad se ha visitado para dicho
índice.
También se ha tenido en cuenta resolver el problema utilizando una matriz de datos de
tamaño 12x12. Este tamaño viene dado porque el máximo de ciudades utilizado para poder
visualizar el grafo medianamente bien ha sido de 12. Aunque se podría aumentar, cambiando
el número de ciudades y añadiendo más datos a la matriz utilizada. Esta matriz, es también
simétrica, lo que nos ha facilitado resolver el problema más fácilmente. Esto significa que hay
la misma distancia de la ciudad A hacia la ciudad B, que a la inversa, de la ciudad B a la
ciudad A.
El algoritmo utilizado para resolver este problema pertenece a la clase jsEOGA, el
cual estaba previamente desarrollado en esta biblioteca. Este se explicara más adelante en el
apartado 5.1.3.2.
El operador de cruce desarrollado para resolver este problema, ha sido el OX, el cual
se explica cómo funciona en el apartado 2.5., y se detallara más adelante en el apartado
5.1.3.2.1. El operador de mutación desarrollado ha sido el de intercambio, también explicado
en el apartado 2.6., y el cual será detallado en el apartado 5.1.3.2.1.
El operador de selección utilizado, ha sido el de por torneo, que ya estaba previamente
desarrollado. Este se explicó en el apartado 2.4. y también se detallara más adelante en el
apartado 5.1.3.2.
En cuanto a la función de fitness desarrollada, se ha basado en una sumatoria. Esta
sumatoria viene a ser la suma de todas las distancias empezando desde la inicial hasta la
final, para finalmente sumarle la distancia de esta última a la ciudad de origen.
El diagrama de clases de este problema, sería el siguiente:
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 62
Ilustración 38. Diagrama de clases de jsEORepresentationOrder
En cuanto a la interfaz para visualizar la resolución de este problema, sería la
siguiente:
Ilustración 39. Interfaz TSP (1)
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 63
Ilustración 40. Interfaz TSP (2)
5.1.2.2.2. Problema de las N-Reinas
En el problema de las N-Reinas, no nos hacen falta datos de entradas, como en el
problema del TSP, ya que estas soluciones se generan inicialmente de manera aleatoria, al
igual que en todos los problemas desarrollados.
En esta ocasión se ha codificado la solución como un Array de Objetos. Cada posición
de esta array contiene una reina. Estas con codificadas como objetos que son permutaciones
que almacenan la fila y la columna sobre la que se coloca dicha reina.
En esta ocasión, dada la visualización del tablero, se ha tenido en cuenta un máximo
de 8 reinas para resolver el problema.
En cuanto al algoritmo utilizado, al igual que en problema anterior, se ha utilizado
jsEOGA. El operador de cruce desarrollado ha sido el de cruce basado en un punto. Este
operador esta explicado en el apartado 2.5. y se detallara más adelante en el apartado
5.1.3.2.2.
El operador de mutación utilizado ha sido el de intercambio. Con respecto al operador
de selección, ha sido el de selección por torneo.
La función de fitness desarrollada se basa en la estrategia que utiliza este problema.
Se comprueba si hay alguna reina en la misma fila, columna o diagonal que dicha reina. Por
cada reina que se encuentre en dicha situación al valor de fitness se le resta 1. El valor de
fitness inicial viene a ver el número total de reinas – 1, es decir, si se encontrase una solución
en la que todas las reinas se comiesen, su fitness seria 0, mientras que en el caso contrario, y
suponiendo que estas fueran 8, su valor de fitness sería igual a 7.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 64
El diagrama de clases de este problema sería:
Ilustración 41. Diagrama de clases de jsEOPermutation
Y en cuanto a la interfaz de este problema:
Ilustración 42. Interfaz N-Reinas (1)
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 65
Ilustración 43. Interfaz N-Reinas (2)
5.1.2.2.3. TSP Multi-Objetivo
Para este problema se ha utilizado la misma codificación que en el problema del TSP,
explicado en el apartado 5.1.2.2.1.
Los individuos ahora provienen de la clase jsEOMOIndividual, la cual difiere de la que
hemos usado en los anteriores problemas principalmente, en cómo evalúa a los individuos, ya
que esta ahora no solo tiene un valor de fitness, sino varios objetivos. Esta clase, también
contiene nuevas características, las cuales serán explicadas en el apartado 5.1.3.2.3.
Al ser más complejo y necesitar más tiempo de procesamiento debido al algoritmo
utilizado, el NSGA-II, cuyo tiempo de complejidad es 𝑂(𝑀 ∗ 𝑁2), siendo M el número de
objetivos a satisfacer y N el tamaño de la población. Esta complejidad es mejor que la de su
predecesor, el NSGA, el cual tiene una complejidad de 𝑂(𝑀 ∗ 𝑁3).
El algoritmo desarrollado ha sido el jsEOMOGA, el cual se basa en el algoritmo
comentado en el párrafo superior y cuya explicación y funcionamiento se encuentra en el
apartado 3.1. Más adelante se detallara, en el apartado 5.1.3.2.3.
Los operadores de cruce y de mutación, son los mismos que en el caso del TSP. El
operador de cruce OX y el operador de mutación por intercambio.
En cuanto al operador de selección, se ha utilizado una selección por torneo binario.
Este difiere del que ya hemos descrito, en que solo escoge a 2 individuos de la población a
los cuales se les aplicaran nuestros operadores.
En cuanto a la función de fitness, es muy parecía a la del problema del TSP, solo que
a parte de calcular la distancia, calcula también el tiempo. Esto se hace de igual manera tanto
para un objetivo como para otro. La diferencia reside, en que esta función no devuelve un
valor, sino un array con el valor para cada objetivo a satisfacer.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 66
El diagrama de clases de este problema sería el siguiente:
Ilustración 44. Diagrama de clases de jsEOMultiObjective
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 67
La interfaz para este problema sería:
Ilustración 45. Interfaz TSP Multi-objetivo (1)
Ilustración 46. Interfaz TSP Multi-objetivo (2)
5.1.3. Implementación
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 68
5.1.3.1. Servidor
En este apartado voy a explicar cómo se ha desarrollado el servidor y de qué manera
se ha estructurado este para su correcto funcionamiento con la biblioteca.
Para el servidor, se han desarrollado varios ficheros, entre ellos está el principal,
server.js, el cual inicializa todos los módulos necesarios para el funcionamiento de este, así
como de crear la api mediante el modulo Express e inicializa los métodos GET y POST
correspondientes para atender a las peticiones que se le hagan a este. Por último se lanza el
servidor en la dirección 0.0.0.0 y el puerto 8888.
El servidor se inicia en esta dirección, para permitir que los usuarios que estén
conectados a nuestra red puedan acceder al servidor, introduciendo la dirección IP del
dispositivo desde el cual se ha lanzado el servidor, y en el puerto 8888. Es decir, suponiendo
que nuestro dispositivo tuviese la IP 192.168.1.207 y que lanzamos el servidor desde este,
cualquier persona que esté conectada a nuestra red e introduzca en su navegador
192.168.1.207:8888/, podrá acceder a la biblioteca y probar los problemas que se quieren
resolver.
También se crea la conexión con la base de datos de MongoDB, en la dirección
/localhost:27017/jsEO. Esta dirección es de la por defecto a donde se instala nuestra base de
datos en la maquina local. Al crear la conexión con esta, hemos indicado mediante “/jsEO”
cual sería nuestra base de datos, y es aquí donde guardaremos las colecciones que
usaremos para resolver nuestros problemas.
El código del fichero server, sería el siguiente:
Otro fichero también necesario para el funcionamiento del servidor es controllers.js,
este fichero es el controlador que se ha creado para administrar las peticiones que le llegan al
servidor, es decir, ya sea para guardar información que recibimos de un usuario o bien para
enviársela a algún usuario haciendo una consulta en nuestra base de datos.
Este controlador administra también la llamada a la página principal de la biblioteca,
así como de varias funciones que nos sirven para poder administrar nuestra base de datos.
Para guardar la información en la base de datos primero obtenemos la petición del
usuario y accediendo al contenido de la consulta, sabemos a qué problema y que tamaño de
la solución se refiere. Una vez obtenida dicha información, se realiza una consulta a la base
de datos, más en concreto, a la colección. Si se encuentra un individuo en dicha colección
con los datos solicitados, lo recupera y en caso de que el individuo que se ha enviado al
servidor sea mejor que el que tenemos almacenado, este actualiza el ya existente con los
datos provenientes de dicho usuario. En caso contrario, deja el individuo como esta y
devuelve un mensaje de error.
En el caso de que el servidor recibiese una petición para enviar el mejor individuo de la
base de datos, se obtiene de la consulta el contenido de esta y se busca en la base de datos.
En este caso, si encuentra un individuo con los datos solicitados, se le devuelve al usuario. En
caso contrario devuelve un mensaje de error.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 69
Debido a su extensión, el código del controlador se puede consultar en el Anexo III.
Por ultimo tenemos los modelos desarrollados. Para cada problema se ha creado el
Schema apropiado utilizando el módulo mongoose de Node. En total hay 6 modelos, uno para
cada uno de los problemas desarrollados, tanto para este TFG como los que había
previamente. El último modelo, se ha utilizado para guardar los datos de los experimentos
desarrollados.
El código de los modelos, también se puede consultar en el Anexo III.
En cuanto al funcionamiento de la biblioteca, es simple. Una vez accedemos a la
dirección IP del dispositivo que haya lanzado el servidor y en el puerto correspondiente, nos
saldrá la interfaz descrita en el apartado 5.1.2.1. En esta podremos ver todos los problemas
desarrollados, así como la codificación utilizada para cada uno y los operadores. También nos
da la posibilidad de seleccionar los parámetros para nuestro algoritmo. Estos vienen
predefinidos por defecto, para garantizar un correcto funcionamiento del algoritmo.
Una vez elegido el problema que queremos probar y configurado con los parámetros
que queremos para ejecutar el algoritmo, si pinchamos en “Ver detalles”, se nos abrirá una
ventana nueva. Esta ventana cargara un archivo HTML, llamado template.html. Como su
propio nombre en ingles indica, es una plantilla genérica para todos los problemas. Cada
problema cargara un contenido, para observar como se ha resuelto y los resultados
obtenidos.
5.1.3.2. Cliente
En este apartado hablaremos de las diferentes representaciones usadas para resolver
los problemas realizados en este TFG. Estas representacions incluyen, los operadores
implementados, la codificación de los individuos y el algoritmo utilizado. Tambien, para cada
problema se ha creado un archivo JavaScript que contiene los parámetros de entrada que
recoge de la página principal comentada en el apartado anterior, asi como la función para
calcular el fitness y datos de entrada, en el caso del TSP y el TSP Multi-objetivo, y la llamada
a la ejecución del algoritmo.
Antes de explicar cada problema por separado, hablaremos de las partes comunes a
todos estos problemas. Entre estas, se encuentran las clases jsEO, jsEOAlgorithm,
jsEOIndividual, jsEOPopulation, jsEOOperator, jsEOOperatorsWheel, y los operadores
jsEOOpSelectorTournament, jsEOGetIndividualsNode y jsEOSendIndividualsNode.
El archivo jsEOUtils, en si no es una clase, sino una variable que almacena todos los
métodos desarrollados y que nos han sido de utilidad para por ejemplo, calcular el mejor
fitness para los problemas de minimización, la media del fitness de una población, los
resultados de los 5 mejores individuos de una población, la solución greedy para el problema
del TSP, asi como la visualización del grafo para este mismo problema, tanto este como el
multi-objetivo. Tambien contiene métodos para la visualización del tablero de ajedrez para el
problema de las N-Reinas o una grafica para ver como ha variado el fitness con respecto de
una población a otra o en el caso del TSP-Multiobjetivo, una grafica indicando cuantos
individuos se encuentran en el frente de Pareto y como se han repartido el resto entre los
diferentes frentes.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 70
El funcionamiento de las clases anteriormente descritas, junto con jsEOUtils, se puede
encontrar en el apartado 4.3.3. Aun asi, para ver el código de estas clases y los métodos y
variables más importante, se puede consultar todo en el Anexo IV.
5.1.3.2.1. TSP
Para resolver el problema del TSP, creamos una representación descrita en el
apartado 5.1.2.2.1. llamada jsEORepresentationOrder. Esta incluye las siguientes clases:
jsEOROGA
jsEOROIndividual
jsEOROOpCrossOver
jsEOROOpMutation
A continuación, explicare las funciones más importantes de cada fichero.
Clase jsEOROGA:
run: function(_fitFn) {
// Program
if (this.configure) {
this.doConfigure();
return;
}
this.population = new jsEOPopulation();
for (var i = 0; i < this.popSize; ++i) {
var myRO = new jsEOROIndividual();
myRO.randomize(this.indSize, this.minValue, this.maxValue).
evaluate(_fitFn);
this.population.addIndividual(myRO);
}
this.population.sort();
this.indivSelector = new
jsEOOpSelectorTournament(this.tournamentSize,
Math.floor(this.popSize * this.replaceRate));
this.operSelector = new jsEOOperatorsWheel();
this.operSelector.
addOperator(new jsEOROOpCrossOver(this.xOverRate));
this.operSelector.
addOperator(new jsEOROOpMutation(this.mutRate,
this.mutPower,
this.minValue,
this.maxValue));
if( this.opGet ) {
this.operSelector.addOperator(this.opGet);
}
jsEOUtils.drawGreedySolution("Solución Greedy", "greedy");
jsEOUtils.showLegend("legend");
jsEOUtils.showPopTSP(this.population, "Población Inicial",
this.showing, "ipop");
jsEOUtils.println("Fitness Medio: " +
jsEOUtils.averageFitness(this.population), "ipop");
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 71
var fitnessInit = jsEOUtils.bestFitnessMin(this.population);
jsEOUtils.println("Mejor Fitness: " + fitnessInit, "ipop");
var popInit = [];
for(var a = 0; a < this.showing; ++a){
popInit.push(this.population.getAt(a).getFitness());
}
var startTime = new Date();
this.privateRun(_fitFn, this.showing, this.numGenerations );
var endTime = new Date();
var fitnessFin = jsEOUtils.bestFitnessMin(this.population);
jsEOUtils.showTime((endTime-startTime), "time");
jsEOUtils.showDescription("TSP", "title");
jsEOUtils.showPopTSP(this.population, "Población Final",
this.showing, "fpop");
jsEOUtils.println("Fitness Medio: " +
jsEOUtils.averageFitness(this.population), "fpop");
jsEOUtils.println("Mejor fitness: " + fitnessFin, "fpop");
var popFin = [];
for(var a = 0; a < this.showing; ++a){
popFin.push(this.population.getAt(a).getFitness());
}
jsEOUtils.drawFitness(popInit, popFin);
jsEOUtils.drawGraphTSP(this.population.getAt(0), this.positionsTSP);
}
Este es el método principal de nuestro algoritmo. Este crea una población inicial
this.population, a la que le va añadiendo individuos jsEOROIndividual, creándolos
aleatoriamente mediante el método randomize. Luego ordena la población por fitness
siguiendoel criterio de minimización, mediante la función sort. Añade un operador de
selección jsEOOpSelectorTournament. Crea el manejador de operadores
jsEOOperatorWheels y añade los operadores de cruce (jsEOROOpCrossOver), mutacion
(jsEOROOpMutation) y el operador Get (jsEOGetIndividualsNode), el cual se utilizara para el
modelo de islas. El operador Send (jsEOSendIndividualsNode) se ejecuta siempre en todas
las generaciones. Por consiguiente dibuja la solución Greedy mediante
jsEOUtils.drawGreedySolution, muestra la población inicial mediante el método showPopTSP
y llama al método this.privateRun el cual ejecuta el algoritmo genético.
Una vez termina la ejecución de este, vuelve a llamar a showPopTSP para mostrar la
población final. Por ultimo llama a los métodos jsEOUtils.drawFitness el cual dibujara la
grafica comparativa de los fitness de la población inicial y final y a drawGraphTSP, el cual nos
dibujara el grafo obtenido a partir de la mejor solución de la población final.
La estructura de esta clase es la misma que la de jsEOPGA y jsEOMOTSPGA, solo
guardan algunas difencias significativas que serán explicadas en los apartados 5.1.3.2.2. y
5.1.3.2.3.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 72
Clase jsEOROIndividual:
randomize: function(_length, _min, _max) {
if (typeof _length == 'undefined') {
_length = 10;
}
if (typeof _min == 'undefined') {
_min = 0;
}
if (typeof _max == 'undefined') {
_max = 1;
}
var used = new Array(_length);
var chr = new Array(_length);
for ( var i = 0; i < _length; ++i){
used[i] = false;
}
var index = 0;
chr[0] = 0;
used[0] = true;
for ( var j = 1; j < _length; ++j){
do{
index = jsEOUtils.intRandom(1, _length-1);
}while(used[index]);
chr[j] = index;
used[index] = true;
}
this.setChromosome(chr);
return this;
}
El método randomize de jsEOROIndividual lo que hace es crear un individuo de
manera aleatoria. Para ello, siempre toma la posición inicial con un valor 0, ya que en la
teoría, esta seria la ciudad de origen de nuestras soluciones. Sabiendo que la ciudad inicial es
0, el resto de ciudades en función del número de ciudades que hayamos seleccionado, las
repartirá de manera aleatoria en el array. Cuando crea al individuo, lo devuelve.
Clase jsEOROOpCrossOver:
operate: function (_auxPop) {
jsEOUtils.debugln("Applying jsEOROOpCrossOver");
var toRet = new jsEOPopulation();
//If the population type is not defined, a new population is
returned
if (typeof _auxPop == 'undefined') {
return toRet;
}
if (_auxPop.length() <= 0) {
toRet.add(_auxPop.getAt(0).copy());
return toRet;
}
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 73
//Basic CrossOver
var rnd2 = jsEOUtils.intRandom(1, _auxPop.length() - 1);
var tmpChr1 = _auxPop.getAt(0).getChromosome().slice();
var tmpChr2 = _auxPop.getAt(rnd2).getChromosome().slice();
var point1 = Math.floor(tmpChr1.length / 2);
//The cut-off point is chosen, which will go from position 2 to that
point,
//which will always be half the size
var newChr = [];
var auxChr = [];
var orderChr = tmpChr2.slice();
//The subsequence of the first parent is copied to an auxiliary
individual
for (var i = 1; i <= point1; ++i) {
auxChr.push(tmpChr1[i]);
}
//Once the subsequence is copied, the elements that have been copied
//and copied in the corresponding orderand more similar to that of
the second parent
for (var i = 0; i < auxChr.length; ++i) {
var index = orderChr.indexOf(auxChr[i]);
if (index != -1) {
orderChr.splice(index, 1);
}
}
orderChr.splice(0, 1);
newChr.push(0);
for (var j = 0; j < auxChr.length; ++j) {
newChr.push(auxChr[j]);
}
for (var n = 0; n < orderChr.length; ++n) {
newChr.push(orderChr[n]);
}
jsEOUtils.debugln(" Inicio es " + tmpChr1 + " Final " + newChr);
toRet.add(new jsEOROIndividual());
toRet.getAt(0).setChromosome(newChr);
return toRet;
}
El funcionamiento de este operador consiste en lo siguiente. Primero, escoge los 2
primeros individuos de la población que se le pasa (esta proviene del operador de selección).
Despues elige un punto aleatorio con respecto al tamaño de la solución. Desde la posición 1
del array hasta la posición del punto elegido, copia la parte del padre numero 1. Despues
copia en otro array, todas las “ciudades” que no estén en la solución creada anteriormente a
partir del primer padre intentando mantener lo mejor posible el orden en el que se
encontraban en el segundo padre. Entonces, crea una solución nueva a la que le añade la
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 74
parte correspondiente del primer padre hasta el punto seleccionado y a partir de ahí copia al
segundo en el orden que hemos obtenido. Por ultimo, crea una nueva población, toRet, y
añade el individuo creado y devuelve dicha población.
Clase jsEOROOpMutation:
operate: function (_auxPop) {
jsEOUtils.debugln("Applying jsEOROOpMutation");
//Exchange Mutation
var toRet = new jsEOPopulation();
var gen_mutation = 0;
var gen_mutation_2 = 0;
var value = 0;
var tmpChr = _auxPop.getAt(0).getChromosome();
var newChr = [];
for (var i = 0; i < tmpChr.length; ++i) {
newChr.push(tmpChr[i]);
}
if(Math.random() < this.genesRate){
gen_mutation = jsEOUtils.intRandom(1, tmpChr.length - 1);
gen_mutation_2 = jsEOUtils.intRandom(1, tmpChr.length - 1);
if (gen_mutation === gen_mutation_2) {
do {
gen_mutation_2 = jsEOUtils.intRandom(1, tmpChr.length -
1);
} while (gen_mutation === gen_mutation_2);
}
jsEOUtils.debugln(" Individual is " + newChr);
value = newChr[gen_mutation];
newChr[gen_mutation] = newChr[gen_mutation_2];
newChr[gen_mutation_2] = value;
jsEOUtils.debugln(" Final " + newChr);
}
toRet.add(new jsEOROIndividual());
toRet.getAt(0).setChromosome(newChr);
return toRet;
}
El operador de mutacion, escoge a al primer individuo de la población que se le pasa
como argumento. Se obtiene un numero aleatorio y se compara con el porcentaje de
mutacion, si este valor es menor, se realiza la mutacion, en caso contrario, se devuelve el
individuo como tal. En caso de realizarse la mutacion, se escogen 2 posiciónes aleatorias a
mutar mediante jsEOUtils.intRandom, y se intercambian sus posiciones. Una vez modificado
el individuo se crea una nueva población y se devuelve con el individuo mutado.
5.1.3.2.2. Problema de las N-Reinas
Para resolver el problema de las N-Reinas, creamos una representación descrita en el
apartado 5.1.2.2.1. llamada jsEOPermutation. Esta incluye las siguientes clases:
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 75
jsEOPGA
jsEOPIndividual
jsEOPOpCrossOver
jsEOPOpMutation
jsEOPosition
La clase jsEOPGA como comentábamos en el apartado anterior, mantiene la misma
estructura y la misma metodología que en jsEOROGA, con la diferencia de que en vez de
llamar al método showPopTSP se llama al método showPopQueens, ya que la codificación de
la solución para cada problema es distinta y también, que no dibuja ningún grafo como
hacíamos en el problema anterior, sino que llama al método drawChessBoard, al que le
pasamos la mejor solución obtenida en la población final, y dibuja un tablero de ajedrez con
las posiciones de las reinas de dicha solución.
Clase jsEOPIndividual:
randomize: function (_length, _min, _max) {
if (typeof _length == 'undefined') {
_length = 10;
}
if (typeof _min == 'undefined') {
_min = 0;
}
if (typeof _max == 'undefined') {
_max = 1;
}
var chr = new Array(_length);
var used = new Array();
for(var n = 0; n < _length; ++n){
for ( var m = 0; m < _length; ++m){
used.push(new jsEOPosition(n,m));
}
}
for (var i = 0; i < _length; ++i) {
var random = jsEOUtils.intRandom(0, used.length - 1);
chr[i] = used[random];
used.splice(random,1);
}
this.setChromosome(chr);
return this;
}
En este caso el individuo de este problema utiliza una codificación distinta al del
problema anterior. Es por ello que el método randomize varia. En este caso, la codificación
utilizada es un Array de permutaciones. Para crear a un individuo aleatorio, primero creamos
otro Array con todas las posibles combinaciones de reinas, para ello hacemos uso de
jsEOPosition, que es una clase que contiene la fila y la columna en la que se situa cada reina.
Acto seguido, vamos asignadole al Array del individuo las posiciones de las reinas. En este
problema, la única restricción que encontramos es la de no poder asignar al mismo individuo
dos veces la misma reina, por lo que nos aseguramos que una vez le hemos asignado dicha
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 76
reina, eliminarla del Array que contiene las combinaciones de estas. Una vez creamos al
individuo, lo retornamos.
Clase jsEOPosition:
kills: function( position ) {
return Math.abs(this.x-position.getX())===0
|| Math.abs(this.y-position.getY())==0
|| Math.abs(this.x-position.getX())===Math.abs(this.y-
position.getY());
}
En esta clase, el método mas importante se llama kills. Este método se encarga de
comprobar si 2 reinas se encuentran en la misma fila, columna o diagonal.
Clase jsEOPOpCrossOver:
operate: function (_auxPop) {
jsEOUtils.debugln("Applying jsEOROOpCrossOver");
var toRet = new jsEOPopulation();
//If the population type is not defined, a new population is
returned
if (typeof _auxPop == 'undefined') {
return toRet;
}
if (_auxPop.length() <= 0) {
toRet.add(_auxPop.getAt(0).copy());
return toRet;
}
var rnd2 = jsEOUtils.intRandom(1, _auxPop.length() - 1);
var tmpChr1 = _auxPop.getAt(0).getChromosome().slice();
var tmpChr2 = _auxPop.getAt(rnd2).getChromosome().slice();
var newChr = [];
var orderChr = [];
var auxChr = [];
var point = jsEOUtils.intRandom(1, (tmpChr1.length - 2));
//CrossOver OX
for(var i = 0; i < point; ++i)
newChr.push(tmpChr1[i]);
for(var j = 0; j < tmpChr2.length; ++j)
orderChr.push(tmpChr2[j]);
for(var n = 0; n < newChr.length; ++n){
var index = jsEOUtils.exists(newChr, orderChr);
if(index != -1)
orderChr.splice(index, 1);
}
var m = 0;
while(newChr.length < tmpChr1.length){
newChr.push(orderChr[m]);
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 77
++m;
}
jsEOUtils.debugln(" Inicio es " + tmpChr1 + " Final " + newChr);
toRet.add(new jsEOPIndividual());
toRet.getAt(0).setChromosome(newChr);
return toRet;
}
Este operador está basado en una variedad del operador de cruce basado en un
punto. Se selecciona un punto de corte aleatorio. Desde la primera posición hasta la posición
del punto copia los individuos que se encuentren en el primer padre y y a partir de dicho
punto, los individuos que se encuentren en el segundo padre. La diferencia que hay con
respecto al operador original, es que tenemos la restricción de no copiar una posición de una
reina, si esta ya se encuentra en la solución.
La clase jsEOPOpMutation contiene el mismo operador de mutacion que en el
ejemplo anterior, por lo que su implementación se puede ver en el apartado anterior.
5.1.3.2.3. TSP Multi-objetivo
Para desarrollar este problema, se desarrollo una nuevo algoritmo, el cual se localiza
en la clase jsEOMOGA. Este algoritmo esta basado en el NSGA-II descrito en el apartado
3.1.
privateRun: function (_fitFn, _fitFnParams, _numGenerations) {
var popSize = this.population.length();
this.population.sort();
var sorting = new jsEOMOOpSortingND();
var crowding = new jsEOMOCrowdingDistance();
var childrenPop = new jsEOPopulation();
var auxPop = new jsEOPopulation();
var newPop = new jsEOPopulation();
var bestFit =
parseFloat(jsEOUtils.averageFitness(this.population).toFixed(5)) + 1;
var averFit =
parseFloat(jsEOUtils.averageFitness(this.population).toFixed(5));
//We execute as many times the algorithm as number of generations
ago
for (var j = 0; (j < _numGenerations); ++j) {
//We create a population in which we will include children and
parents.
//The first generation will only be parents
auxPop.join(this.population);
auxPop.join(childrenPop);
//We calculate the fronts from the non-dominated ordering
var fronts = sorting.operate(auxPop);
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 78
//Once the fronts have been calculated, we will add them to the
new population
var n = 0;
//In this loop as long as the size of the population is not
exceeded,
//the fronts are added to the new population.
//These are first sorted by the distance of Crowding
while (newPop.length() + fronts[n].length <= popSize) {
if (newPop.length() == popSize)
break;
crowding.operate(fronts[n], this.numberObjectives);
var _aPop = new jsEOPopulation();
_aPop.setPopulation(fronts[n]);
newPop.join(_aPop);
++n;
}
//Once we do not fit more complete fronts, we do a crowding
order.
//We calculate the crowding distance for the last partial front
if (newPop.length() < popSize) {
crowding.operate(fronts[n], this.numberObjectives);
this.sortCrowding(fronts[n]);
//This will leave us the population ordered in function of
said distance
var frontPopulation = new jsEOPopulation();
frontPopulation.setPopulation(fronts[n]);
//And we will only catch all those who fit in the new
population
frontPopulation.crop(popSize - newPop.length());
//We add them to the new population
newPop.join(frontPopulation);
}
//We make the corresponding crosses and mutations after making
the selection.
//Here we get the children after the selection and apply the
crossing and the mutation
var childrenPop = this.indivSelector.operate(this.population);
for (var i = 0; i < childrenPop.length(); ++i) {
var tmpPop = new jsEOPopulation();
tmpPop.add(childrenPop.getAt(i)).join(childrenPop);
tmpPop = this.operSelector.
operate().
operate(tmpPop).
evaluate(_fitFn, _fitFnParams);
childrenPop.setAt(i, tmpPop.getAt(0));
}
//Once we have obtained the children, we replace the population
//that we had at the beginning by the new population without
adding to the children
this.population.setPopulation(newPop.crop(popSize).getPopulation());
this.population.sortMO(this.population.getPopulation());
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 79
newPop = new jsEOPopulation();
if (typeof this.opSend != 'undefined' && this.opSend != null) {
this.opSend.operate(this.population);
}
auxPop = new jsEOPopulation();
} //for numGenerations
}
El algoritmo multi-objetivo comienza haciendo una unión entre la población de padres
y la población de hijos. En la primera generación la población de hijos esta vacía, por lo que
solo incluye a los padres. Después de esto obtiene los frentes a partir de la unión de la
población anterior y empieza a meterlos dentro de la nueva población. Cuando ya no quepan
más frentes completos en la nueva población, debido a que se exceda el tamaño establecido
para dicha población, ordena los individuos del ultimo frente por su distancia de crowding e
inserta todos los que aun puedan caber. Finalmente genera la población de hijos, y ordena la
nueva población obtenida en función de sus objetivos.
Tambien se creo un nuevo tipo de individuo, llamado jsEOMOIndividual:
var jsEOMOIndividual = new Class({
Extends: jsEO,
matrixObjectives: [],
crowding: 0,
np: 0,
sp: [],
rank: 0,
/**
* Method to know if a individual is dominated for this
* @method dominated
* @param {jsEOMOIndividual} _aIndividual
* @return dominates
*/ dominated: function (_aIndividual) {
//This method tells us if one individual is dominated by another.
//The concept of domination is based on one individual being better
than another.
//If this is not of lower quality in any of its objectives or if it
is better than the other in at least one of the objectives
var dominates = false;
for (var i = 0; i < this.matrixObjectives.length; ++i) {
if(this.matrixObjectives[i] > _aIndividual.matrixObjectives[i])
return false;
else if (this.matrixObjectives[i] <
_aIndividual.matrixObjectives[i])
dominates = true;
}
return dominates;
}
,
/**
* Comparison by distance of crowding
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 80
* @method crowdedComparison
* @param {jsEOMOIndividual} _aInd2
* @return toRet
*/
crowdedComparison: function (_aInd2) {
var toRet = 0;
//This method compares 2 individuals based on their crowding
distance.
//It is used to know which individuals enter from the front when
this does not fit whole
if (this.crowding > _aInd2.crowding)
toRet = 1;
else if (this.crowding < _aInd2.crowding)
toRet = -1;
return toRet;
}
,
evaluate: function (_fitFn) {
//This evaluation function does not only receive a fitness
//but receives an array with fitness for each objective to satisfy
var objectives = _fitFn(this.chromosome);
for (var i = 0; i < objectives.length; ++i) {
this.matrixObjectives.push(objectives[i]);
}
return this;
}
Este individuo multi-objetivo tiene un método llamado dominated, este método
comprueba si un individuo que se le pasa como parámetro domina a otro o no. También tiene
un método llamado crowdedComparison que sirve para comparar 2 individuos por su
distancia de crowding. Finalmente, el método evaluate evalúa al individuo para cada objetivo.
En este problema, también se desarrollaron 2 nuevos operadores,
jsEOMOOpSortingND y jsEOCrowdingDistance. Estos operadores vienen a realizar el
ordenamiento no dominado y el calculo de la distancia de crowding, respectivamente.
La representacion desarrollada para resolver este problema se llama
jsEOMultiobjective y contiene los siguientes ficheros.
jsEOMOTSPGA
jsEOMOTSPIndividual
jsEOMOOpCrossOver
jsEOMOOpMutation
jsEOMOCrowdingDistance
jsEOMOOpSortingND
La clase jsEOMOTSTGA tiene la misma estructura que jsEOROGA y jsEOPGA, con
la diferencia de que el grafico utilizado para la representación de este problema, se basa en
uno circular que muestra cuantos individuos están en el frente de Pareto y como se reparten
los restantes. Para ello se utiliza el método drawFitnessMO.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 81
La clase jsEOMOTSPIndividual se inicializa de la misma forma que
jsEOROIndividual, solo que su función evaluate difiere, ya que se le evalúa por objetivos.
evaluate: function(_fitFn) {
var objectives = _fitFn(this.chromosome);
for (var i = 0; i < objectives.length; ++i) {
this.matrixObjectives.push(objectives[i]);
}
return this;
}
Tanto la clase jsEOMOOpCrossOver como jsEOMOOpMutation utilizan los mismos
operadores que en la representación jsEORepresentationOrder, descritas en el apartado
5.1.3.2.1.
Clase jsEOMOCrowdingDistance:
operate: function (_aFront, _numberObjectives) {
//This method does what is done as a function of the number of
objectives
//to be satisfied is calculating the distance of each individual
from the front
//that is passed as a function of the individuals who are on his
left and his right.
//Finally it returns the front with the individuals but already
these with its distance
if(_aFront.length == 0)
return _aFront;
for (var i = 0; i < _numberObjectives; ++i) {
this.sortMO(_aFront, i);
_aFront[0].crowding = 9999999;
_aFront[(_aFront.length - 1)].crowding = 9999999;
for (var j = 1; j < _aFront.length - 1; ++j) {
_aFront[j].crowding += (_aFront[j + 1].matrixObjectives[i] -
_aFront[j - 1].matrixObjectives[i]);
}
}
return _aFront;
}
,
/**
* Sort the population by objectives
* @method sortMO
* @param {jsEOPopulation} _aPop Population to order
* @param {Integer} obj_index Number of the objective
* @return This population ordered
*/
sortMO: function (_aPop, obj_index) {
if(_aPop.length == 1)
return this;
var ind1 = 0, ind2 = 0;
for (var i = (_aPop.length - 1); i > -1; --i) {
for (var j = 1; j < (i + 1); ++j) {
ind1 = _aPop[j - 1];
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 82
ind2 = _aPop[j];
if (ind1.getFitnessAt(obj_index) >
ind2.getFitnessAt(obj_index)) {
_aPop[j - 1] = ind2;
_aPop[j] = ind1;
}
}
}
return this;
}
Este operador tiene 2 métodos destacables. El primero es el principal, opérate. En
este se encarga de calcular la distancia de crowding de un individuo en función de los
individuos que se encuentren a su alrededor. Para ello utiliza otro método, sortMO, el cual
ordena el frente que se le pasa como parámetro en función de sus objetivos para así obtener
adecuadamente la distancia de crowding de cada individuo.
Clase jsEOMOOpSortingND:
operate: function (_auxPop) {
var frentes = new Array();
frentes[0] = new Array();
//For each individual of the population we calculate the front to
which belongs
for (var i = 0; i < _auxPop.length(); ++i) {
_auxPop.getAt(i).sp = [];
_auxPop.getAt(i).np = 0;
for (var j = 0; j < _auxPop.length(); ++j) {
if (i === j)
continue;
//Create this method. Returns true if individual i is better
than individual j
//If individual i is better than j, it gets into the set of
individuals dominated by i
if (_auxPop.getAt(i).dominated(_auxPop.getAt(j))) {
_auxPop.getAt(i).sp.push(_auxPop.getAt(j));
//Otherwise, the level of dominance of the individual i
} else if (_auxPop.getAt(j).dominated(_auxPop.getAt(i))) {
_auxPop.getAt(i).np += 1;
}
}
//If the dominance level is 0, then go to the first front
if (_auxPop.getAt(i).np === 0){
_auxPop.getAt(i).rank = 0;
frentes[0].push(_auxPop.getAt(i));
}
//Once the first front is created, we calculate the others
var n = 0;
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 83
//While the front i is not empty
while (frentes[n].length !== 0) {
var Q = new Array();
//For each individual on the front
for (var a = 0; a < frentes[n].length; ++a) {
var ind = frentes[n][a];
//For each individual of the dominated set of the
individual i
for (var b = 0; b < ind.sp.length; ++b) {
//We obtain its level of dominance and we subtract 1
ind.sp[b].np -= 1;
//If its dominance level is 0, it goes to the set Q
if (ind.sp[b].np === 0) {
ind.sp[b].rank = n + 1;
Q.push(ind.sp[b]);
}
}
}
//Finally, we increase the front and we assign it
++n;
if(typeof frentes[n] != "undefined" && frentes[n] != null &&
frentes[n].length > 0){
for(var l = 0; l < Q.length; ++l)
frentes[n].push(Q[l]);
}else
frentes[n] = Q;
}
}
//Returns the vector with the fronts
return frentes;
}
Este operador se encarga de hacer el ordenamiento no dominado de los individuos, y
ordenarlos por frentes. Para cada individuo comprueba si es domina a otro de la población, en
caso afirmativo lo mete en sp que es el vector que contiene a los individuos a los que
domina. En caso contrario aumenta la variable np que indica el nivel de dominancia del
individuo, o lo que es lo mismo, el número de individuos por los que es dominado. Una vez
termina, comprueba su nivel de dominancia, si este es 0, es decir, no es dominado por nadie
lo mete en el primer frente o lo que es lo mismo, el frente de Pareto. Después, para todos los
individuos a los que domina comprueba si alguno de ellos se domina entre sí, asignándole así
el frente que le corresponde. Finalmente, devuelve el array con los frentes en los que se
encuentran los individuos.
5.2. Pruebas
En este apartado voy a hablar del estudio realizado de la efectividad del algoritmo
genético implementado para resolver el problema del TSP, y ver como varian las soluciones
obtenidas por este. Para realizar estos experimentos se ha utilizado un sistema operativo
Ubuntu en su versión 16.04 junto con la versión 4.2.6 de Node.js y MongoDB 3.4.
Antes de explicar los resultados, explicare brevemente el proceso utilizado para
calibrar la tasa de los operadores del algoritmo, asi como el resto de parámetros que son fijos.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 84
En nuestro caso, para calibrar dicho algoritmo usamos una población de 25 individuos
sobre los cuales se han aplicado 10 generaciones. Tambien se ha dejado la tasa de
reemplazamiento del operador de selección a 0.5 y el porcentaje de mutacion a 0.5.
Tambien para calibrar el algoritmo, se han utilizado un total de 8 ciudades en el
problema del TSP. A la hora de hacer los experimentos se aumento la complejidad, subiendo
el numero de ciudades a 12.
Los valores posibles dados para los operadores han ido en el rango de 0 - 0.25 - 0.5 -
0.75 – 1. Se han hecho 5 ejecuciones de las combinaciones posibles. Estas se pueden ver en
el Anexo V.
Una vez probadas las distintas configuraciones posibles, se eligieron los 2 mejores
valores para cada operador y se combinaron para asi obtener la mejor configuración. Estos
resultados se pueden consultar en el Anexo V.
Ahora que ya tenemos el algoritmo “calibrado” voy a pasar a explicar los experimentos.
Se han realizado un total de 9 experimentos sobre los que se han realizado 10 ejecuciones
para cada uno.
Estos experimentos se han basado en como mejora el algoritmo las soluciones
encontradas, en función del tamaño de la población, asi como del numero de generaciones.
Para ello se han tomado los siguientes datos para realizarlos:
Poblacion de 500 individuos y 20 generaciones
Poblacion de 500 individuos y 30 generaciones
Poblacion de 500 individuos y 40 generaciones
Poblacion de 750 individuos y 20 generaciones
Poblacion de 750 individuos y 30 generaciones
Poblacion de 750 individuos y 40 generaciones
Poblacion de 1000 individuos y 20 generaciones
Poblacion de 1000 individuos y 30 generaciones
Poblacion de 1000 individuos y 40 generaciones
Se han tomado estos datos debido a que el tiempo de computo del algoritmo varia de
un dispositivo a otro, pero debido a las limitaciones que tiene JavaScript era posible que en
algunos dispositivos no funcionase correctamente. Ademas, como para el calibrado del
algoritmo se utilizo un total de 8 ciudades, el numero total de combinaciones posibles para
una solución seria de 8! lo que viene a hacer un total de 40320 combinaciones posibles.
Es por esto que se decidio coger un rango de entre un 20 – 30 % de combinaciones
posibles, con respecto del total, atendiendo a que el Tamaño Poblacion / Nº Generaciones
fuese el equivalente a dicho porcentaje.
Para valorar dichos experimentos y poder sacar resultados en claro, se han tenido en
cuenta los siguientes valores:
Fitness de la solución optima de la población inicial
Fitness de la solución optima de la población final
Tiempo de ejecución
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 85
A partir de estos valores se ha obtenido el ratio de mejora del algoritmo al aplicárselo a
una población aleatoria inicial y cuanto tiempo ha necesitado para ejecutarse.
Los resultados obtenidos para los diferentes experimentos se pueden encontrar en las
3 siguientes tablas:
Pobl.: 500 Gen.:20 Pobl.: 500 Gen.:30 Pobl.: 500 Gen.:40
Ejecucion Fit. Inicial Fit. Final Fit. Inicial Fit. Final Fit. Inicial Fit. Final
1 646 600 711 450 715 422
2 687 527 711 415 620 459
3 435 435 751 492 709 466
4 751 496 760 461 593 469
5 713 540 618 482 728 451
6 696 536 719 477 704 419
7 682 576 686 468 732 411
8 785 578 773 483 759 472
9 785 484 734 512 680 451
10 651 534 631 512 622 393
Mejora 152,5 234,2 244,9 Tabla 5. Tabla de experimentos con tamaño de poblacion 500
Pobl.: 750 Gen.:20 Pobl.: 750 Gen.:30 Pobl.: 750 Gen.:40
Ejecucion Fit. Inicial Fit. Final Fit. Inicial Fit. Final Fit. Inicial Fit. Final
1 662 467 648 434 708 415
2 672 534 620 424 772 429
3 581 463 658 516 736 361
4 609 531 743 466 583 482
5 566 515 568 447 715 444
6 611 550 484 445 514 438
7 673 499 463 463 692 400
8 685 564 669 506 657 381
9 610 546 689 471 643 419
10 687 533 672 471 489 431
Mejora 115,4 157,1 230,9 Tabla 6. Tabla de experimentos con tamaño de poblacion 750
Pobl.: 1000 Gen.:20 Pobl.: 1000 Gen.:30 Pobl.: 1000 Gen.:40
Ejecucion Fit. Inicial Fit. Final Fit. Inicial Fit. Final Fit. Inicial Fit. Final
1 688 560 704 444 528 399
2 594 481 634 444 687 444
3 648 539 735 416 581 439
4 677 472 703 383 647 419
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 86
5 699 532 591 524 662 379
6 656 538 724 420 672 411
7 661 545 639 428 719 413
8 673 421 601 452 681 419
9 674 547 726 470 655 407
10 488 439 654 498 695 430
Mejora 138,4 223,2 236,7 Tabla 7. Tabla de experimentos con tamaño de poblacion 1000
Los números en negrita de las tablas anteriores, indican la mejor solución obtenida
para cada experimento realizado.
En la siguiente figura se puede ver el ratio de mejora, el cual es la media de las
diferencias de los fitness de las mejores soluciones de la poblacion inicial y final, de cada
experimento realizado con el algoritmo utilizado.
Como podemos observar, en el grafico anterior, los experimentos con el mayor
numero de generaciones son aquellos en los que el ratio de mejora aumenta
considerablemente. Por tanto, se podria decir, que al aumentar las generaciones mejoran las
soluciones encontradas mientras que al aumentar el tamaño de la poblacion creamos mas
diversidad.
Tambien se ha experimentado con los operadores Get y Send implementados para la
comunicación con el servidor. Se han probado 3 maquinas distintas conectadas al servidor y
se ha comprobado si la utilizacion de este mejoraba la solucion encontrada por otro usuario.
En este aspecto, las maquinas utilizadas han sido:
Ordenador Portatil Hacer Aspire 5742G, con un procesador Intel Core i3 con 4
GB de RAM. Utilizado navegador Firefox.
Ratio Mejora Experimentos
Exp. 1
Exp. 2
Exp. 3
Exp. 4
Exp. 5
Exp. 6
Exp. 7
Exp. 8
Exp. 9
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 87
Ordenador de sobremesa Beep, con un procesador AMD Athlon x64 con 1,37
GB de RAM. Utilizado navegador Firefox.
Smartphone Samsung Galaxy J5, con procesador Qualcomm Snapdragon 410
8916 y 1,5 GB de RAM. Utilizado navegador web Chrome for Android.
Para realizar este experimento, se ha utilizado la configuracion obtenida para calibrar
el algoritmo y se han tomado como datos, los siguientes:
Poblacion de 750 individuos con 30 generaciones, con un total de 12 ciudades.
Este se ha ejecutado 10 veces en cada dispositivo, y los resultados obtenidos, han
sido los siguientes:
Acer Aspire Sobremesa Beep Samsung Galaxy J5
Ejecucion Fit. Inicial Fit. Final Fit. Inicial Fit. Final Fit. Inicial Fit. Final
1 621 499 560 431 676 466
2 648 525 743 475 669 397
3 667 493 522 459 685 461
4 710 417 638 484 578 383
5 585 468 590 426 522 477
6 688 393 697 493 592 477
7 715 419 685 494 660 419
8 608 493 612 510 670 440
9 620 470 685 525 596 490
10 704 460 684 500 647 502
Mejora 192,9 161,9 178,3 Tabla 8. Experimento 10 sobre el modelo de islas
Como podemos ver, la ejecucion de este algoritmo en cada uno de los dispositivos
consigue que la solucion del otro mejore, incluse en el dispositivo movil. La verificacion de que
el modelo de islas funciona, se comprueba en la ejecucion 7. Tanto en el dispositivo Hacer
Aspire como en el dispositivo Samsung Galaxy, la mejor solucion de la poblacion final
coinciden. Esto quiere decir que intercambien informacion, aunque como podemos ver en el
resto de ejecuciones, al intercambiar la informacion, el algoritmo es capaz de mejorarla
solucion en cada dispositivo en casi todos los casos.
En total, se han realizado 10 experimentos. Esto se debe a que hemos querido tener
una primera aproximacion a como funciona el algoritmo y la capacidad de computo que
posee.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 88
6. Conclusión
El Algoritmo Genetico tiene un comportamiento probabilístico. Por lo que como bien
ha dejado claro los resultados, puede dar buenos resultados en un periodo corto de tiempo,
en función del problema a resolver y los valores para los operadores, asi como el tamaño de
la población y el numero de generaciones.
El Algoritmo Genetico utilizado mejoraba la solución Greedy encontrada en casi todos
los casos. En la mayoría de los casos, la mejor solución de la población inicial ya de por si era
mucho mejor que la encontrada por la greedy, algo que en cierto modo es casual, debido a
que se crea aleatoriamente. Sin embargo, después de ejecutar todas las pruebas casi en un
80 % de las veces el algoritmo daba con una solución mucho mejor que la greedy al finalizar
este.
Se ha podido comprobar que en el caso de se han obtenido muchas mejores
soluciones que con respecto al resto de experimentos. El hecho de tener una población
grande te da la posibilidad de tener una población inicial bastante diversa ya que esta es
aleatoria mientras que una pequeña también te permite tener una población diversa, con la
diferencia de que en función del numero de generaciones que tengamos para cada una, hara
que el algoritmo llegue antes a un optimo local o al global.
Tambien hemos conseguido calibrar el algoritmo genético para una mejor eficiencia a
la hora de encontrar las soluciones. Ha quedado claro que aumentar el numero de
mutaciones, amplia el espacio de búsqueda del algoritmo o lo que es lo mismo, genera
diversidad en la población por lo que es mas fácil encontrar mejores soluciones. Para el cruce
también se ha utilizado un valor alto, pero menor que el de la mutacion, ya que de esta
manera evitamos asi que el algoritmo converja al principio.
En definitiva, jsEO es una biblioteca bastante potente para el desarrollo de algoritmos
genéticos y problemas que puedan ser resueltos por estos. Es bastante fácil de comprender y
utilizar, asi como también la posibilidad de añadir mas contenido, entiéndase por esto,
algoritmos, problemas, operadores, etc.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 89
7. Anexos
Anexo I Instalación de Node.js
Para instalar Node.js, basta con que nos dirijamos a la siguiente dirección web
https://nodejs.org/en/download/. Ahí descargar el fichero de instalación en función del sistema
operativo que estemos usando.
Para instalar Node.js en Windows:
Una vez hayamos entrado a la pagina, nos dara la opción de elegir entre la versión
LTS o Current. La versión LTS es la recomendada, ya que aunque no es la mas novedosa si
contiene todas las tecnologías posibles de Node que se consideran estables, mientras que la
versión Current, es justo lo contrario.
Una vez seleccionada la versión a descagar, elegir el tipo de instalador y la
arquitectura de este en función de la arquitectura de nuestro sistema operativo ya sea 32 o 64
bit. Yo recomiendo el instalador con formato “.msi” ya que este se encarga de hacer todo el
proceso de instalación, agregando las variables de sistema e incluyendo el gestor de modulos
de Node NPM.
Una vez descargado el fichero y ejecutarlo, nos saldrán una serie de ventanas:
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 90
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 91
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 92
Una vez instalado, para probar su funcionamiento abrimos una consola de comandos
de Windows, para ello pulsamos las teclas de Windows+R y escribimos en el recuadro
cmd
Una vez abierta la consola de comandos de Windows, escribimos el siguiente
comando:
Node
Si todo ha ido bien, y ejecutamos:
console.log(“Hola Mundo”);
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 93
Nos saldrá lo siguiente:
Para instalar las dependencias de Node, nos valdremos del fichero package.json, el
cual tendrá un formato parecido al siguiente:
Ilustración 47. Package.json
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 94
Volveremos a abrir una consola de comandos, como antes lo hemos hecho, y
ejecutamos el siguiente comando:
cd \path
donde \path es el directorio donde tenemos el proyecto.
Una vez ubicados en el directorio de nuestro proyecto, ejecutar el siguiente comando:
npm install
Este comando al no tener argumentos, instalara todas las dependencias de nuestro
package.json. Con las que vienen en nuestro fichero, es mas que suficiente para que la
aplicación funcione, pero en caso de tener las mas actualizadas y/o que estas se hayan
quedado obsoletas, ejecutar el comando:
npm update
Este comando actualizara todas las dependencias de las que depende nuestro fichero
package.json a la ultima versión mas reciente y estable.
En Ubuntu , la instalación se haría de la siguiente manera(en la versión 16.04 se
incluye una versión de Node.js por defecto):
Abriremos un terminal nuevo y ejecutaremos el comando:
sudo –s
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 95
Este comando nos dara privilegios de administrador en Ubuntu, por lo cual siempre
que queramos ejecutar Node y las dependencias de este deberemos estar logueados como
tal.
Una vez introducido el comando nos pedirá la contraseña del adminsitrador, la
introducimos, y una vez hecho eso, nos debería de aparecer lo siguiente:
(logueo como superusuario terminal pantallazo)
Ahora que tenemos privilegios de administrador del sistema, nos iremos al directorio
donde tengamos nuestro proyecto con el comando:
cd /path
Donde “/path” es el directorio donde tenemos el proyecto.
Una vez estemos situados en el directorio, ejecutar el siguiente comando:
sudo apt-get update
Este comando descarga la lista de paquetes de los repositorios y actualizaciones mas
recientes.
Ahora ejecutaremos el siguiente comando:
sudo apt-get install nodejs
Una vez termine la instalación de Node, podemos comprobar si todo ha ido bien
escribienod el siguiente comando:
nodejs –v
Con este comando, se nos mostrara la versión de Node.js instalada.
Debido a un conflicto con otro paquete, para poder ejecutar Node en Ubuntu,
tendremos que ejecutar el comando:
nodejs
Para poder instalar las dependencias de nuestro package.json, instalaremos el gestor
de paquetes de Node.js mediante el siguiente comando:
sudo apt-get install npm
Por ultimo, una vez instalado este, para que nos instale y actualice las dependencias
de nuestro proyecto ejecutaremos los siguientes comandos, en el siguiente orden:
npm install
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 96
npm update
En MacOS, la instalación se haría:
Primero deberemos ir a la pagina oficial de Node. Una vez allí y al igual que como en
Windows, seleccionar la versión que queremos. La versión LTS es la mas recomendada, asi
que una vez seleccionada, descargar el fichero en formato .pkg.
Ejecutar el fichero que nos hemos descargado previamente. Una vez ejecutado se nos
abrirán una serie de ventanas:
Introduccion: Seleccionar Continuar.
Licencia: Seleccionar Continuar y después Aceptar.
Tipo de instalación: Seleccionar Instalar y nos pedirá nuestras credenciales de usuario
para autenticar la instalación. Por ultimo, seleccionar Instalar Sotfware.
Sumario: Seleccionar cerrar.
Para verificar que Node se ha instalado correctamente, ejecutar en una consola el
siguiente comando:
node –v
Este comando nos mostrara la versión de Node que ha sido instalada. Por ultimo, para
actualizar el gestor de paquetes de Node, ejecutaremos el siguiente comando en una consola:
sudo npm install npm --global
Por último y esto es en función del SO que hayamos usado para instalar Node, para
lanzar nuestro servidor y poder probar nuestra aplicación, abriremos una terminal, nos
dirigiremos al directorio donde se encuentre el servidor y ejecutaremos el siguiente comando
si nos encontramos en Ubuntu:
nodejs nombreservidor.js
En Windows seria:
node nombreservidor.js
Y en MacOS:
node nombreservidor.js
, donde nombreservidor.js hace referencia al nombre del fichero JavaScript en el que
tengamos nuestro servidor Node.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 97
Anexo II Instalación de MongoDB
Para instalar MongoDB en Ubuntu 16.04, seguiremos los siguientes pasos:
Ubuntu, se asegura de autenticar los paquetes de software, asi que primero
deberemos importar la llave para el repositorio oficial de MongoDB. Para ello, antes de nada
deberemos loguearnos como administrador. Para ello solo es necesario abrir una terminal y
ejecutar el siguiente comando:
sudo –s
Este comando nos pedirá que introduzcamos la contraseña para el administrador del
sistema. Una vez introducida nos quedara lo siguiente:
Despues de habernos logueado como root, procederemos a agregar la clave del
repositorio oficial de MongoDB. Este se consigue con el siguiente comando:
sudo apt-key adv –keyserver hkp://keyserver.ubuntu.com:80 –recv EA312927
Despues de agregar la llave, nos aparecerá lo siguiente:
Justo después de esto, debermos agregar el repositorio de Mongo para que el gestor
de paquetes sepa de donde debe descagarlos. Escribiremos el siguiente comando:
echo “deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse” | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 98
Despues de agregar los detalles del repositorio, actualizaremos la lista de paquetes de
nuestro sistema:
sudo apt-get update
Ahora ya podemos instalar MongoDB. Para ello ejecutaremos:
sudo apt-get install –y mongodb-org
Una vez termine, tendremos MongoDB instalado en nuestro sistema Ubuntu. Si lo que
queremos es lanzar MongoDB como un servicio de Ubuntu 16.04, deberemos crear un
archivo que describa dicho servicio.
Para ello vamos a crear un fichero de configuración llamado mongodb.service en el
directorio /etc/systemd/system ayundadonos del comando nano:
sudo nano /etc/systemd/system/mongodb.service
A continuación rellenaremos dicho fichero con el siguiente contenido:
Lo guardaremos y cerraremos el archivo. Lo siguiente que habrá que hacer será iniciar
el servicio recién creado con systemctl:
sudo systemctl start mongodb
Para comprobar que el servicio se ha iniciado, podemos hacer uso del comando:
sudo systemctl status mongodb
Obtendremos el siguiente mensahe en la consola:
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 99
Por ultimo, solo nos quedaría habilitar el arranque automatico de MongoDB cada vez
que nuestro sistema se inicie. Para ello utilizar el comando:
sudo systemctl enable mongodb
Para acceder a la consola de Mongo, solo nos bastaría con ejecutar el siguiente
comando, y siempre con privilegios de administrador:
Mongo
Y nos debería de aparecer lo siguiente:
Para instalar MongoDB en Windows, tendríamos que hacer lo siguiente:
Acceder a la siguiente URL y descargar el instalador de Windows. En este caso
recomiendo utilizar la versión Community Server cuya versión actual es la 3.4.7. Esta funciona
con versiones de Windows Server 2008 R2 y posteriores siempre que su arquitectura sea de
64 bit. Tener en cuenta que las versiones de 64 bits de MongoDB no funcionan con versiones
de Windows de 32 bits, por posteriores que sean estas a Windows Server 2008 R2.
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 100
Una vez descargado el archivo de instalación, lo ejecutaremos, y a partir de ahí seguir
los pasos. Una vez terminada la instalación y para comprobar que esta se ha realizado con
éxito, abriremos una consola de comandos de Windows y ejecutaremos el siguiente comando:
“C:\Program Files\MongoDB\Server\3.4\bin\mongo.exe”
Una vez hemos ejecutado ese comando, y si todo ha ido bien, se lanzara la consola de
mongo en nuestra consola de comandos. Ahora, si queremos tener un servicio en Windows
para MongoDB, seguiremos los siguientes pasos.
Primero de todo abriremos una consola de comando en modo Administrador, para ello
solo debemos pulsar las teclas Windows+R y escribir en el recuadro que nos aparecerá
cmd.exe y acto seguido presionaremos las siguientes teclas Ctrl+Shift+Intro. Asi se nos
lanzara una consola con permisos de Administrador.
Ahora vamos a crear 2 directorios. Una para nuestra base de datos y otro para los
ficheros de registro, o como se le conocen, archivos log:
mkdir C:\data\db mkdir C:\data\log
Una vez creados los directorios, crearemos un fichero de configuración, el cual se
encontrara en “C:\Program Files\MongoDB\Server\3.4\” y cuyo nombre será “mongod.cfg”. El
contenido de este fichero, será el siguiente:
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 101
Por ultimo, instalaremos el servicio de MongoDB. Para ello escribiremos el siguiente
comando en nuestra consola:
“C:\Program Files\MongoDB\Server\3.4\mongod.exe” –config “C:\Program Files\MongoDB\Server\3.4\mongod.cfg” –install
Nota: Cuando ejecutemos todos estos comando en nuestra consola de Windows,
debemos de asegurarnos que tenemos privilegios de Administrador.
Por ultimo, para instalar MongoDB en MacOS, haremos lo siguiente:
Abriremos una consola y escribiremos el siguiente comando:
brew update
Este comando actualizara la base de datos de paquetes de HomeBrew. Una vez
actualizada, solo tendremos que ejecutar lo siguiente:
brew install mongodb
Ahora crearemos un directorio, el cual nos servirá para que MongoDb almacene los
datos. Por defecto, este utiliza el directorio /data/db. Si utilizásemos otro directorio, tendremos
que indicarlo. El siguiente comando toma como directorio el descrito anteriormente
mkdir -p /data/db
Una vez creado el directorio, y antes de ejecutar MongoDB, debemos asegurarnos de
que el directorio que acabamos de crear tiene permisos de lectura y escritura. Por ultimo, ya
solo nos queda ejecutar MongoDB. Para ello usaremos el comando:
mongod
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 102
Anexo III Código fuente del controlador del servidor Node y de los
modelos para la base de datos con MongoDB
Debido a que el código del controlador es bastante extenso, voy a explicar cada
método implementado en el utilizando un ejemplo.
Entre estos métodos se encuentran sending, receiving y root.
El método sending, se encarga de actualizar los individuos de nuestra base de datos
con MongoDB, en función de si este es mejor que el que se encuentra almacenado, en caso
contrario devuelve un mensaje de error y mantiene al individuo que se encontraba en la base
de datos.
Para el ejemplo del TSP, busca al individuo en la base de datos en función del tamaño
del problema, es decir, si tiene 5, 6 8 o 12 ciudades.
exports.sending = function(req, res){
allowCORS(res);
var obj = req.body;
console.log("Datos recibidos en el servidor para insertar",
JSON.stringify(obj));
switch(obj.Problem){
case 'TSP':
var id = 0;
switch(obj.tamIndividual){
case '5':
id=1;
break;
case '6':
id=2;
break;
case '8':
id=3;
break;
case '12':
id=4;
break;
}
jsEOTSP.findById(id, function(err, individual){
if(err){
console.log("Error en la insercion", err);
res.send({Solution: null, Fitness: null, Success:false,
msg:"Error en la insercion", Problem: obj.data});
}else{
if(individual != null){
var obj1 = JSON.parse(obj.Fitness);
var obj2 = JSON.parse(individual.Fitness);
if(obj1 < obj2){
individual.Solution = obj.Solution;
individual.Fitness = obj.Fitness;
individual.save(function(err, data){
if(err) console.log(err);
else{console.log("Datos para
actualizar", data);
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 103
var result = {Solution:
data.Solution, Fitness: data.Fitness, Success: true, msg: "Actualizacion
realizada con exito", Problem: obj.Problem};
res.send(result);
}
});
}else{
var result = {Solution: null, Fitness: null,
Success: false, msg: "Actualizacion fallida", Problem: obj.Problem};
res.send(result);
}
}else{
var newjseo = new jsEOTSP({
_id : id,
Solution: obj.Solution,
Fitness: obj.Fitness
});
newjseo.save(function(err, data){
if(err) res.send({Solution: null, Fitness: null,
Success:false, msg:"Error en la insercion", Problem: obj.data});
else{
console.log("Datos guardados", data);
var result = {Solution: data.Solution,
Fitness: data.Fitness, Success: true, msg: "Primera insercion realizada con
exito", Problem: obj.Problem};
res.send(result);
}
});
}
}
});
break;
El método receiving, se encarga de administrar las peticiones de los usuarios que
están ejecutando algún problema y solicitan al servidor el individuo que se encuentra en la
base de datos. Al igual que antes para el ejemplo del TSP, la consulta se hace en función del
número de ciudades seleccionado con el que se haya ejecutado el problema.
exports.receiving = function(req, res){
allowCORS(res);
var obj = req.query;
var tam = obj.tamIndividual;
console.log("Recibida peticion de individuo para :", obj.data);
switch(obj.data){
case 'TSP':
var id = 0;
switch(obj.tamIndividual){
case '5':
id=1;
break;
case '6':
id=2;
break;
case '8':
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 104
id=3;
break;
case '12':
id=4;
break;
}
jsEOTSP.findById(id, function(err, individual){
if(err){
console.log("Error en la consulta", err);
res.send({Solution: "", Fitness: "", Success: false,
msg: "Error en la consulta", Problem: obj.data});
}else
{
if(individual !== null){
var result = {Solution: individual.Solution,
Fitness: individual.Fitness, Success: true, msg: "Obtenido individuo de la
BBDD", Problem: obj.data};
res.send(result);
}else{
res.send({Solution: null, Fitness: null, Success:
false, msg: "No hay individuos de este tipo aun en la BBDD", Problem:
obj.data});
}
}
});
break;
En cuanto a los modelos para la base de datos, hay 5 distintos, en función de la
colección con la que trabajan. En este caso, debido a que los Schemas utilizados para todos
los problemas son muy parecidos, solo usaremos uno de ejemplo.
var mongoose = require('mongoose')
,Schema = mongoose.Schema
,ObjectId = mongoose.ObjectId;
var jsEOTSPSchema = new Schema({
_id: {type: Number},
Solution: {type: Array},
Fitness: {type: Number}
}, {upsert: true, collection: 'TSP'});
module.exports = mongoose.model('jsEOTSP', jsEOTSPSchema);
Como se puede observar, cada Schema tiene asignada una colección, donde las
variables utilizadas son un identificador, _id, la variable Solution que se utiliza para guardar la
codificación de la solución y Fitness que se utiliza para almacenar el valor de fitness de la
solución. Por último se crea el modelo mediante mongoose.model(“Nombre del modelo”,
Nombre del Schema).
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 105
Anexo IV Código fuente de los ficheros comunes utilizados para el
desarrollo de los problemas
En este anexo, se va a mostrar las funciones más destacables de las clases utilizadas
para el desarrollo y ejecución de los problemas. Entre estos se encuentran, jsEOGA,
jsEOOpSelectorTournament, jsEOGetIndividualsNode y jsEOSendIndividualsNode.
El resto de clases, básicamente son abstractas por lo que las descritas durante el
apartado 5.1.3. son las que realmente tienen los métodos necesarios para la ejecución del
algoritmo.
Clase jsEOGA:
privateRun: function(_fitFn, _fitFnParams, _numGenerations) {
var popSize = this.population.length();
this.population.sort();
var bestFit =
parseFloat(jsEOUtils.averageFitness(this.population).toFixed(5)) + 1;
var averFit =
parseFloat(jsEOUtils.averageFitness(this.population).toFixed(5));
for (var j = 0; (j < _numGenerations); ++j) {
var newPop = this.indivSelector.operate(this.population);
for (var i = 0; i < newPop.length(); ++i) {
var tmpPop = new jsEOPopulation();
tmpPop.add(newPop.getAt(i)).join(newPop);
tmpPop = this.operSelector.
operate().
operate(tmpPop).
evaluate(_fitFn, _fitFnParams);
newPop.setAt(i, tmpPop.getAt(0));
}
this.population.join(newPop).sort().crop(popSize);
if (typeof this.opSend != 'undefined' && this.opSend != null) {
this.opSend.operate(this.population);
}
bestFit =
parseFloat(this.population.getAt(0).getFitness().toFixed(5));
averFit =
parseFloat(jsEOUtils.averageFitness(this.population).toFixed(5));
jsEOUtils.recordStats(this.population.getLast().getFitness(),
jsEOUtils.averageFitness(this.population),
this.population.getAt(0).getFitness());
} //for numGenerations
}
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 106
Clase jsEOOpSelectorTournament:
operate: function( _auxPop ) {
var toRet = new jsEOPopulation();
for (var j = 0; j <this.numIndividuals; ++j) {
var tmpInd = Math.floor(Math.random() * _auxPop.length());
for (var i = 1; i < this.tournamentSize; ++i) {
rnd = Math.floor(Math.random() * _auxPop.length());
jsEOUtils.debugln(" Comparando " +
_auxPop.getAt(rnd).getFitness() +
" con " + _auxPop.getAt(tmpInd).getFitness());
tmpInd = (_auxPop.getAt(rnd).gt(_auxPop.getAt(tmpInd))) ?
rnd : tmpInd;
}
jsEOUtils.debugln(" Final " +
_auxPop.getAt(tmpInd).getFitness());
toRet.add(_auxPop.getAt(tmpInd).copy());
}
return toRet;
}
Clase jsEOGetIndividualsNode:
operate: function(_auxPop) {
var toRet = _auxPop;
var data2bSend = {"data": jsEOUtils.getProblemId(), "tamIndividual":
_auxPop.getAt(0).getChromosome().length};
try {
new Request.JSON({
url: jsEOUtils.getGetURL(),
method: 'GET',
data: data2bSend,
onComplete: function(response){
if(response.Success === false){
console.log("Error: "+response.msg);
return;
}else{
console.log("Respuesta del servidor para el
individuo solicitado: ", response.msg);
if(response.Solution.length > 0){
var newInd = new jsEOIndividual();
newInd.setChromosome(JSON.parse(response.Solution));
newInd.setFitness(response.Fitness);
toRet.add(newInd);
console.log("Individuo incorporado con exito a
la poblacion", newInd);
}else{
toRet = _auxPop;
}
}
}
}).send();
} catch (err) {
return toRet=_auxPop;
}
return toRet;
}
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 107
Clase jsEOSendIndividualsNode:
operate: function(_auxPop) {
var tmpPop = new jsEOPopulation();
var problemID = jsEOUtils.getProblemId();
var solution = [],fitness = 0, matrixObj = [];
for (var i = 0; i < this.numIndividuals; ++i) {
var tmpChr = _auxPop.getAt(i).getChromosome();
if (Object.prototype.toString.call(tmpChr) === '[object Array]')
{
for (var j = 0; j < tmpChr.length; ++j) {
if(Object.prototype.toString.call(tmpChr[j]) ===
'[object Object]')
solution.push(tmpChr[j].getJSON());
else
solution.push(tmpChr[j]);
}
} else {
solution = tmpChr;
}
fitness = _auxPop.getAt(i).getFitness();
}
var data2bSend = {"Problem" : problemID, "Solution" :
JSON.stringify(solution), "Fitness" : fitness, "tamIndividual":
solution.length};
try{
new Request({
url: jsEOUtils.getSendURL(),
method: 'POST',
data: data2bSend,
onComplete: function(response){
var res = JSON.parse(response);
var result = {Solution: JSON.parse(res.Solution),
Fitness: res.Fitness};
console.log("Respuesta del servidor al enviar el
individuo: ", res.msg);
}
}).send();
}catch(error){
console.log("Error al enviar el individuo: ", error);
}
return null;
}
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 108
Anexo V Tablas con resultados para configuración del algoritmo
Ejecucion 1 Ejecucion 2 Ejecucion 3 Ejecucion 4 Ejecucion 5 Media
Cruce/Mutacion Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
(Fitness Ini. - Fitness Fin.)
0 0 637 637 736 736 770 770 770 770 741 741 0
0 0.25 729 679 788 715 714 695 714 666 704 671 54,6
0 0.5 826 652 736 729 710 645 763 732 739 674 68,4
0 0.75 735 629 702 626 697 661 779 632 699 679 77
0 1 735 666 734 693 711 677 761 654 773 677 69,4
0.25 0 763 722 782 660 766 745 793 734 729 710 52,4
0.5 0 681 663 725 629 717 632 716 687 763 753 47,6
0.75 0 832 763 761 757 761 629 832 711 767 744 69,8
1 0 718 716 733 626 783 656 754 718 789 735 65,2 Tabla 9. Tabla configuraciones iniciales de los operadores
Ejecucion 1 Ejecucion 2 Ejecucion 3 Ejecucion 4 Ejecucion 5 Media
Cruce/Mutacion Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
Fit. P.Inicial
Fit. P.Final
(Fitness Ini. - Fitness Fin.)
0.75 0.25 763 693 712 634 724 609 702 641 733 681 78,6
0.75 1 749 637 701 674 746 632 752 674 733 629 87
1 0.75 774 632 749 721 761 652 693 634 752 675 83
1 1 699 679 753 629 712 629 714 632 756 711 70,8 Tabla 10. Tabla mejores configuraciones encontradas para cada operador
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 109
Bibliografía
Citas:
Benitez, J., s.f. Tecnopedia.net. [En línea]
Available at: http://www.tecnopedia.net/minitecnos/chart-js-libreria-javascript-para-generar-
graficas/
[Último acceso: Julio 2017].
EO, s.f. http://eodev.sourceforge.net/. [En línea]
Available at: http://eodev.sourceforge.net/
[Último acceso: Marzo 2017].
Epochx, s.f. Epochx. [En línea]
Available at: http://www.epochx.org/
[Último acceso: Marzo 2017].
Esteso, M. P., s.f. Geekytheory. [En línea]
Available at: https://geekytheory.com/dibuja-grafos-en-javascript-con-vis-js
[Último acceso: Julio 2017].
Express, s.f. Express. [En línea]
Available at: http://expressjs.com/es/
[Último acceso: Mayo 2017].
Gacto Colorado, M. J., 2016. Métodos basados en poblaciones. Algoritmos Genéticos, s.l.:
s.n.
IBM, s.f. ¿Simplemente qué es Node.js?. [En línea]
Available at: https://www.ibm.com/developerworks/ssa/opensource/library/os-nodejs/
[Último acceso: Mayo 2017].
Koza, J., 1992. Genetic Programming: On the Programming of Computers by Means of
Natural Selection. s.l.:Complex Adaptive Systems.
Lopez, J. C., s.f. AdictosAlTrabajo. [En línea]
Available at: https://www.adictosaltrabajo.com/tutoriales/jgap/
[Último acceso: Mayo 2017].
WatchMaker, s.f. WatchMaker. [En línea]
Available at: https://watchmaker.uncommons.org/
[Último acceso: Mayo 2017].
Whitley, L. D., 1993. Foundatios of Genetic Algorithms 2. s.l.:Morgan Kaufmann Publishers.
Wikipedia, s.f. Bootstrap. [En línea]
Available at: https://es.wikipedia.org/wiki/Bootstrap_(framework)
[Último acceso: Julio 2017].
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 110
Wikipedia, s.f. Fork (desarrollo de software). [En línea]
Available at: https://es.wikipedia.org/wiki/Bifurcaci%C3%B3n_(desarrollo_de_software)
[Último acceso: Julio 2017].
Wikipedia, s.f. Java Evolutionary Computing Toolkit. [En línea]
Available at: https://en.wikipedia.org/wiki/Java_Evolutionary_Computation_Toolkit
[Último acceso: Mayo 2017].
Wikipedia, s.f. Javascript. [En línea]
Available at: https://es.wikipedia.org/wiki/JavaScript
[Último acceso: Marzo 2017].
Wikipedia, s.f. jQuery. [En línea]
Available at: https://es.wikipedia.org/wiki/JQuery
[Último acceso: Julio 2017].
Wikipedia, s.f. MongoDB. [En línea]
Available at: https://es.wikipedia.org/wiki/MongoDB
[Último acceso: Junio 2017].
Wikipedia, s.f. Mootools. [En línea]
Available at: https://es.wikipedia.org/wiki/Mootools
[Último acceso: Marzo 2017].
Páginas Web consultadas:
http://visjs.org/
https://www.digitalocean.com/community/tutorials/como-instalar-node-js-en-ubuntu-16-04-es
https://www.w3schools.com/
https://www.researchgate.net/figure/288975720_fig1_Figure-2-Pseudocode-of-the-fast-non-
dominated-sort-algorithm
http://smartcomments.github.io/
http://yui.github.io/yuidoc/args/index.html
https://www.digitalocean.com/community/tutorials/como-instalar-mongodb-en-ubuntu-16-04-es
https://www.researchgate.net/publication/277250496_A_multiobjective_non-
dominated_sorting_genetic_algorithm_NSGA-
II_for_the_Multiple_Traveling_Salesman_Problem
www.chartjs.org/
Javier Guzmán García jsEO 2: Uso y ampliación de una biblioteca de Algoritmos Bioinspirados escrita en JavaScript
Escuela Politécnica Superior de Jaén Página 111