anatomía de un algoritmo genético en jenes
TRANSCRIPT
Anatomía de un algoritmo genético en Jenes
Programación en Jenes es fácil, intuitiva y divertida. En esta sección, vamos a proporcionar algunos conceptos que hay que tener en cuenta al esbozar una solución en Jenes. Estos conceptos le ayudarán a moverse de manera efectiva entre las clases previstas en el API Jenes. Usted se sorprenderá de lo rápido que se puede definir un algoritmo genético, implementado y probado.
Los cromosomas, individuos y poblaciones.
La primera cosa a tener en cuenta es la diferencia entre los cromosomas, los individuos y las poblaciones.
Soluciones en un espacio de búsqueda se representan por los individuos. Están hechas de un cromosoma y de la aptitud de valor: INDIVIDUAL = CROMOSOMA + FITNESS.
Los cromosomas son las codificaciones de solución. En Jenes, se les considera como un conjunto de genes. Aquí está una lista de las clases de cromosomas proporcionadas por Jenes.
BitwiseChromosome: modelos de un cromosoma de bits. Su genoma contiene valores codificados otorgado a la codificación de bits especificado.
BooleanChromosome: modelos de un cromosoma de valores booleanos. Cada gen alelo puede ser verdadera o falsa.
DoubleChromosome: modelos de un cromosoma de valores dobles. Cada valor está en el rango [lowerBound, upperBound]. Estos límites se especifican en el momento de instancias.
IntegerChromosome: modelos de un cromosoma de valores enteros. Cada valor está en el rango [lowerBound, upperBound]. Estos límites se especifican en el momento de instancias.
ObjectChromosome: representa un cromosoma con genes que contiene un valor de alelo objeto.
PermutationChromosome: proporciona un cromosoma capaz de modelar permutaciones. Una matriz de valores enteros es su genoma. La propiedad más importante es que el genoma no contiene valores duplicados.
Todas las clases de cromosomas son la implementación de la interfaz del cromosoma. En general, los cromosomas se fija número de genes, pero su longitud puede variar durante la ejecución del algoritmo con el fin de implementar una codificación de longitud variable de solución. Una población es simplemente una colección de individuos (por ejemplo, soluciones).
Los individuos son todas las instancias de la clase paramétrica individual <T extiende Chromosome>, con el fin de tener un mayor control sobre los tipos. Un individuo puede ser legal o no legal. Es posible ajustar el lagality de un individuo mediante la invocación del método setLegal (boolen) en su instancia. Por defecto, cada individuo se fija te bo inicialmente legal.
Durante la evolución de algoritmos, las poblaciones del pasado se almacenan en la historia del algoritmo. En cada iteración de la evolución, la población más antigua y sus individuos se vuelven a utilizar. Las poblaciones del pasado, en lugar de ser cancelado la asignación, se mantenido en la memoria y volver a utilizar. Esta técnica evita la asignación en memoria de las nuevas poblaciones y limita el uso del recolector de basura.
Como se ha dicho, un algoritmo genético procesa una población de individuos. En cada generación hay poblaciones de entrada y salida. Es importante tener en cuenta que la población de salida se pre-inizialized con individuos "reciclados" tomados de la historia (por razones de rendimiento). Así Jenes generalmente no asigna nuevos individuos.
Estructura Algoritmo
Jenes proporciona una estructura flexible para la evolución de una población de individuos. En general, un algoritmo genético está estructurado como se muestra en la figura.
Básicamente, un algoritmo genético realiza el siguiente bucle:
Aleatoriamente crear una población inicial
repetición
Valorar la aptitud de cada individuo
Seleccione uno o más individuos de la población con una probabilidad basada en la aptitud para partecipate en las operaciones genéticas
Crear nuevos individuos mediante la aplicación de las operaciones genéticas con probabilidades especificadas
hasta que una solución aceptable se encuentra o se cumpla alguna otra condición parar
devolverle al individuo mejor tan lejos
En Jenes un algoritmo genético se puede implementar mediante el uso o la subclasificación GeneticAlgorithm y definir una implementación de funcion gimnasio subclasificando Fitness y la implementación del método abstracto evaluar (Individual). Este método se utiliza para evaluar la capacidad de un solo individuo. La implementación de este método está relacionada específicamente con el problema a resolver.
En Jenes cuerpo algoritmo genético es un "tubo" de etapas. Las etapas se ejecutan secuencialmente en el orden en que se agregan a la secuencia. Cada etapa recibe una población de entrada (producido como salida por la etapa anterior) y produce una población de salida. ¿Qué etapas y en qué orden para considerar que queda a las necesidades del algoritmo.
La referencia a la actual y en el proceso de llenado se puede respectivamente obtiene por la getCurrentPopulation métodos () y getNextPopulation ().
Jenes también permite definir etapas paralelas. Una etapa paralelo está formado por diferentes ramas, cada rama es una etapa que recibe una subpoblación de acuerdo con el dispensador de población utilizada. Un dispensador distribuye una población entre las ramas de una etapa en paralelo y se funde la salida de cada rama en la población de salida del paralelo.
El punto de quiebre y los operadores genéticos representan las etapas de tuberías generales más simples.
Una etapa BreakPoint notifica a sus oyentes cuando se invocated su método de proceso. No alterate la población de entrada.
Operadores genéticos utilizados en los algoritmos genéticos son análogos a éstos que se producen en el mundo natural:
Selección: la que da preferencia a los mejores individuos, que les permite pasar a la siguiente generación.
Crossover: lo que representa el apareamiento entre individuos.
Mutación: que introduce modificaciones aleatorias.
Jenes proporciona la implementación de los operadores genéticos más comunes:
TournamentSelector: choses a un número aleatorio de individuos de la población. Estos se comparan entre sí y el mejor de ellos es elegido para ser un padre. Selector torneo hace
asegurar que incluso los individuos de calidad media tienen alguna posibilidad de tener hijos. En Jenes es necesario especificar el número de intentos de torneo.
RouletteWheelSelector: coge un miembro particular de la población para ser un padre con una probabilidad igual a su condición física, dividido por el total de la aptitud de la población.
OnePointCrossover: elige al azar un punto de cruce de los cromosomas de los padres y crea una nueva descendencia. Uno de los puntos de cruce puede tener este aspecto (donde "|" es el punto de cruce):
Chromosome 1 11101 | 001000
Chromosome 2 00001 | 010101
Offspring 1 11101 | 010101
Offspring 2 00001 | 001000
TwoPointsCrossover: escoge al azar dos puntos de cruce de los cromosomas de los padres y crea una nueva descendencia. Dos puntos de cruce puede tener este aspecto:
Chromosome 1 11 | 10100 | 1000
Chromosome 2 00 | 00101 | 0101
Offspring 1 11 | 00101 | 1000
Offspring 2 00 | 10100 | 0101
En Jenes necesita especificar la probabilidad por la cual se utilizará el operador de cruce. En general, para un operador de cruce la probabilidad se establece en 80%
Mutador simple: choses al azar un punto de mutación y aleatoriza su gen. Operador de mutación también necesita un número probabilidad que especifica su uso.
En general, para un operador de mutación la probabilidad se establece en 20%.
Jenes proporciona también una interfaz sencilla para GeneticAlgorithm. Es SimpleGA clase que implementa un algoritmo genético tres etapas hecha de una herramienta de selección, uno de cruce y uno Mutador en secuencia.
Ofrece una forma sencilla de implementar su propio algoritmo genético que le permite descuidar los problemas relacionados con la construcción de los estadios.
La clase proporciona un conjunto de constructores por lo que crear una instancia de un algoritmo SimpleGA. Constructores permite decidir qué método de selección (es decir, Torneo o ruleta) o el método cruzado (es decir, un punto o dos puntos) a adoptar. También cruce y probabilidad de mutación puede ser especificado en el momento de la construcción. SimpleGA es una subclase GeneticAlgorithm.
Jenes incluye un soporte para el elitismo, es decir los mejores individuos de cada generación tienen la garantía de estar en la próxima generación. El número de individuos de la élite es fijado por el setElitism método (int).
Estos individuos se sustituyen a algunos individuos a la población procesada de acuerdo con las siguientes estrategias:
Random Estrategia Elitismo: próximos individuos de población son seleccionados al azar y se sustituye por la élite.
Peor Estrategia Elitismo: próximo de población peores individuos son sustituidos por la élite.
Usted puede configurar su estrategia de elitismo preferida de la siguiente manera:
sga.setElitismStrategy(ElitismStrategy.RANDOM); or sga.setElitismStrategy(ElitismStrategy.WORST);
La primera estrategia es más eficiente, ya que no requiere para ordenar la población. El inconveniente es que los individuos con una buena aptitud podrían ser sustituidos por la élite. La segunda estrategia es más lento, pero asegura que sólo los peores individuos están sustituidos.
Jenes utiliza el MersenneTwisterRandom clase para la generación de números y valores de pseudo-aleatorios. Este una clase optimizado que proporciona para la generación rápida de números aleatorios de muy alta calidad. Por esta razón, se utiliza en lugar de java.util.Random. La generación de números pseudoaleatorios es muy útil durante todas las fases de la evolución algoritmo (tales como la inicialización de la población, el uso de operadores de selección, la determinación de puntos de cruce y mutación, y así sucesivamente).
Eventos, oyentes y Estadísticas
La ejecución del algoritmo genético es invocado por el método de evolucionar (). La ejecución del algoritmo pasa a través de los siguientes eventos:
Inicio: la evolución algoritmo comienza.
Init: estructuras internas, como la población dada como entrada a la generación 0 y la historia, se inicializan.
Generación: una generación se ha acaba de realizar.
Stop: el algoritmo termina sus ejecuciones.
Cada uno de estos eventos pueden ser capturados por AlgorithmEventListeners y GenerationEventListeners. También pueden ser caputured por la subclase GeneticAlgorithm, reemplazando los métodos onStart (largos), onInit (largo), onGeneration (tiempo) y onStop (largo). La captura de eventos es útil para recopilar estadísticas y realizar análisis.
AlgorithmEventListeners proporciona la interfaz para la captura de eventos a nivel de algoritmo. Esta interfaz debe ser implementado por todas las clases interesadas en beign notificado por acontecimientos algoritmo (Start, inicio, parada). Un AlgorithmEventListener se puede registrar en el algoritmo por el método GeneticAlgorithm.addAlgorithmEventListener (AlgorithmEventListener). El oyente puede ser eliminado por la invocación del método GeneticAlgorithm.removeAlgorithmEventListener (AlgorithmEventListener).
GenerationEventListener es un oyente del evento de generación de algoritmo genético. Esta escucha se notifica una vez que se ejecuta una etapa de generación. A GenerationEventListener se puede registrar en el algoritmo por el método GeneticAlgorithm.addGenerationEventListener (GenerationEventListener). El oyente puede ser eliminado por la invocación del método GeneticAlgorithm.removeGenerationEventListener (GenerationEventListener).
Durante la evolución del algoritmo genético , Jenes recopila estadísticas sobre:
Ejecución de algoritmo: La clase GeneticAlgorithm.Statistics se encarga de almacenar información sobre el tiempo dedicado por todo el proceso evolutivo y el número de generaciones creadas . Puede invocar el método () GeneticAlgorithm.getStatistics para obtener las estadísticas de algoritmos .
Población: La clase Population.Statistics se encarga de almacenar información sobre una población. A medida que cada lata población contiene individuos legales e ilegales , mantiene los individuos con la aptitud mayor y menor , los valores medios y la desviación ambos consideran a las personas jurídicas y las ilegales . Puede invocar el método las Population.getStatistics () para obtener sus estadísticas .
Operadores: Las Selection.Statistics , Crossover.Statistics y Mutator.Statistics clases son responsables de almacenar información sobre los operadores utilizados y el tiempo empleado para su ejecución . Ellos sostienen respectivamente el número de cruce , selección y mutación realizado . Puede invocar el método () Operator.getStatistics en una instancia de la clase operador para obtener sus estadísticas.
Un problema boolean sencilla
Vamos a empezar con un problema sencillo. Dado un espacio de búsqueda hecha de variables booleanas, nuestro objetivo es encontrar una solución hecha de verdaderos valores (o valores falsos). Este problema es enfrentado por el diseño de un algoritmo genético de trabajo en los cromosomas booleanos. La función de aptitud se calcula contando el número de verdaderos valores dentro del cromosoma. Así que con el fin de encontrar la solución con todos los verdaderos valores podemos maximizar la función de aptitud, mientras que se minimiza la función de aptitud con el fin de encontrar una solución a base de todos los falsos valores.
Este tutorial te dará la oportunidad de entender cómo se estructura un algoritmo genético y taylored a un problema de optimización. Por otra parte, vamos a ver cómo los oyentes se pueden utilizar para capturar los eventos algoritmos.
1. Elegir un problema cromosómico adecuado y crear la población inicial.
La elección de una representación cromosómica adecuada es la tarea más importante que hacer antes de ejecutar un algoritmo genético. ¿Qué representación a utilizar depende de la estructura de las soluciones a los problemas. En nuestro caso, las soluciones están hechas de matrices booleanas. Por lo tanto, BooleanChromosome parece ser la opción approppriate. Los cromosomas representan el componente clave de las soluciones (es decir, individuos). Para la construcción de la población inicial que necesitamos un prototipo de soluciones (muestra), como se muestra en el siguiente código.
060 Individual<BooleanChromosome> sample = new Individual<BooleanChromosome>(new BooleanChromosome(CHROMOSOME_LENGTH));061 Population<BooleanChromosome> pop = new Population<BooleanChromosome>(sample, POPULATION_SIZE);
cuando
045 private static int POPULATION_SIZE=50;046 private static int CHROMOSOME_LENGTH=100;047 private static int GENERATION_LIMIT=1000;
2. Puesta en marcha del algoritmo genético.
Cualquier algoritmo en Jenes 2.0 se basa en GeneticAlgorithm, para wich debe definir una función de aptitud que se utiliza para evaluar a los individuos. Para definir una función de aptitud en Jenes 2.0 sólo tenemos que crear una subclase de fitness, una clase abstracta, la aplicación del método de evaluar (individual).
Cualquier algoritmo en Jenes se basa en GeneticAlgorithm, una clase abstracta cuyo único método es abstracto evaluateIndividual que es un problema dependiente. A continuación se muestra el código para crear una subclase de GeneticAlgorithm y evaluar a un individuo en nuestro problema.
Por ejemplo, podría ser fácil de definir una función de aptitud mediante el uso de una clase interna anónima como seguimiento:
063 Fitness<BooleanChromosome> fit = new Fitness<BooleanChromosome>(false) {064 065 @Override066 public void evaluate(Individual<BooleanChromosome> individual) {067 BooleanChromosome chrom = individual.getChromosome();068 int count = 0;069 int length=chrom.length();070 for(int i=0;i<length;i++)071 if(chrom.getValue(i))072 count++;073 074 individual.setScore(count);075 }076 077 };
Y luego pasar este objeto como primer parámetro de algoritmo genético (o cualquier subclase predefinido)
079 GeneticAlgorithm<BooleanChromosome> ga = new GeneticAlgorithm<BooleanChromosome>080 (fit, pop, GENERATION_LIMIT);
La clase de fitness ofrece dos constructores diferentes para definir el número de objetivos a considerar en la evaluación de las personas, y para cada objetivo de la bandera que indica si se trata de una función de maximizar o no. En el código que aparece más arriba, creamos un gimnasio para un solo problema objetivo (sólo una bandera como parámetros).
3. Elija los operadores a utilizar por el algoritmo genético y agregarlos como etapas en el ga.
Una vez definido el algoritmo genético, es necesario especificar la secuencia de la población operadores atravesará. El esquema más simple contiene sólo tres operadores en secuencia: un selector, una de cruce y uno de mutador. Sin embargo, es posible crear un
tubo más complejo que tiene paralléles y secuencias. A los efectos de este tutorial vamos a adoptar la siguiente estructura simple (ver APIs forman más detalles sobre los operadores adoptados):
082 AbstractStage<BooleanChromosome> selection = new TournamentSelector<BooleanChromosome>(3);083 AbstractStage<BooleanChromosome> crossover = new OnePointCrossover<BooleanChromosome>(0.8);084 AbstractStage<BooleanChromosome> mutation = new SimpleMutator<BooleanChromosome>(0.02);085 ga.addStage(selection);086 ga.addStage(crossover);087 ga.addStage(mutation);
4. Establezca los parámetros del algoritmo y ejecutar la evolución.
Es posible personalizar el ajuste del valor de elitismo algoritmo genético y el objetivo de optimización antes de ejecutar la evolución. El elitismo es el número de las mejores personas para celebrar en la próxima generación (1 en nuestro caso).
089 ga.setElitism(1);
Por último, podemos hacer que el algoritmo de funcionamiento.
091 ga.evolve();
5. Obtención del resultado de la evolución.
Jenes proporciona estadísticas tanto para el algoritmo y la población. La primera referencia a las estadísticas relativas a la ejecución de algoritmo, es decir, los tiempos de inicialización, comenzando, la evolución y las generaciones. El segundo a la distribución de soluciones y servicios conexos valores de fitness, como las personas ordenadas por la disminución de la función física, la media máxima, y mínima de los valores de fitness. Ellos se pueden recuperar en cualquier momento. Le llamaremos cuando el algoritmo ha terminado.
093 Population.Statistics stats = ga.getCurrentPopulation().getStatistics();094 GeneticAlgorithm.Statistics algostats = ga.getStatistics();095 096 System.out.println("Objective: " + (fit.getBiggerIsBetter()[0] ? "Max! (All true)" : "Min! (None true)"));097 System.out.println();098 099 Group legals = stats.getGroup(Population.LEGALS);100 101 Individual solution = legals.get(0);102 103 System.out.println("Solution: ");
104 System.out.println( solution );105 System.out.format("found in %d ms.\n", algostats.getExecutionTime() );106 System.out.println();107 108 Utils.printStatistics(stats);
Ejemplo completo:
package jenes.tutorials.problem1;
import jenes.Fitness;import jenes.GeneticAlgorithm;import jenes.chromosome.BooleanChromosome;import jenes.population.Individual;import jenes.population.Population;import jenes.population.Population.Statistics.Group;import jenes.stage.AbstractStage;import jenes.stage.operator.common.OnePointCrossover;import jenes.stage.operator.common.SimpleMutator;import jenes.stage.operator.common.TournamentSelector;import jenes.tutorials.utils.Utils;
public class BooleanProblem { private static int POPULATION_SIZE=50; private static int CHROMOSOME_LENGTH=100; private static int GENERATION_LIMIT=1000; public static void main(String[] args) throws Exception { Utils.printHeader(); System.out.println(); System.out.println("TUTORIAL 1:"); System.out.println("This algorithm aims to find a vector of booleans that is entirely true or false."); System.out.println(); // Random.getInstance().setStandardSeed(); Individual<BooleanChromosome> sample = new Individual<BooleanChromosome>(new BooleanChromosome(CHROMOSOME_LENGTH)); Population<BooleanChromosome> pop = new Population<BooleanChromosome>(sample, POPULATION_SIZE); Fitness<BooleanChromosome> fit = new Fitness<BooleanChromosome>(false) {
@Override public void evaluate(Individual<BooleanChromosome> individual) { BooleanChromosome chrom = individual.getChromosome(); int count = 0; int length=chrom.length(); for(int i=0;i<length;i++) if(chrom.getValue(i)) count++; individual.setScore(count); } }; GeneticAlgorithm<BooleanChromosome> ga = new GeneticAlgorithm<BooleanChromosome> (fit, pop, GENERATION_LIMIT); AbstractStage<BooleanChromosome> selection = new TournamentSelector<BooleanChromosome>(3); AbstractStage<BooleanChromosome> crossover = new OnePointCrossover<BooleanChromosome>(0.8); AbstractStage<BooleanChromosome> mutation = new SimpleMutator<BooleanChromosome>(0.02); ga.addStage(selection); ga.addStage(crossover); ga.addStage(mutation); ga.setElitism(1); ga.evolve(); Population.Statistics stats = ga.getCurrentPopulation().getStatistics(); GeneticAlgorithm.Statistics algostats = ga.getStatistics(); System.out.println("Objective: " + (fit.getBiggerIsBetter()[0] ? "Max! (All true)" : "Min! (None true)")); System.out.println(); Group legals = stats.getGroup(Population.LEGALS); Individual solution = legals.get(0); System.out.println("Solution: "); System.out.println( solution ); System.out.format("found in %d ms.\n", algostats.getExecutionTime() ); System.out.println(); Utils.printStatistics(stats); } }
Estructuración de un algoritmo genético avanzado
En nuestro segundo tutorial mostramos cómo crear un escenario paralelo y lo puso en la secuencia algoritmo, cómo distribuir y combinar estrategias paralelas, cómo establecer una condición de terminación definida por el usuario y cómo utilizar los oyentes de atrapar eventos algoritmo. El objetivo en este problema tutorial es para encontrar la matriz más cercano número entero (representable con un número especificado de cifras decimales) a una de destino con cada número dentro de la gama [0,49].
1. Elegir un problema cromosómico adecuado y crear la población inicial
Un IntegerChromosome es una representación natural de la matriz de enteros, cada valor gen se verá limitado en el rango [0,49] para respetar el problema de alcance restricción.
065 IntegerChromosome chrom = new IntegerChromosome(CHROMOSOME_LENGTH,0,MAX_INT);066 Individual<IntegerChromosome> ind = new Individual<IntegerChromosome>(chrom);
Donde MAX_INT esta definido como:
059 private static int MAX_INT = 49;
2. Puesta en marcha del algoritmo genético.
En este tutorial vamos a crear una subclase de GeneticAlogithm llamado PatternGA en wich personalizamos la condición de terminación del algoritmo ovverriding final () método que mejor se expone en el apartado 4.
La aptitud de cada individuo es el error absoluto entre su cromosoma y el cromosoma de destino. Por lo tanto, la mejor individuo es el uno con el error absoluto más pequeño. La función de aptitud se codifica en clase PatternFitness definida como clase interna en PatternGA. En este caso le ofrecemos una definición de un problema de un solo objetivo que ofrece al programador la posibilidad de configurar el esquema de solución esperada y la precisión mínima a alcanzar para detener la búsqueda.
El método de evaluar se mostró a continuación:
67 @Override68 public void evaluate(Individual<IntegerChromosome> individual) {69 IntegerChromosome chrom = individual.getChromosome();70 int diff = 0;71 int length = chrom.length();72 for (int i = 0; i < length; i++) {73 diff += Math.abs(chrom.getValue(i) - target[i]);74 }75 individual.setScore(diff);76 }
3 . Elija los operadores a utilizar por el algoritmo genético y agregarlos como etapas en la ga.
Supongamos que tenemos dos operadores de cruce diferentes (un punto con probabilidad 0,8 y dos puntos con una probabilidad de 0,5) para aplicarlos en dos subconjuntos de población separados. En Jenes podemos hacer esto a través del uso de la clase paralela. Por lo tanto organizamos los dos operadores de cruce como dos ramas paralelas de la tubería principal.
El dispensador distribuye la población entre las ramas de la etapa en paralelo y se funde la salida de cada rama en la población salida de la etapa paralela. En Jenes hay dos tipos de dispenses : el dispencer general y la dispencer exclusiva . El dispencer en general permite que , durante la operación de distribución, para agregar un individuo en más sucursales ( se requieren copias de la misma persona para evitar la superposición de referencias entre subpopolations sucursales ) . El dispencer exclusiva no lo permite: cada individuo se puede agregar en una sola rama (no es necesario añadir una copia de la misma). Elegimos utilizar un dispencer exclusiva. La aplicación se mostró a continuación.
43 public class SimpleDispenser<T extends Chromosome> extends ExclusiveDispenser<T> {44 45 private int count;46 47 public SimpleDispenser(int span) {48 super(span);49 }50 51 public void preDistribute(Population<T> population){52 this.count = 0;53 }54 55 @Override56 public int distribute(Individual<T> ind) {57 return count++ % span;58 }59 60 }
Esta implementación básica de un dispencer exclusiva utiliza una política de distribución simple. Esto pone a las personas en posiciones pares en la primera rama y los individuos en las posiciones impares en la rama otros. El método preDistribute es útil para restablecer el estado dispencer. El método de distribuir devuelve el índice de la sección donde poner el individuo especificado. No hay que preocuparse por la fusión de la salida de los manojos, ya que se lleva a cabo por la clase ExclusiveDispencer.
El código siguiente muestra cómo crear un escenario paralelo y la inserta en la tubería.
072 AbstractStage<IntegerChromosome> selection = new TournamentSelector<IntegerChromosome>(2);073 074 Parallel<IntegerChromosome> parallel = new Parallel<IntegerChromosome>(new SimpleDispenser<IntegerChromosome>(2));075 076 AbstractStage<IntegerChromosome> crossover1p = new OnePointCrossover<IntegerChromosome>(0.8);
077 parallel.add(crossover1p);078 079 AbstractStage<IntegerChromosome> crossover2p = new TwoPointsCrossover<IntegerChromosome>(0.5);080 parallel.add(crossover2p);081 082 AbstractStage<IntegerChromosome> mutation = new SimpleMutator<IntegerChromosome>(0.02);083 084 algorithm.addStage(selection);085 algorithm.addStage(parallel);086 algorithm.addStage(mutation);
3. Personalizar el algoritmo genético.
Como se dijo antes la meta de nuestro algoritmo genético es minimizar el error absoluto de los individuos generados, con el fin de obtener una matriz de enteros, tan pronto como sea posible cerca del destino.
En Jenes la evolución del algoritmo genético se ejecuta hasta que alcance el límite de generación especificada o hasta que se cumpla un criterio de terminación. Para especificar el criterio de terminación nuestro algoritmo genético tiene que reemplazar el método "extremo". Por defecto, este método tiene un cuerpo vacío por lo que no tiene ningún efecto sobre la evolución del algoritmo. En nuestro ejemplo, la evolución se detiene cuando el valor de aptitud más bajo es inferior a un valor (denominado precisión) especificado por el usuario.
52 @Override53 protected boolean end() {54 jenes.population.Population.Statistics stat = this.getCurrentPopulation(). getStatistics();55 return stat.getGroup(Population.LEGALS).getMin()[0] <= this.fitness.precision;56 }
La clase GeneticAlgorithm implementa el patrón Observer para notificar a sus oyentes la ocurrencia de un evento en particular. En Jenes hay dos tipos de detectores de eventos: el detector de eventos de generación (invocado cuando finaliza una generación) y el detector de eventos algoritmo (Se invoca cuando un evento occours algoritmo, por ejemplo, el inicio, el final y el inicio del algoritmo).
En este tutorial vamos optar por utilizar un detector de eventos de generación con el fin de imprimir algunas informaciones al final de cada generación. El código de oyente se mostró a continuación.
113 public void onGeneration(GeneticAlgorithm ga, long time) {114 Statistics stat = ga.getCurrentPopulation().getStatistics();
115 Group legals = stat.getGroup(Population.LEGALS);116 System.out.println("Current generation: " + ga.getGeneration());117 System.out.println("\tBest score: " + legals.getMin()[0]);118 System.out.println("\tAvg score : " + legals.getMean()[0]);119 }
El código para añadir un detector de eventos generación en la clase Algoritmo Genético es:
087 algorithm.addGenerationEventListener(this);
En el código siguiente se resumen los principales pasos para la configuración del algoritmo genético.
65 IntegerChromosome chrom = new IntegerChromosome(CHROMOSOME_LENGTH,0,MAX_INT);066 Individual<IntegerChromosome> ind = new Individual<IntegerChromosome>(chrom);067 Population<IntegerChromosome> pop = new Population<IntegerChromosome>(ind, POPULATION_SIZE);068 069 algorithm = new PatternGA(pop, GENERATION_LIMIT);070 algorithm.setElitism(5);071 072 AbstractStage<IntegerChromosome> selection = new TournamentSelector<IntegerChromosome>(2);073 074 Parallel<IntegerChromosome> parallel = new Parallel<IntegerChromosome>(new SimpleDispenser<IntegerChromosome>(2));075 076 AbstractStage<IntegerChromosome> crossover1p = new OnePointCrossover<IntegerChromosome>(0.8);077 parallel.add(crossover1p);078 079 AbstractStage<IntegerChromosome> crossover2p = new TwoPointsCrossover<IntegerChromosome>(0.5);080 parallel.add(crossover2p);081 082 AbstractStage<IntegerChromosome> mutation = new SimpleMutator<IntegerChromosome>(0.02);
083 084 algorithm.addStage(selection);085 algorithm.addStage(parallel);086 algorithm.addStage(mutation);087 algorithm.addGenerationEventListener(this);
Ejemplo1:
package jenes.tutorials.problem2;020 021 import jenes.GenerationEventListener;022 import jenes.GeneticAlgorithm;023 import jenes.utils.Random;024 import jenes.chromosome.IntegerChromosome;
025 import jenes.population.Individual;026 import jenes.population.Population;027 import jenes.population.Population.Statistics;028 import jenes.population.Population.Statistics.Group;029 import jenes.stage.AbstractStage;030 import jenes.stage.Parallel;031 import jenes.stage.operator.common.OnePointCrossover;032 import jenes.stage.operator.common.SimpleMutator;033 import jenes.stage.operator.common.TournamentSelector;034 import jenes.stage.operator.common.TwoPointsCrossover;035 import jenes.tutorials.utils.Utils;
054 public class PatternProblem implements GenerationEventListener<IntegerChromosome> {055 056 private static int POPULATION_SIZE = 100;057 private static int CHROMOSOME_LENGTH = 10;058 private static int GENERATION_LIMIT = 1000;059 private static int MAX_INT = 49;060 061 private PatternGA algorithm = null;062 063 public PatternProblem() {064 065 IntegerChromosome chrom = new IntegerChromosome(CHROMOSOME_LENGTH,0,MAX_INT);066 Individual<IntegerChromosome> ind = new Individual<IntegerChromosome>(chrom);067 Population<IntegerChromosome> pop = new Population<IntegerChromosome>(ind, POPULATION_SIZE);068 069 algorithm = new PatternGA(pop, GENERATION_LIMIT);070 algorithm.setElitism(5);071 072 AbstractStage<IntegerChromosome> selection = new TournamentSelector<IntegerChromosome>(2);073 074 Parallel<IntegerChromosome> parallel = new Parallel<IntegerChromosome>(new SimpleDispenser<IntegerChromosome>(2));075 076 AbstractStage<IntegerChromosome> crossover1p = new OnePointCrossover<IntegerChromosome>(0.8);077 parallel.add(crossover1p);078 079 AbstractStage<IntegerChromosome> crossover2p = new TwoPointsCrossover<IntegerChromosome>(0.5);080 parallel.add(crossover2p);081 082 AbstractStage<IntegerChromosome> mutation = new SimpleMutator<IntegerChromosome>(0.02);083 084 algorithm.addStage(selection);085 algorithm.addStage(parallel);
086 algorithm.addStage(mutation);087 algorithm.addGenerationEventListener(this);088 }089 090 public void run(int[] target, int precision) {091 ((PatternGA.PatternFitness) algorithm.getFitness()).setTarget(target);092 ((PatternGA.PatternFitness) algorithm.getFitness()).setPrecision(precision);093 algorithm.evolve();094 095 Population.Statistics stats = algorithm.getCurrentPopulation().getStatistics();096 GeneticAlgorithm.Statistics algostats = algorithm.getStatistics();097 098 System.out.println();099 System.out.print("Target:[");100 for( int i = 0; i < target.length; ++i ) {101 System.out.print(target[i]+ ( i < target.length - 1 ? " " : ""));102 }103 System.out.println("]");104 System.out.println();105 106 System.out.println("Solution: ");107 System.out.println(stats.getGroup(Population.LEGALS).get(0));108 System.out.format("found in %d ms and %d generations.\n", algostats.getExecutionTime(), algostats.getGenerations() );109 System.out.println();110 }111 112 113 public void onGeneration(GeneticAlgorithm ga, long time) {114 Statistics stat = ga.getCurrentPopulation().getStatistics();115 Group legals = stat.getGroup(Population.LEGALS);116 System.out.println("Current generation: " + ga.getGeneration());117 System.out.println("\tBest score: " + legals.getMin()[0]);118 System.out.println("\tAvg score : " + legals.getMean()[0]);119 }120 121 private static void randomize(int[] sample) {122 for(int i=0;i<sample.length;i++)123 sample[i] = Random.getInstance().nextInt(0, MAX_INT+1);124 }125 126 public static void main(String[] args) {127 128 Utils.printHeader();129 System.out.println();130 131 System.out.println("TUTORIAL 2:");132 System.out.println("This algorithm aims to autonomously find a vector of integers that best matches with a target vector.");
133 System.out.println();134 135 Random.getInstance().setStandardSeed();136 137 PatternProblem problem = new PatternProblem();138 int[] target = new int[CHROMOSOME_LENGTH];139 140 randomize(target);141 problem.run(target, 2);142 143 randomize(target);144 problem.run(target, 0);145 146 }147 148 }
Ejemplo2:
19 package jenes.tutorials.problem2;20 21 import jenes.chromosome.Chromosome;22 import jenes.population.Individual;23 import jenes.population.Population;24 import jenes.stage.ExclusiveDispenser;
43 public class SimpleDispenser<T extends Chromosome> extends ExclusiveDispenser<T> {44 45 private int count;46 47 public SimpleDispenser(int span) {48 super(span);49 }50 51 public void preDistribute(Population<T> population){52 this.count = 0;53 }54 55 @Override56 public int distribute(Individual<T> ind) {57 return count++ % span;58 }59 60 }
Ejemplo3:
19 package jenes.tutorials.problem2;20 21 import jenes.Fitness;
22 import jenes.GeneticAlgorithm;23 import jenes.chromosome.IntegerChromosome;24 import jenes.population.Individual;25 import jenes.population.Population;
43 public class PatternGA extends GeneticAlgorithm<IntegerChromosome> {44 45 private PatternFitness fitness = new PatternFitness();46 47 public PatternGA(Population<IntegerChromosome> pop, int numGen) {48 super(pop, numGen);49 this.setFitness(fitness);50 }51 52 @Override53 protected boolean end() {54 jenes.population.Population.Statistics stat = this.getCurrentPopulation().getStatistics();55 return stat.getGroup(Population.LEGALS).getMin()[0] <= this.fitness.precision;56 }57 58 public class PatternFitness extends Fitness<IntegerChromosome> {59 60 private int[] target = null;61 private int precision = 0;62 63 private PatternFitness() {64 super(false);65 }66 67 @Override68 public void evaluate(Individual<IntegerChromosome> individual) {69 IntegerChromosome chrom = individual.getChromosome();70 int diff = 0;71 int length = chrom.length();72 for (int i = 0; i < length; i++) {73 diff += Math.abs(chrom.getValue(i) - target[i]);74 }75 individual.setScore(diff);76 }77 78 public void setTarget(int[] target) {79 this.target = target;80 }81 82 public void setPrecision(int precision) {83 this.precision = precision;84 }85 }86 }
Redefinición de las operaciones genéticas
En algunas circunstancias el espacio de búsqueda es escasa dentro de un espacio más grande. Este es el caso del problema del agente de viajes, con el objetivo de encontrar una mínima de enrutamiento entre un conjunto de puntos, teniendo en cuenta las distancias entre ellos.
Una forma natural para la representación de una solución es por una permutación de elementos, proporcionando el orden por el que la mínima de enrutamiento visita los puntos. Por lo tanto, un cromosoma es generalmente de Indices de enteros, cada referencia a un punto en particular (ciudad). Los operadores tradicionales de cruce y mutación no aseguran para producir soluciones válidas, es decir, permutaciones, ya que algunas ciudades podrían reproducirse o falta. Hay una necesidad para la redefinición de estas operaciones. En este tutorial se muestran dos formas de realizar esta tarea.
1. El uso o la creación de una clase cromosoma específico2. Redefinir el cruce y mutador
1. El uso o la creación de una clase cromosoma específico.
En Jenes, los operadores genéticos se basan en los primitivos puestos a disposición por la interfaz de cromosomas, tales como cruz, de forma aleatoria, de intercambio y desplazamiento. Por lo tanto, es posible también definir una subclase cromosoma específico con un conjunto de primitivas evitando transformaciones que conducen a soluciones no factibles.
En Jenes la PermutationChromosome es capaz de procesar permutaciones de acuerdo a la siguiente Stategy:
Crossover: alelos en un cromosoma se ordenarán de acuerdo con el orden en que son considerados por el otro cromosoma
Mutación: un alelo se intercambia con otro elegido al azar
El código de código utilizado para configurar el algoritmo y la función de aptitud se mostró a continuación:
127 Individual<PermutationChromosome> sample = new Individual<PermutationChromosome>(new PermutationChromosome(cities));128 Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample, POPULATION_SIZE);129 130 Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {131 132 @Override133 public void evaluate(Individual<PermutationChromosome> individual) {134 PermutationChromosome chrom = individual.getChromosome();135 double count = 0;136 int size = chrom.length();137 for(int i=0;i<size-1;i++){138 int val1 = chrom.getElementAt(i);139 int val2 = chrom.getElementAt(i+1);140 count += TravelSalesmanProblem.this.map[val1][val2];141 }142 count += TravelSalesmanProblem.this.map[size-1][0];143 individual.setScore(count);144 }145 146 };147 148 SimpleGA<PermutationChromosome> sga = new SimpleGA<PermutationChromosome>(fitness, pop, GENERATION_LIMIT);
2. Redefinir el cruce y mutador
Crossover
Redefinición de un crossover es bastante simple. Lo primero que debe hacer es extender la clase del cromosoma.
057 public class TSPCityCenteredCrossover extends Crossover<IntegerChromosome>{
El Crossover Ciudad Centrado, elige una ciudad y volver a ordenar las siguientes ciudades en un cromosoma de acuerdo a la orden dada por el otro. Esta operación se realiza mediante la aplicación del método de cruz.
078 protected void cross(Individual<IntegerChromosome> offsprings[]) {079 080 IntegerChromosome chrom_child1 = offsprings[0].getChromosome();081 IntegerChromosome chrom_child2 = offsprings[1].getChromosome();082 083 if( chrom_parent1 == null ) {084 chrom_parent1 = chrom_child1.clone();085 chrom_parent2 = chrom_child2.clone();086 } else {087 chrom_parent1.setAs(chrom_child1);088 chrom_parent2.setAs(chrom_child2);089 }090 091 final int size = chrom_child1.length();092 if( chrom_child2.length() != size )093 throw new AlgorithmException("Error: the two chromosomes are required to have the same length.");094 095 096 //we choose a random city097 int city = Random.getInstance().nextInt(0, size);098 099 //i1, i2 are the positions of the city respectively in child1 and child2100 int i1 = findPositionOf(city, chrom_child1);101 int i2 = findPositionOf(city, chrom_child2);102 103 int j1 = 0;104 int j2 = 0;105 for( int i = 0; i < size; ++i ) {106 // get the city c1 in position i for parent1107 int c1 = chrom_parent1.getValue(i);108 // find the position of c1 in parent 2109 int p2 = findPositionOf(c1, chrom_parent2);110 // if the position is over the cross-point, it copies c1 in child2111 if( p2 > i2 ) {112 chrom_child2.setValue(i2 + (++j2), c1);113 }114 115 // similarly we process the other pair116 int c2 = chrom_parent2.getValue(i);117 int p1 = findPositionOf(c2, chrom_parent1);118 if( p1 > i1 ) {119 chrom_child1.setValue(i1 + (++j1), c2);120 }121 }122 }
Mutador
Redefinir el mutador es sencillo también. En primer lugar, tenemos que crear una subclase de la operadora Mutador.
047 public class TSPScrambleMutator extends Mutator<IntegerChromosome> {
El mutador Scramble se basa en la siguiente estrategia: dos Indices de i1 e i2 se eligen al azar y el orden de los elementos dentro de la gama [i1, i2] cambia aleatoriamente.
Este algoritmo es ejecutado por el método randomize:
081 public void randomize(IntegerChromosome chrom, int min, int max) {082 083 //we create a temporany array084 int len = max-min+1;085 int[] base = new int[len];086 087 //we fill it with the elements within [min,max]088 for(int i=0;i<len;i++)089 base[i]= chrom.getValue(min+i);090 091 //the loop ends when the temporany array is empty092 for( int i = 0; len > 0; --len, ++i) {093 //we choose a random position pos in the array and copy the element at pos in the chromosome094 int pos = Random.getInstance().nextInt(0,len);095 chrom.setValue(min+i,base[pos]);096 //we removes the chosen element from the temporany array097 for(int j=pos;j<(len-1);j++){098 base[j]=base[j+1];099 }100 }101 }
Ejemplo del tema:
019 package jenes.tutorials.problem3;020 021 import jenes.Fitness;022 import jenes.GeneticAlgorithm;023 import jenes.utils.Random;024 import jenes.algorithms.SimpleGA;025 import jenes.chromosome.IntegerChromosome;026 import jenes.chromosome.PermutationChromosome;027 import jenes.population.Individual;028 import jenes.population.Population;
029 import jenes.population.Population.Statistics.Group;030 import jenes.stage.AbstractStage;031 import jenes.stage.operator.common.TournamentSelector;032 import jenes.tutorials.utils.Utils;
046 public class TravelSalesmanProblem {047 048 public static final int POPULATION_SIZE = 1000;049 private static int GENERATION_LIMIT = 2000;050 public static final int MAX_DISTANCE = 10;051 052 private TSPGA algorithm;053 private int cities;054 private double[][] map;055 056 public static void main(String[] args) {057 058 Utils.printHeader();059 System.out.println();060 061 System.out.println("TUTORIAL 3:");062 System.out.println("The Travel Salesman Problem, a classics.");063 System.out.println();064 065 Random.getInstance().setStandardSeed();066 067 System.out.println("Case 1: 10 cities in circle");068 double[][] m1 = simpleMap(10);069 TravelSalesmanProblem tsp1 = new TravelSalesmanProblem(m1);070 tsp1.solve();071 072 System.out.println("Case 2: 30 cities in circle");073 double[][] m2 = simpleMap(30);074 TravelSalesmanProblem tsp2 = new TravelSalesmanProblem(m2);075 tsp2.solve();076 077 System.out.println("Case 3: 30 cities at random");078 double[][] m3 = randomMap(30);079 TravelSalesmanProblem tsp3 = new TravelSalesmanProblem(m3);080 tsp3.solve();081 082 System.out.println("Case 4: An application of PermutationChromosome");083 tsp2.solvePC();084 }085 086 public TravelSalesmanProblem(double[][] matrix) {087 088 cities = matrix[0].length;089 map = matrix;
090 091 IntegerChromosome chrom = new IntegerChromosome(cities,0,cities-1);092 for( int i = 0; i < cities; ++i ) {093 chrom.setValue(i, i < cities - 1 ? i+1 : 0);094 }095 Individual<IntegerChromosome> sample = new Individual<IntegerChromosome>(chrom);096 Population<IntegerChromosome> pop = new Population<IntegerChromosome>(sample, POPULATION_SIZE);097 098 algorithm = new TSPGA(matrix, pop, GENERATION_LIMIT);099 algorithm.setRandomization(true);100 101 AbstractStage<IntegerChromosome> selection = new TournamentSelector<IntegerChromosome>(1);102 AbstractStage<IntegerChromosome> crossover = new TSPCityCenteredCrossover(0.8);103 AbstractStage<IntegerChromosome> mutation = new TSPScrambleMutator(0.02);104 105 algorithm.addStage(selection);106 algorithm.addStage(crossover);107 algorithm.addStage(mutation);108 109 algorithm.setElitism(10);110 }111 112 public void solve() {113 algorithm.evolve();114 115 Population.Statistics stats = algorithm.getCurrentPopulation().getStatistics();116 GeneticAlgorithm.Statistics algostats = algorithm.getStatistics();117 118 Group legals = stats.getGroup(Population.LEGALS);119 120 System.out.println(legals.get(0));121 System.out.format("found in %d ms and %d generations.\n", algostats.getExecutionTime(), algostats.getGenerations() );122 System.out.println();123 }124 125 public void solvePC() {126 127 Individual<PermutationChromosome> sample = new Individual<PermutationChromosome>(new PermutationChromosome(cities));128 Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample, POPULATION_SIZE);129 130 Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {131 132 @Override133 public void evaluate(Individual<PermutationChromosome> individual) {
134 PermutationChromosome chrom = individual.getChromosome();135 double count = 0;136 int size = chrom.length();137 for(int i=0;i<size-1;i++){138 int val1 = chrom.getElementAt(i);139 int val2 = chrom.getElementAt(i+1);140 count += TravelSalesmanProblem.this.map[val1][val2];141 }142 count += TravelSalesmanProblem.this.map[size-1][0];143 individual.setScore(count);144 }145 146 };147 148 SimpleGA<PermutationChromosome> sga = new SimpleGA<PermutationChromosome>(fitness, pop, GENERATION_LIMIT);149 150 sga.setElitism(10);151 sga.setMutationProbability(0.02);152 sga.evolve();153 154 Population.Statistics stats = sga.getCurrentPopulation().getStatistics();155 GeneticAlgorithm.Statistics algostats = sga.getStatistics();156 157 Group legals = stats.getGroup(Population.LEGALS);158 159 System.out.println(legals.get(0));160 System.out.format("found in %d ms and %d generations.\n", algostats.getExecutionTime(), algostats.getGenerations() );161 System.out.println();162 }163 164 public static double[][] simpleMap( int cities ) {165 double[][] matrix = new double[cities][cities];166 167 matrix[0][0] = 0;168 for( int i = 1; i <= cities/2; ++i) {169 matrix[0][i] = i;170 matrix[0][cities-i] = i;171 }172 173 for( int i = 1; i < cities; ++i ) {174 for( int j = 0; j < cities; ++j ) {175 matrix[i][(i+j)%cities] = matrix[0][j];176 }177 }178 return matrix;179 }180
181 public static double[][] randomMap( int cities ) {182 double[][] matrix = new double[cities][cities];183 for( int i = 0; i < cities; ++i ) {184 for( int j = 0; j < cities; ++j ) {185 matrix[i][j] = i!=j ? Random.getInstance().nextDouble(MAX_DISTANCE) : 0;186 }187 }188 return matrix;189 }190 191 }
Ejemplo del tema:
019 package jenes.tutorials.problem3;020 021 import jenes.utils.Random;022 import jenes.chromosome.IntegerChromosome;023 import jenes.population.Individual;024 import jenes.stage.operator.Mutator;
047 public class TSPScrambleMutator extends Mutator<IntegerChromosome> {048 049 public TSPScrambleMutator(double pMut) {050 super(pMut);051 }052 053 @Override054 protected void mutate(Individual<IntegerChromosome> t) {055 int size = t.getChromosomeLength();056 int index1,index2;057 do{058 index1 = Random.getInstance().nextInt(0,size);059 index2 = Random.getInstance().nextInt(0,size);060 }while(index2==index1);061 062 int min,max;063 if(index1<index2){064 min=index1;065 max=index2;066 }else{067 min=index2;068 max=index1;069 }070 071 randomize(t.getChromosome(),min, max);072 }073 074 /**
075 * Randomizes the elements chromosome within the range [min,max]076 * <p>077 * @param chrom the individual to mutate078 * @param min the lower bound079 * @param max the upper bound080 */081 public void randomize(IntegerChromosome chrom, int min, int max) {082 083 //we create a temporany array084 int len = max-min+1;085 int[] base = new int[len];086 087 //we fill it with the elements within [min,max]088 for(int i=0;i<len;i++)089 base[i]= chrom.getValue(min+i);090 091 //the loop ends when the temporany array is empty092 for( int i = 0; len > 0; --len, ++i) {093 //we choose a random position pos in the array and copy the element at pos in the chromosome094 int pos = Random.getInstance().nextInt(0,len);095 chrom.setValue(min+i,base[pos]);096 //we removes the chosen element from the temporany array097 for(int j=pos;j<(len-1);j++){098 base[j]=base[j+1];099 }100 }101 }102 }
Ejemplo del tema:
package jenes.tutorials.problem3;
import jenes.Fitness;import jenes.GeneticAlgorithm;import jenes.utils.Random;import jenes.chromosome.IntegerChromosome;import jenes.population.Individual;import jenes.population.Population;
public class TSPGA extends GeneticAlgorithm<IntegerChromosome> { private double[][] matrix; private TSPFitness fitness; public TSPGA(double[][] matrix, Population<IntegerChromosome> pop, int genlimit) { super(null, pop, genlimit); this.matrix = matrix; fitness = new TSPFitness();
this.setFitness(fitness); } @Override protected void randomizeIndividual(Individual<IntegerChromosome> individual) { Random rand = Random.getInstance(); int len = individual.getChromosomeLength(); for( int i = 0; i < 100; ++i ) { int j = rand.nextInt(len); int k = rand.nextInt(len); individual.getChromosome().swap(j, k); } }
public class TSPFitness extends Fitness<IntegerChromosome> {
public TSPFitness() { super(false); }
@Override public void evaluate(Individual<IntegerChromosome> individual) { IntegerChromosome chrom = individual.getChromosome(); double count = 0; int size = chrom.length(); for (int i = 0; i < size - 1; i++) { int val1 = chrom.getValue(i); int val2 = chrom.getValue(i + 1); count += matrix[val1][val2]; } count += matrix[size - 1][0];
individual.setScore(count); } } }
Ejemplo del tema:
019 package jenes.tutorials.problem3;020 021 import jenes.AlgorithmException;022 import jenes.utils.Random;023 import jenes.chromosome.IntegerChromosome;024 import jenes.population.Individual;025 import jenes.stage.operator.Crossover;
057 public class TSPCityCenteredCrossover extends Crossover<IntegerChromosome>{058 059 public TSPCityCenteredCrossover(double pCross) {060 super(pCross);061 }062 063 /**064 * Returns the number of chromosomes (i.e. 2) this operator entails.065 */066 @Override067 public int spread() {068 return 2;069 }070 071 private IntegerChromosome chrom_parent1 = null;072 private IntegerChromosome chrom_parent2 = null;073 /**074 * This method implements the crossover operation.075 *076 * @param offsprings the chromosomes to be crossed.077 */078 protected void cross(Individual<IntegerChromosome> offsprings[]) {079 080 IntegerChromosome chrom_child1 = offsprings[0].getChromosome();081 IntegerChromosome chrom_child2 = offsprings[1].getChromosome();082 083 if( chrom_parent1 == null ) {084 chrom_parent1 = chrom_child1.clone();085 chrom_parent2 = chrom_child2.clone();086 } else {087 chrom_parent1.setAs(chrom_child1);088 chrom_parent2.setAs(chrom_child2);089 }090 091 final int size = chrom_child1.length();092 if( chrom_child2.length() != size )093 throw new AlgorithmException("Error: the two chromosomes are required to have the same length.");094 095 096 //we choose a random city097 int city = Random.getInstance().nextInt(0, size);098 099 //i1, i2 are the positions of the city respectively in child1 and child2100 int i1 = findPositionOf(city, chrom_child1);101 int i2 = findPositionOf(city, chrom_child2);102 103 int j1 = 0;104 int j2 = 0;
105 for( int i = 0; i < size; ++i ) {106 // get the city c1 in position i for parent1107 int c1 = chrom_parent1.getValue(i);108 // find the position of c1 in parent 2109 int p2 = findPositionOf(c1, chrom_parent2);110 // if the position is over the cross-point, it copies c1 in child2111 if( p2 > i2 ) {112 chrom_child2.setValue(i2 + (++j2), c1);113 }114 115 // similarly we process the other pair116 int c2 = chrom_parent2.getValue(i);117 int p1 = findPositionOf(c2, chrom_parent1);118 if( p1 > i1 ) {119 chrom_child1.setValue(i1 + (++j1), c2);120 }121 }122 }123 124 /**125 * Finds the position of one specific city in the chromosome.126 * <p>127 * @param city the city to find128 * @param chrom the chromosome to search129 * @return the city position130 */131 private int findPositionOf(int city, IntegerChromosome chrom){132 final int size = chrom.length();133 for( int i = 0; i < size; ++i ) {134 if( chrom.getValue(i) == city )135 return i;136 }137 return -1;138 }139 140 }
Redefinición de las operaciones genéticas
En algunas circunstancias el espacio de búsqueda es escasa dentro de un espacio más grande. Este es el caso del problema del agente de viajes, con el objetivo de encontrar una
mínima de enrutamiento entre un conjunto de puntos, teniendo en cuenta las distancias entre ellos.
Una forma natural para la representación de una solución es por una permutación de elementos, proporcionando el orden por el que la mínima de enrutamiento visita los puntos. Por lo tanto, un cromosoma es generalmente de Indices de enteros, cada referencia a un punto en particular (ciudad). Los operadores tradicionales de cruce y mutación no aseguran para producir soluciones válidas, es decir, permutaciones, ya que algunas ciudades podrían reproducirse o falta. Hay una necesidad para la redefinición de estas operaciones. En este tutorial se muestran dos formas de realizar esta tarea.
1. El uso o la creación de una clase cromosoma específico2. Redefinir el cruce y matador
1. El uso o la creación de una clase cromosoma específico
En Jenes, los operadores genéticos se basan en los primitivos puestos a disposición por la interfaz de cromosomas, tales como cruz, de forma aleatoria, de intercambio y desplazamiento. Por lo tanto, es posible también definir una subclase cromosoma específico con un conjunto de primitivas evitando transformaciones que conducen a soluciones no factibles.
En Jenes la PermutationChromosome es capaz de procesar permutaciones de acuerdo a la siguiente Stategy:
Crossover: alelos en un cromosoma se ordenarán de acuerdo con el orden en que son considerados por el otro cromosoma
Mutación: un alelo se intercambia con otro elegido al azar
El código de código utilizado para configurar el algoritmo y la función de aptitud se mostró a continuación:
127 Individual<PermutationChromosome> sample = new Individual<PermutationChromosome>(new PermutationChromosome(cities));128 Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample, POPULATION_SIZE);129 130 Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {131 132 @Override133 public void evaluate(Individual<PermutationChromosome> individual) {
134 PermutationChromosome chrom = individual.getChromosome();135 double count = 0;136 int size = chrom.length();137 for(int i=0;i<size-1;i++){138 int val1 = chrom.getElementAt(i);139 int val2 = chrom.getElementAt(i+1);140 count += TravelSalesmanProblem.this.map[val1][val2];141 }142 count += TravelSalesmanProblem.this.map[size-1][0];143 individual.setScore(count);144 }145 146 };147 148 SimpleGA<PermutationChromosome> sga = new SimpleGA<PermutationChromosome>(fitness, pop, GENERATION_LIMIT);
2. Redefinir el cruce y mutador
crossover
Redefinición de un crossover es bastante simple. Lo primero que debe hacer es extender la clase del cromosoma.
057 public class TSPCityCenteredCrossover extends Crossover<IntegerChromosome>{
El Crossover Ciudad Centrado, elige una ciudad y volver a ordenar las siguientes ciudades en un cromosoma de acuerdo a la orden dada por el otro. Esta operación se realiza mediante la aplicación del método de cruz.
078 protected void cross(Individual<IntegerChromosome> offsprings[]) {079 080 IntegerChromosome chrom_child1 = offsprings[0].getChromosome();081 IntegerChromosome chrom_child2 = offsprings[1].getChromosome();082 083 if( chrom_parent1 == null ) {084 chrom_parent1 = chrom_child1.clone();085 chrom_parent2 = chrom_child2.clone();086 } else {087 chrom_parent1.setAs(chrom_child1);088 chrom_parent2.setAs(chrom_child2);089 }090 091 final int size = chrom_child1.length();092 if( chrom_child2.length() != size )
093 throw new AlgorithmException("Error: the two chromosomes are required to have the same length.");094 095 096 //we choose a random city097 int city = Random.getInstance().nextInt(0, size);098 099 //i1, i2 are the positions of the city respectively in child1 and child2100 int i1 = findPositionOf(city, chrom_child1);101 int i2 = findPositionOf(city, chrom_child2);102 103 int j1 = 0;104 int j2 = 0;105 for( int i = 0; i < size; ++i ) {106 // get the city c1 in position i for parent1107 int c1 = chrom_parent1.getValue(i);108 // find the position of c1 in parent 2109 int p2 = findPositionOf(c1, chrom_parent2);110 // if the position is over the cross-point, it copies c1 in child2111 if( p2 > i2 ) {112 chrom_child2.setValue(i2 + (++j2), c1);113 }114 115 // similarly we process the other pair116 int c2 = chrom_parent2.getValue(i);117 int p1 = findPositionOf(c2, chrom_parent1);118 if( p1 > i1 ) {119 chrom_child1.setValue(i1 + (++j1), c2);120 }121 }122 }
mutador
Redefinir el mutador es sencillo también. En primer lugar, tenemos que crear una subclase de la operadora Mutador.
047 public class TSPScrambleMutator extends Mutator<IntegerChromosome> {
El mutador Scramble se basa en la siguiente estrategia: dos Indices de i1 e i2 se eligen al azar y el orden de los elementos dentro de la gama [i1, i2] cambia aleatoriamente.
Este algoritmo es ejecutado por el método randomize.
081 public void randomize(IntegerChromosome chrom, int min, int max) {082 083 //we create a temporany array
084 int len = max-min+1;085 int[] base = new int[len];086 087 //we fill it with the elements within [min,max]088 for(int i=0;i<len;i++)089 base[i]= chrom.getValue(min+i);090 091 //the loop ends when the temporany array is empty092 for( int i = 0; len > 0; --len, ++i) {093 //we choose a random position pos in the array and copy the element at pos in the chromosome094 int pos = Random.getInstance().nextInt(0,len);095 chrom.setValue(min+i,base[pos]);096 //we removes the chosen element from the temporany array097 for(int j=pos;j<(len-1);j++){098 base[j]=base[j+1];099 }100 }101 }
Cómo utilizar SimpleGa
En este tutorial se muestra cómo utilizar SimpleGA, una subclase estándar GeneticAlgorithm. Representa la configuración clásica de un algoritmo genético, utilizando sólo los principales operadores (selectores, cruce y mutantes) en un tubo muy simple. Es útil cuando no se necesitan operadores de complejos y se requiere una ga rápida puesta en marcha. El objetivo de este tutorial es para minimizar la entropía de Shannon de un conjunto de valores en el rango [0,1].
1. Elegir un problema cromosómico adecuada y crear la población inicial;
Un DoubleChromosome es adecuado para este problema con cada alelo dentro del rango [0,1].
55 Individual<DoubleChromosome> sample = new Individual<DoubleChromosome>(new DoubleChromosome(CHROMOSOME_LENGTH,0,1));56 Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATION_SIZE);
2. Puesta en marcha del algoritmo genético
Para utilizar SimpleGA tenemos que definir sólo la codificación función de aptitud. En este caso, la aptitud de cada individuo es igual al valor de entropía de acuerdo a la definición clásica de entropía: la entropía es la medida de la cantidad de información que falta antes de la recepción y se refiere a veces como la entropía de Shannon.
38 @Override39 public void evaluate(Individual<DoubleChromosome> individual) {40 DoubleChromosome chrom = individual.getChromosome();41 42 int length = chrom.length();43 44 double sum = 0.0;45 for (int i = 0; i < length; ++i) {46 sum += chrom.getValue(i);47 }48 49 double entropy = 0.0;50 for (int i = 0; i < length; ++i) {51 double pxi = chrom.getValue(i) / sum;52 chrom.setValue(i, pxi);53 54 entropy -= (pxi * Math.log(pxi) / Math.log(2));55 }56 57 individual.setScore(entropy);58 }
3. Elija los operadores a utilizar por el algoritmo genético y agregarlos como etapas en la ga.
Con SimpleGA nuestro algoritmo genético ya está configurado con respecto a los operadores a utilizar. Por defecto se utiliza la selección del torneo (intentos = 2), cruce de un punto (probabilidad = 0,8) y simple mutador (probabilidad = 0,2). Además SimpleGA usa por defecto una estrategia de sustitución de azar para el elitismo. De todos modos, es possibile para modificar esta configuración predeterminada que proporciona diferentes parámetros para los operadores y / o una estrategia de elitismo diferente. En nuestro caso utilizamos la configuración SimpleGA defecto.
4. Personalizar el algoritmo genético
El código de configuración entera se enumeran a continuación:
55 Individual<DoubleChromosome> sample = new Individual<DoubleChromosome>(new DoubleChromosome(CHROMOSOME_LENGTH,0,1));56 Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATION_SIZE);57
58 ga = new SimpleGA<DoubleChromosome>(null, pop, GENERATION_LIMIT);59 60 System.out.println("Solving Max!:");61 solve( EntropyFitness.MAX );62 63 System.out.println("Solving Min!:");64 solve( EntropyFitness.MIN );
Como puede ver, el algoritmo genético se ha inicializado sin la definición de la función de idoneidad para su uso. Este es possibile en que los casos en wich el gimnasio podría ser determinada sólo en un momento posterior con respecto a la creación del algoritmo genético. De todos modos la definición de una función de aptitud se requiere antes de que el método de evolucionar de GeneticAlgorithm se invoca, y esto es posible sólo hacer la siguiente llamada al método:
68 ga.setFitness(fitness);69 ga.evolve();
Ejemplos del tema:
19 package jenes.tutorials.problem4;20 21 import jenes.GeneticAlgorithm;22 import jenes.algorithms.SimpleGA;23 import jenes.chromosome.DoubleChromosome;24 import jenes.population.Individual;25 import jenes.population.Population;26 import jenes.population.Population.Statistics;27 import jenes.population.Population.Statistics.Group;28 import jenes.tutorials.utils.Utils;29 30 /**31 * Tutorial showing how to set-up a minimization problem.32 * The problem is to find a vector whose entroy, after normalization, is minimal.33 *34 * @author Luigi Troiano35 *36 * @version 2.037 * @since 1.038 */39 public class EntropyProblem {40 41 private static int POPULATION_SIZE = 100;42 private static int CHROMOSOME_LENGTH = 5;43 private static int GENERATION_LIMIT = 100;44 45 private static GeneticAlgorithm<DoubleChromosome> ga; 46
47 public static void main(String[] args) {48 Utils.printHeader();49 System.out.println();50 51 System.out.println("TUTORIAL 4:");52 System.out.println("Find the probability distribution that maximizes (or minimize) the Shannon's entropy.");53 System.out.println();54 55 Individual<DoubleChromosome> sample = new Individual<DoubleChromosome>(new DoubleChromosome(CHROMOSOME_LENGTH,0,1));56 Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATION_SIZE);57 58 ga = new SimpleGA<DoubleChromosome>(null, pop, GENERATION_LIMIT);59 60 System.out.println("Solving Max!:");61 solve( EntropyFitness.MAX );62 63 System.out.println("Solving Min!:");64 solve( EntropyFitness.MIN );65 }66 67 private static void solve(EntropyFitness fitness) {68 ga.setFitness(fitness);69 ga.evolve();70 71 Statistics stats = ga.getCurrentPopulation().getStatistics();72 GeneticAlgorithm.Statistics algostats = ga.getStatistics();73 74 Group legals = stats.getGroup(Population.LEGALS); 75 76 System.out.println(legals.get(0));77 System.out.format("found in %d ms.\n", algostats.getExecutionTime() );78 System.out.println();79 80 Utils.printStatistics(stats); 81 }82 }
Ejemplos del tema:
19 package jenes.tutorials.problem4;20 21 import jenes.Fitness;22 import jenes.chromosome.DoubleChromosome;23 import jenes.population.Individual;24 25 /**
26 *27 * @author Luigi Troiano28 */29 public class EntropyFitness extends Fitness<DoubleChromosome> {30 31 public static EntropyFitness MAX = new EntropyFitness(true);32 public static EntropyFitness MIN = new EntropyFitness(false);33 34 private EntropyFitness(boolean maximize) {35 super(maximize);36 }37 38 @Override39 public void evaluate(Individual<DoubleChromosome> individual) {40 DoubleChromosome chrom = individual.getChromosome();41 42 int length = chrom.length();43 44 double sum = 0.0;45 for (int i = 0; i < length; ++i) {46 sum += chrom.getValue(i);47 }48 49 double entropy = 0.0;50 for (int i = 0; i < length; ++i) {51 double pxi = chrom.getValue(i) / sum;52 chrom.setValue(i, pxi);53 54 entropy -= (pxi * Math.log(pxi) / Math.log(2));55 }56 57 individual.setScore(entropy);58 }59 }
ObjectChromosome
En este tutorial se muestra cómo utilizar el ObjectChromosome en Jenes. El ObjectChromosome representa un cromosoma complejo con genes que contienen un valor de alelo objeto. Dado un espacio de búsqueda hecho de variables objeto de cromosomas, nuestro objetivo es encontrar la secuencia más cercana de los valores (representable con objetos específicos) a una de destino. En nuestro problema de ejemplo cada cromosoma tiene cinco genes pertenecientes a diferentes valores de los alelos objeto. El primer gen es un número entero en el intervalo [0,9], el segundo es también un número entero, pero en el intervalo [10,30], la tercera es un número real en el intervalo [0,1], la cuarta es una boolean y el último es un gen que es el alfabeto {NEGRO, ROJO, BLANCO}
1 . Elegir un problema cromosómico adecuada y crear la población inicial ;
Un ObjectChromosome es adecuado para este problema . Contará con cinco genes cada uno de ellos con un conjunto de alelos diferentes : dos conjuntos de alelos entero para los dos primeros genes , la primera con el rango [ 0,9 ] y el segundo con rango [ 10,30 ], un doble alelo fijado para el tercer gen con rango [ 0,1] , un alelo boolean fijado para el gen de la vuelta y un grupo de alelos de la enumeración basada contiene { NEGRO , ROJO, BLANCO } para el último gen .
Para crear instancias de un grupo de alelos que podemos implementar la interfaz AlleleSet o utilizar la clase GenericAlleleSet . El grupo de alelos de número entero se obtiene subclases la clase GenericAlleleSet e implementación de dos métodos de fábrica , que se utiliza para crear instancias de los valores enteros en sus rangos especificados ( los valores son elegidos al azar o con una distribución de probabilidad uniforme en los intervalos especificados ) . El conjunto alelo real se obtiene de la misma manera . El grupo de alelos boolean se obtiene creando un objeto GenericAlleleSet containg los valores booleanos true y false. Por último , se define una enumeración, llamada Color , que contiene los valores NEGRO , rojo y blanco, entonces se crea un objeto GenericAlleleSet con los valores de la enumeración de color. El código se mostró a continuación.
36 public enum Color {37 38 RED, BLACK, WHITE;39 40 }
Ahora podemos construir nuestra representación cromosoma.
053 private static ObjectChromosome template =054 new ObjectChromosome( IntegerAlleleSet.createUniform(10, 0, 9),055 IntegerAlleleSet.createUniform(21, 10, 30),056 DoubleAlleleSet.createRandom(10, 0, 1),057 new GenericAlleleSet<Boolean>(true, false),058 new GenericAlleleSet<Color>(Color.values()) );
2. Puesta en marcha del algoritmo genético
A continuación se muestra el código para evaluar a un individuo en nuestro problema.
062 @Override063 public void evaluate(Individual<ObjectChromosome> individual) {064 ObjectChromosome template = individual.getChromosome();065 066 int i1 = (Integer)template.getValue(0);067 int i2 = (Integer)template.getValue(1);068 double d = (Double)template.getValue(2);069 boolean b = (Boolean)template.getValue(3);070 Color c = (Color)template.getValue(4);071 072 double acc = b ? (3*i1 + 4*i2 + d) : i1;073 074 switch( c ) {075 case BLACK : acc += 10; break;076 case RED : acc += 10; break;077 case WHITE : acc += 10; break;078 }079
080 individual.setScore(acc);081 }
En nuestro ejemplo los mejores individuos son los que tienen los valores más altos para el entero y genes dobles, con un verdadero valor para el gen boolean y con cualquier valor para el gen "Color".
Por lo tanto, la mejor persona que esperamos ejecutar este ejemplo podría ser: [9, 30, 0,99, es cierto, NEGRO].
3. Elija los operadores a utilizar por el algoritmo genético y agregarlos como etapas en la ga
En este tutorial se utiliza la configuración simple algoritmo clásico.
098 ga.addStage(new TournamentSelector<ObjectChromosome>(3));099 ga.addStage(new OnePointCrossover<ObjectChromosome>(0.8));100 ga.addStage(new SimpleMutator<ObjectChromosome>(0.02));
4.Personalizar el algoritmo genético
El objetivo global del algoritmo genético es maximizar los valores de fitness. Todo el código es visible en los archivos adjuntos a este tutorial.
Ejemplo de tema:
019 package jenes.tutorials.problem5;020 021 import jenes.Fitness;022 import jenes.GeneticAlgorithm;023 import jenes.chromosome.GenericAlleleSet;024 import jenes.chromosome.ObjectChromosome;025 import jenes.population.Individual;026 import jenes.population.Population;027 import jenes.population.Population.Statistics;028 import jenes.population.Population.Statistics.Group;029 import jenes.stage.operator.common.OnePointCrossover;030 import jenes.stage.operator.common.SimpleMutator;031 import jenes.stage.operator.common.TournamentSelector;032 import jenes.tutorials.utils.Utils;033 034 /**035 * Tutorial illustrating the use of object-oriented chromosomes, whose036 * allele set can be defined by the user for each gene.037 *038 * In this example the chromosomes are combinations of colors. We aim at finding039 * the vector of colors closest to a given sequence.040 *041 * This class defines the problem.
042 *043 * @author Luigi Troiano044 *045 * @version 2.0046 * @since 1.0047 */048 public class OCProblem {049 050 private static int POPULATION_SIZE = 10;051 private static int GENERATION_LIMIT = 100;052 053 private static ObjectChromosome template =054 new ObjectChromosome( IntegerAlleleSet.createUniform(10, 0, 9),055 IntegerAlleleSet.createUniform(21, 10, 30),056 DoubleAlleleSet.createRandom(10, 0, 1),057 new GenericAlleleSet<Boolean>(true, false),058 new GenericAlleleSet<Color>(Color.values()) );059 060 private static Fitness<ObjectChromosome> fitness = new Fitness<ObjectChromosome>(true) {061 062 @Override063 public void evaluate(Individual<ObjectChromosome> individual) {064 ObjectChromosome template = individual.getChromosome();065 066 int i1 = (Integer)template.getValue(0);067 int i2 = (Integer)template.getValue(1);068 double d = (Double)template.getValue(2);069 boolean b = (Boolean)template.getValue(3);070 Color c = (Color)template.getValue(4);071 072 double acc = b ? (3*i1 + 4*i2 + d) : i1;073 074 switch( c ) {075 case BLACK : acc += 10; break;076 case RED : acc += 10; break;077 case WHITE : acc += 10; break;078 }079 080 individual.setScore(acc);081 } 082 };083 084 private static GeneticAlgorithm<ObjectChromosome> ga =085 new GeneticAlgorithm<ObjectChromosome>( fitness, new Population<ObjectChromosome>(new Individual<ObjectChromosome>(template), POPULATION_SIZE), GENERATION_LIMIT);086 087 088 public static void main(String[] args)throws Exception {089 Utils.printHeader();
090 System.out.println();091 092 System.out.println("TUTORIAL 5:");093 System.out.println("Find the sequence of colors nearest to the target.");094 System.out.println();095 096 //Random.getInstance().setStandardSeed();097 098 ga.addStage(new TournamentSelector<ObjectChromosome>(3));099 ga.addStage(new OnePointCrossover<ObjectChromosome>(0.8));100 ga.addStage(new SimpleMutator<ObjectChromosome>(0.02));101 102 ga.evolve();103 104 Statistics stats = ga.getCurrentPopulation().getStatistics();105 GeneticAlgorithm.Statistics algostats = ga.getStatistics();106 107 Group legals = stats.getGroup(Population.LEGALS);108 109 System.out.println("Solution: ");110 System.out.println(legals.get(0));111 System.out.format("found in %d ms.\n", algostats.getExecutionTime() );112 System.out.println();113 114 Utils.printStatistics(stats);115 }116 }
Ejemplos de tema:
19 package jenes.tutorials.problem5;20 21 22 /**23 * Tutorial illustrating the use of object-oriented chromosomes, whose24 * allele set can be defined by the user for each gene.25 *26 * In this example the chromosomes are combinations of colors. We aim at finding27 * the vector of colors closest to a given sequence.28 *29 * This class defines the enumeration of possible colors.30 *31 * @author Luigi Troiano32 *33 * @version 1.034 * @since 1.035 */36 public enum Color {37
38 RED, BLACK, WHITE;39 40 }
Ejemplos del tema:
19 package jenes.tutorials.problem5;20 21 import java.util.HashSet;22 import java.util.Set;23 24 import jenes.utils.Random;25 import jenes.chromosome.GenericAlleleSet;26 27 /**28 * Tutorial illustrating the use of object-oriented chromosomes, whose29 * allele set can be defined by the user for each gene.30 *31 * In this example the chromosomes are combinations of colors. We aim at finding32 * the vector of colors closest to a given sequence.33 *34 * This class defines an allele set made of doubles.35 *36 * @author Luigi Troiano37 *38 * @version 1.039 * @since 1.040 */41 42 public class DoubleAlleleSet extends GenericAlleleSet<Double> {43 44 public DoubleAlleleSet(Set<Double> set) {45 super(set);46 }47 48 /**49 * Builds a DoubleAlleleSet with random values within the range [lowerBound,upperBound]50 * <p>51 * @param size the allala set cardinality52 * @param lowerBound the min value to choose53 * @param upperBound the max value to choose54 * @return a new DoubleAlleleSet55 */56 public static DoubleAlleleSet createRandom(int size, double lowerBound, double upperBound ) {57 HashSet<Double> values = new HashSet<Double>();58 Random rand = Random.getInstance();59 60 for( int i = 0; i < size; ++i ) {61 values.add(rand.nextDouble(lowerBound, upperBound+Double.MIN_VALUE));
62 }63 64 return new DoubleAlleleSet(values);65 }66 67 /**68 * Builds a new DoubleAlleleSet with uniformly distributed values within the range [lowerBound,upperBound]69 * <p>70 * @param size the allala set cardinality71 * @param lowerBound the min value to choose72 * @param upperBound the max value to choose73 * @return a new DoubleAlleleSet74 */75 public static DoubleAlleleSet createUniform(int size, int lowerBound, int upperBound ) {76 HashSet<Double> values = new HashSet<Double>();77 78 double step = 1.0/upperBound - lowerBound;79 for( double x = lowerBound; x <= upperBound; x += step ) {80 values.add(x);81 }82 83 return new DoubleAlleleSet(values);84 }85 86 87 }
Ejemplos del tema:
19 package jenes.tutorials.problem5;20 21 import java.util.HashSet;22 import java.util.Set;23 24 import jenes.utils.Random;25 import jenes.chromosome.GenericAlleleSet;26 27 /**28 * Tutorial illustrating the use of object-oriented chromosomes, whose29 * allele set can be defined by the user for each gene.30 *31 * In this example the chromosomes are combinations of colors. We aim at finding32 * the vector of colors closest to a given sequence.33 *34 * This class defines a set of integers.35 *36 * @author Luigi Troiano37 *
38 * @version 1.039 * @since 1.040 */41 public class IntegerAlleleSet extends GenericAlleleSet<Integer> {42 43 public IntegerAlleleSet(Set<Integer> set) {44 super(set);45 }46 47 /**48 * Builds an IntegerAlleleSet with random values within the range [lowerBound,upperBound]49 * <p>50 * @param size the allala set cardinality51 * @param lowerBound the min value to choose52 * @param upperBound the max value to choose53 * @return a new IntegerAlleleSet54 */55 public static IntegerAlleleSet createRandom(int size, int lowerBound, int upperBound ) {56 HashSet<Integer> values = new HashSet<Integer>();57 int s0 = upperBound - lowerBound + 1;58 if( size > s0 ) size = s0;59 60 Random rand = Random.getInstance();61 62 for( int i = 0; i < s0; ++i ) {63 64 int chosen = values.size();65 double coin = ((double)size-chosen)/(s0-i);66 boolean justEnough = s0-i == size-chosen;67 68 if( justEnough || rand.nextBoolean(coin) ) {69 values.add(lowerBound + i);70 }71 }72 73 return new IntegerAlleleSet(values);74 }75 76 /**77 * Builds a new IntegerAlleleSet with uniformly distributed values within the range [lowerBound,upperBound]78 * <p>79 * @param size the allala set cardinality80 * @param lowerBound the min value to choose81 * @param upperBound the max value to choose82 * @return a new IntegerAlleleSet83 */84 public static IntegerAlleleSet createUniform(int size, int lowerBound, int upperBound ) {85 HashSet<Integer> values = new HashSet<Integer>();
86 int s0 = upperBound - lowerBound + 1;87 if( size > s0 ) size = s0;88 89 double step = 1.0/(upperBound - lowerBound);90 for( double x = lowerBound; x <= upperBound; x += step ) {91 int i = (int) Math.round(x);92 values.add(i);93 }94 95 return new IntegerAlleleSet(values);96 }97 98 }
El uso de metas de algoritmo genético locales
Jenes ofrece la posibilidad de utilizar objetivos de algoritmos genéticos locales. El objetivo global es el utilizado por GeneticAlgorithm durante la evolución de la población en su principal secuencia de etapas. En su lugar, el objetivo local es el utilizado por la sola etapa (por ejemplo, una secuencia contenida en una rama paralela, etc.). Esta tutoriales muestra cómo resolver el problema de la mochila que define dos secuencias paralelas diferentes, con diferentes objetivos internos. El objetivo del problema de la mochila es maximizar la utilidad total de los elementos dentro de la bolsa, sin exceder la capacidad máxima de la bolsa. Tenemos n elementos cada uno con tiene una huella y un valor de utilidad.
1. Elegir un problema cromosómico adecuada y crear la población inicial
Una solución candidata es un conjunto de elementos con un peso y una utilidad. Las soluciones se codifican usando un cromosoma booleano. Los valores boolean Indica si un elemento está presente o no dentro de la bolsa. Por ejemplo, si el gen i-ésimo es verdad, entonces el elemento de orden i está en la bolsa. Un cromosoma con todos los genes que tiene un valor falso representa una bolsa vacía, mientras que un cromosoma con todos los genes que tengan un verdadero valor representa una bolsa con todos los elementos posibles.
070 public KnapsackGA( int popsize, int generations, double[] utilities, double[] weights) {071 super( new Population<BooleanChromosome>(new Individual<BooleanChromosome>(new BooleanChromosome(utilities.length)), popsize), generations);072
2. Puesta en marcha del algoritmo genético
Ponemos en práctica la clase KnapsackGA subclasificando GeneticAlgorithm e implementación de la función de aptitud como se informa a continuación.
053 @Override054 public void evaluate(Individual<BooleanChromosome> individual) {
055 double utility = KnapsackGA.this.getUtilityOf(individual);056 double weight = KnapsackGA.this.getWeightOf(individual);057 individual.setScore(utility);058 individual.setLegal(weight <= KnapsackGA.this.capacity);059 }
La aptitud de cada solución es igual a la suma de las utilidades de los elementos que contiene. Si el peso total supera la capacidad de la bolsa, entonces el individuo se establece como ilegal. La función de código anterior, utilice los siguientes métodos de utilidad para evaluar la utilidad y el peso de una solución particular.
116 public double getUtilityOf(Individual<BooleanChromosome> individual) {117 BooleanChromosome chrom = individual.getChromosome();118 double utility = 0.0;119 int size = chrom.length();120 for(int i = 0; i < size; ++i){121 utility += chrom.getValue(i) ? this.utilities[i] : 0.0;122 }123 return utility;124 }125 126 public double getWeightOf(Individual<BooleanChromosome> individual) {127 BooleanChromosome chrom = individual.getChromosome();128 double weight=0.0;129 int size = chrom.length();130 for(int i = 0; i < size; ++i){131 weight += chrom.getValue(i) ? this.weights[i] : 0.0;132 }133 return weight;134 }
3. Elija los operadores a utilizar por el algoritmo genético y agregarlos como etapas en la ga
Decidimos procesar individuos de manera diferente legales e ilegales. Por lo tanto, creamos una secuencia, llamada seq_legal, para procesar persona jurídica y una secuencia, llamada seq_illegal, para procesar los individuos ilegales. Estas secuencias tienen el mismo cuerpo (selector de torneo, un punto de cruce y simple mutador), pero diferentes objetivos. De hecho, el objetivo de seq_legal es maximizar la aptitud de las personas jurídicas, mientras que la de seq_illegal es minimizar la aptitud de las personas ilegales con el fin de eliminar el exceso de elementos y hacer legal la persona.
Evolucionamos en personas legales e ilegales paralelas y en cada generación pasamos las soluciones legales para seq_legal y las soluciones ilegales para seq_illegal a través del uso de un dispensador exclusivo
076 Parallel<BooleanChromosome> parallel =077 new Parallel<BooleanChromosome>(new ExclusiveDispenser<BooleanChromosome>(2){078 079 @Override080 public int distribute(Individual<BooleanChromosome> ind) {081 return ind.isLegal() ? 0 :1;
082 }083 084 });085 086 this.setFitness(this.maximize);087 088 Sequence<BooleanChromosome> seq_legal = new Sequence<BooleanChromosome>();089 seq_legal.appendStage(new TournamentSelector<BooleanChromosome>(2));090 seq_legal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));091 seq_legal.appendStage(new SimpleMutator<BooleanChromosome>(0.02));092 093 Sequence<BooleanChromosome> seq_illegal = new Sequence<BooleanChromosome>();094 seq_illegal.appendStage(new TournamentSelector<BooleanChromosome>(2));095 seq_illegal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));096 seq_illegal.appendStage(new SimpleMutator<BooleanChromosome>(0.2));097 098 parallel.add(seq_legal);099 parallel.add(seq_illegal);100 101 this.addStage(parallel);102 103 seq_legal.setFitness(this.maximize);104 seq_illegal.setFitness(this.minimize);
Ejemplo del tema:
019 package jenes.tutorials.problem6;020 021 import jenes.utils.Random;022 import jenes.population.Individual;023 import jenes.population.Population;024 import jenes.population.Population.Statistics;025 import jenes.population.Population.Statistics.Group;026 import jenes.tutorials.utils.Utils;027 028 /**029 * Tutorial showing how to minimization and maximization sub-prolems can cohesists in030 * the breeding structure of Jenes.031 *032 * This class defines the problem to solve.033 *034 * @author Luigi Troiano035 *036 * @version 1.0037 * @since 1.0038 */039 public class KnapsackProblem {040 041 private static int POPSIZE=100;
042 private static int GENERATION_LIMIT=100;043 044 private static final double[] WEIGHTS = {1, 5, 3, 2, 8, 6, 4, 7, 9, 6};045 private static final double[] UTILITIES = {7, 2, 7, 1, 6, 4, 2, 8, 9, 2};046 047 private KnapsackGA algorithm;048 private double[] utilities;049 private double[] weights;050 051 public KnapsackProblem(double[] utilities, double[] weights) {052 algorithm = new KnapsackGA(POPSIZE, GENERATION_LIMIT, utilities, weights);053 this.weights = weights;054 this.utilities = utilities;055 }056 057 @SuppressWarnings("unchecked")058 public void run() {059 this.algorithm.evolve();060 061 Statistics stat=algorithm.getCurrentPopulation().getStatistics();062 Group legals = stat.getGroup(Population.LEGALS);063 Individual solution= legals.get(0);064 System.out.println(solution.getChromosome());065 System.out.format("W: %f U: %f\n", algorithm.getWeightOf(solution), algorithm.getUtilityOf(solution) );066 }067 068 public double getCapacity() {069 return this.algorithm.getCapacity();070 }071 072 public void setCapacity(double c) {073 this.algorithm.setCapacity(c);074 }075 076 public double[] getUtilities() {077 return utilities;078 }079 080 public double[] getWeights() {081 return weights;082 }083 084 public static KnapsackProblem build(int n) {085 086 Random r = Random.getInstance();087 088 double[] utilities = new double[n];089 for( int i = 0; i < n; ++i ) {
090 utilities[i] = r.nextInt(10);091 }092 093 double[] weights = new double[n];094 for( int i = 0; i < n; ++i ) {095 weights[i] = r.nextInt(10);096 }097 098 return new KnapsackProblem(utilities, weights);099 }100 101 public static void main(String[] args) {102 103 Utils.printHeader();104 System.out.println();105 106 System.out.println("TUTORIAL 6:");107 System.out.println("The Knapsack Problem.");108 System.out.println();109 110 KnapsackProblem p1 = new KnapsackProblem(UTILITIES, WEIGHTS);111 112 System.out.println("Case 1: 10 elements, capacity 15");113 System.out.println("Utilities: " + toString(p1.getUtilities()) );114 System.out.println(" Weights: " + toString(p1.getWeights()) );115 p1.setCapacity(15);116 p1.run();117 System.out.println();118 119 System.out.println("Case 2: 10 elements, capacity 30");120 System.out.println("Utilities: " + toString(p1.getUtilities()) );121 System.out.println(" Weights: " + toString(p1.getWeights()) );122 p1.setCapacity(30);123 p1.run();124 System.out.println();125 126 KnapsackProblem p2 = KnapsackProblem.build(20);127 128 System.out.println("Case 3: 20 random elements, capacity 50");129 System.out.println("Utilities: " + toString(p2.getUtilities()) );130 System.out.println(" Weights: " + toString(p2.getWeights()) );131 p2.setCapacity(50);132 p2.run();133 System.out.println();134 }135 136 private static String toString(double[] values) {137 String s = "[";138 for(int i = 0; i < values.length; ++i ){
139 s += values[i]+ (i < values.length-1 ? " " : "]");140 }141 return s;142 }143 144 }
Ejemplo del tema:
019 package jenes.tutorials.problem6;020 021 import jenes.Fitness;022 import jenes.GeneticAlgorithm;023 import jenes.chromosome.BooleanChromosome;024 import jenes.population.Individual;025 import jenes.population.Population;026 import jenes.stage.ExclusiveDispenser;027 import jenes.stage.Parallel;028 import jenes.stage.Sequence;029 import jenes.stage.operator.common.OnePointCrossover;030 import jenes.stage.operator.common.SimpleMutator;031 import jenes.stage.operator.common.TournamentSelector;032 033 /**034 * Tutorial showing how to minimization and maximization sub-prolems can cohesists in035 * the breeding structure of Jenes.036 *037 * This class implements a genetic algorithm for solving the Knapsack problem.038 *039 * @author Luigi Troiano040 *041 * @version 2.0042 *043 * @since 1.0044 */045 public class KnapsackGA extends GeneticAlgorithm<BooleanChromosome>{046 047 private class KnapsackFitness extends Fitness<BooleanChromosome> {048 049 private KnapsackFitness(boolean maximize) {050 super( maximize );051 }052 053 @Override054 public void evaluate(Individual<BooleanChromosome> individual) {055 double utility = getUtilityOf(individual);056 double weight = getWeightOf(individual);057 individual.setScore(utility);058 individual.setLegal(weight <= KnapsackGA.this.capacity);
059 }060 061 }062 063 private double capacity;064 private double[] weights;065 private double[] utilities;066 067 private KnapsackFitness maximize = new KnapsackFitness(true);068 private KnapsackFitness minimize = new KnapsackFitness(false);069 070 public KnapsackGA( int popsize, int generations, double[] utilities, double[] weights) {071 super( new Population<BooleanChromosome>(new Individual<BooleanChromosome>(new BooleanChromosome(utilities.length)), popsize), generations);072 073 this.utilities = utilities;074 this.weights = weights;075 076 Parallel<BooleanChromosome> parallel =077 new Parallel<BooleanChromosome>(new ExclusiveDispenser<BooleanChromosome>(2){078 079 @Override080 public int distribute(Individual<BooleanChromosome> ind) {081 return ind.isLegal() ? 0 :1;082 }083 084 });085 086 this.setFitness(this.maximize);087 088 Sequence<BooleanChromosome> seq_legal = new Sequence<BooleanChromosome>();089 seq_legal.appendStage(new TournamentSelector<BooleanChromosome>(2));090 seq_legal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));091 seq_legal.appendStage(new SimpleMutator<BooleanChromosome>(0.02));092 093 Sequence<BooleanChromosome> seq_illegal = new Sequence<BooleanChromosome>();094 seq_illegal.appendStage(new TournamentSelector<BooleanChromosome>(2));095 seq_illegal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));096 seq_illegal.appendStage(new SimpleMutator<BooleanChromosome>(0.2));097 098 parallel.add(seq_legal);099 parallel.add(seq_illegal);100 101 this.addStage(parallel);102 103 seq_legal.setFitness(this.maximize);104 seq_illegal.setFitness(this.minimize);105 }106
107 public double getCapacity() {108 return this.capacity;109 }110 111 112 public void setCapacity(double capacity) {113 this.capacity = capacity;114 }115 116 public double getUtilityOf(Individual<BooleanChromosome> individual) {117 BooleanChromosome chrom = individual.getChromosome();118 double utility = 0.0;119 int size = chrom.length();120 for(int i = 0; i < size; ++i){121 utility += chrom.getValue(i) ? this.utilities[i] : 0.0;122 }123 return utility;124 }125 126 public double getWeightOf(Individual<BooleanChromosome> individual) {127 BooleanChromosome chrom = individual.getChromosome();128 double weight=0.0;129 int size = chrom.length();130 for(int i = 0; i < size; ++i){131 weight += chrom.getValue(i) ? this.weights[i] : 0.0;132 }133 return weight;134 } 135 136 }
Registro de las estadísticas
Experimentar con algoritmo genético requiere a menudo para recopilar datos sobre la evolución a lo largo de los diferentes experimentos. En este tutorial vamos a aprender a capturar estadísticas evolución en. Csv y los archivos de MS Excel.
Los madereros
Hay dos tres métodos para registrar las estadísticas en Jenes. La primera es hacerlo usted mismo por guardar los datos en un archivo o base de datos. Los otros dos se basan en los registradores disponibles desde Jenes 1.3.0. La forma más fácil es usar un StatisticsLogger. Esta clase es capaz de grabar automáticamente los objetos que pertenecen desde LoggableStatistics, por ejemplo, Population.Statistics y objetos GeneticAlgorithm.Statistics. StatisticsLogger basa en un registrador real para grabar realmente los datos en algún medio. Por ejemplo, se puede hacer uso de un CSVLogger para grabar en. Csv (es decir, valores separados por comas) archive
072 csvlogger = new StatisticsLogger(073 new CSVLogger(new String[]{"LegalHighestScore", "LegalScoreAvg","LegalScoreDev"}, FOLDER + "knapsackproblem.csv" ) );
o XLSLogger para grabar directamente en un archivo xls MS Excel.
075 xlslogge1 = new StatisticsLogger(076 new XLSLogger(new String[]{"LegalHighestScore", "LegalScoreAvg","LegalScoreDev"}, FOLDER + "knapsack1.log.xls" ) );
En algún momento es útil volver a utilizar un spreedsheet. En este caso, podemos especificar que plantilla se usan en el método constructor de XLSLogger.
078 xlslogge2 = new StatisticsLogger(079 new XLSLogger(new String[]{"LegalHighestScore", "LegalScoreAvg" , "IllegalScoreAvg"}, FOLDER + "knapsack2.log.xls", FOLDER +"knapsack.tpl.xls" ) );
El tercer método hace uso directo de registrador real, por ejemplo
081 xlslogge3 = new XLSLogger(new String[]{"LegalHighestScore", "LegalScoreAvg" , "Run"}, FOLDER + "knapsack3.log.xls");
Esquema
Los madereros son capaces de procesar datos tabulares con un esquema determinado, es decir, un conjunto de campos etiquetados. El esquema de datos se tiene que dar en el momento de la construcción. Por ejemplo, en el primer y segundo caso estamos interesados en grabar LegalHighestScore, LegalScoreAvg y LegalScoreDev
largo de las generaciones. En el tercer caso grabamos LegalHighestScore, LegalScoreAvg y IllegalScoreAvg. Finalmente, en el tercer caso grabamos LegalHighestScore, LegalScoreAvg and Run, representando este último el número de ejecución de la experimentación.
Si utilizamos StatisticsLogger, los campos tienen que ser etiquetados de la misma manera las estadísticas son anotados por registrable (etiqueta). Si utilizamos directamente los madereros reales, podemos hacer uso de distintivos que deseamos para el esquema, como el mapeo estará en nuestro poder, como se describe a continuación.
Tenga en cuenta que el esquema entre mayúsculas y minúsculas, por lo que "someStatistic" es diferente de "SomeStatistic", y ambos son diferentes de "SOMESTATISTIC".
Medios de comunicación
Grabación en. Csv es sencillo. Si el archivo existe, se puede decidir si desea sobrescribir los datos (por defecto) o para añadir datos.
Cuando usamos. Xls debemos cuidar algunos detalles. Cuando no existe el archivo de registro, el registrador crea un libro vacío con una hoja, donde las columnas se asignan de acuerdo con el esquema con primera fila asignada a las etiquetas de campo.
Podemos optar por utilizar un pre-hechos xls.. En este caso tenemos que reservar una columna para cada campo en el esquema. La columna puede colocarse en cualquier parte de la hoja, que la hoja no importa. La única restricción es que la celda en la fila 1 debe contener la etiqueta del campo. También podemos optar por utilizar un archivo xls. Como plantilla. Una plantilla es simplemente un archivo de pre-hechos. En este caso, el archivo no se sobrescribe como el destino es diferente (ver xlslogge2 para un ejemplo).
Por ejemplo, podemos decidir utilizar la plantilla se muestra en la Figura 1.
Recolección de Datos
Una vez que los madereros están establecidos y esquema definido, podemos recoger datos. La mejor manera de recoger los datos es por medio de oyentes. Por ejemplo
143 GenerationEventListener<BooleanChromosome> logger1 = new GenerationEventListener<BooleanChromosome>() {144 145 public void onGeneration(GeneticAlgorithm ga, long time) {146 Population.Statistics stats = ga.getCurrentPopulation().getStatistics();147 148 prb.csvlogger.record(stats);149 prb.xlslogge1.record(stats);150 prb.xlslogge2.record(stats);151 152 }153 154 };
En este caso StatisticsLogger se encarga de registrar datos. Tan sólo hay que proporcionar una LoggableStatistics y StatisticsLogger haremos el trabajo por nosotros. El inconveniente de esta solución es que no somos capaces de añadir datos no considerados por el objeto de estadísticas. Para obtener un control más preciso del proceso, podemos hacer uso directo de un registrador real. Por ejemplo
172 GenerationEventListener<BooleanChromosome> logger2 = new GenerationEventListener<BooleanChromosome>() {173 public void onGeneration(GeneticAlgorithm ga, long time) {174 Population.Statistics stats = ga.getCurrentPopulation().getStatistics();175 176 Group legals = stats.getGroup(Population.LEGALS);177 178 prb.xlslogge3.put("LegalHighestScore", legals.getMax());179 prb.xlslogge3.put("LegalScoreAvg", legals.getMin());180 prb.xlslogge3.put("Run", prb.exec);181 182 prb.xlslogge3.log();183 }184 185 };
Entrar cierre
Por último, podemos hacer que los datos persistentes cerrando los madereros.
165 prb.csvlogger.close();166 prb.xlslogge1.close();167 prb.xlslogge2.close();
195 prb.xlslogge3.close();
Salida
La salida consiguió al final se representa en las figuras siguientes. Ver también los archivos adjuntos de abajo.
Ejemplos del tema:
019 package jenes.tutorials.problem7;020 021 import java.io.File;022 import java.io.FileNotFoundException;023 import java.io.IOException;024 import jenes.GenerationEventListener;025 import jenes.GeneticAlgorithm;026 import jenes.chromosome.BooleanChromosome;027 import jenes.utils.Random;028 import jenes.population.Individual;029 import jenes.population.Population;030 import jenes.population.Population.Statistics;031 import jenes.population.Population.Statistics.Group;032 import jenes.tutorials.utils.Utils;033 import jenes.utils.CSVLogger;034 import jenes.statistics.StatisticsLogger;035 import jenes.tutorials.problem6.KnapsackGA;036 import jenes.utils.XLSLogger;037 038 /**039 * A tutorial showing how to log statistics on different media.040 *041 * @author Luigi Troiano042 *043 * @version 1.3044 *045 * @since 1.3046 */047 public class KnapsackLoggedProblem {048 049 private static int POPSIZE=20;050 private static int GENERATION_LIMIT=100;051 052 private static final double[] WEIGHTS = {1, 5, 3, 2, 8, 6, 4, 7, 9, 6};053 private static final double[] UTILITIES = {7, 2, 7, 1, 6, 4, 2, 8, 9, 2};054
055 private KnapsackGA algorithm;056 private double[] utilities;057 private double[] weights;058 059 private StatisticsLogger csvlogger;060 private StatisticsLogger xlslogge1;061 private StatisticsLogger xlslogge2;062 private XLSLogger xlslogge3;063 private int exec;064 065 private final String FOLDER = "files.Tutorial7" + File.separatorChar;066 067 public KnapsackLoggedProblem(double[] utilities, double[] weights) throws IOException {068 algorithm = new KnapsackGA(POPSIZE, GENERATION_LIMIT, utilities, weights);069 this.weights = weights;070 this.utilities = utilities;071 072 csvlogger = new StatisticsLogger(073 new CSVLogger(new String[]{"LegalHighestScore","LegalScoreAvg","LegalScoreDev"}, FOLDER+"knapsackproblem.csv" ) );074 075 xlslogge1 = new StatisticsLogger(076 new XLSLogger(new String[]{"LegalHighestScore","LegalScoreAvg","LegalScoreDev"}, FOLDER+"knapsack1.log.xls" ) );077 078 xlslogge2 = new StatisticsLogger(079 new XLSLogger(new String[]{"LegalHighestScore", "LegalScoreAvg" , "IllegalScoreAvg"}, FOLDER+"knapsack2.log.xls", FOLDER+"knapsack.tpl.xls" ) );080 081 xlslogge3 = new XLSLogger(new String[]{"LegalHighestScore", "LegalScoreAvg" , "Run"}, FOLDER+"knapsack3.log.xls");082 083 }084 085 @SuppressWarnings("unchecked")086 public void run() {087 this.algorithm.evolve();088 089 Statistics stat=algorithm.getCurrentPopulation().getStatistics();090 Individual solution=stat.getLegalHighestIndividual();091 System.out.println(solution.getChromosome());092 System.out.format("W: %f U: %f\n", algorithm.getWeightOf(solution), algorithm.getUtilityOf(solution) );093 }094 095 public double getCapacity() {096 return this.algorithm.getCapacity();097 }098
099 public void setCapacity(double c) {100 this.algorithm.setCapacity(c);101 }102 103 public double[] getUtilities() {104 return utilities;105 }106 107 public double[] getWeights() {108 return weights;109 }110 111 public static KnapsackLoggedProblem build(int n) throws FileNotFoundException, IOException {112 113 Random r = Random.getInstance();114 115 double[] utilities = new double[n];116 for( int i = 0; i < n; ++i ) {117 utilities[i] = r.nextInt(10);118 }119 120 double[] weights = new double[n];121 for( int i = 0; i < n; ++i ) {122 weights[i] = r.nextInt(10);123 }124 125 return new KnapsackLoggedProblem(utilities, weights);126 }127 128 public static void main(String[] args) throws FileNotFoundException, IOException {129 130 Utils.printHeader();131 System.out.println();132 133 System.out.println("TUTORIAL 7:");134 System.out.println("Logging the Knapsack Problem.");135 System.out.println();136 137 final KnapsackLoggedProblem prb = KnapsackLoggedProblem.build(20);138 139 System.out.println("Utilities: " + toString(prb.getUtilities()) );140 System.out.println(" Weights: " + toString(prb.getWeights()) );141 System.out.println();142 143 GenerationEventListener<BooleanChromosome> logger1 = new GenerationEventListener<BooleanChromosome>() {144 145 public void onGeneration(GeneticAlgorithm ga, long time) {146 Population.Statistics stats = ga.getCurrentPopulation().getStatistics();
147 148 prb.csvlogger.record(stats);149 prb.xlslogge1.record(stats);150 prb.xlslogge2.record(stats);151 152 }153 154 };155 156 prb.algorithm.addGenerationEventListener(logger1);157 158 159 System.out.println("50 random elements, capacity 50");160 prb.setCapacity(50);161 prb.run();162 System.out.println();163 164 System.out.println("Saving the logs ...");165 prb.csvlogger.close();166 prb.xlslogge1.close();167 prb.xlslogge2.close();168 System.out.println("Done.");169 170 prb.algorithm.removeGenerationEventListener(logger1);171 172 GenerationEventListener<BooleanChromosome> logger2 = new GenerationEventListener<BooleanChromosome>() {173 public void onGeneration(GeneticAlgorithm ga, long time) {174 Population.Statistics stats = ga.getCurrentPopulation().getStatistics();175 176 Group legals = stats.getGroup(Population.LEGALS);177 178 prb.xlslogge3.put("LegalHighestScore", legals.getMax());179 prb.xlslogge3.put("LegalScoreAvg", legals.getMin());180 prb.xlslogge3.put("Run", prb.exec);181 182 prb.xlslogge3.log();183 }184 185 };186 187 prb.algorithm.addGenerationEventListener(logger2);188 189 System.out.println();190 System.out.println("Repeating 10 times: 20 random elements, capacity 50");191 for( prb.exec = 0; prb.exec < 10; ++prb.exec) {192 System.out.println((prb.exec+1) + " of 10");193 prb.run();194 }