TEMA 14.DERIVACIONES ECONÓMICAS, SOCIALES Y POLÍTICAS DEL PROCESO DE INTEGRAC...
Anatomía de un algoritmo genético en jenes
1. 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.
2. 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.
3. 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.
4. 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
5. 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.
6. 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
7. 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 .
8. 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.
9. 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 BooleanC
hromosome(CHROMOSOME_LENGTH));
061
Population<BooleanChromosome> pop = new Population<BooleanChromosome>(sample, POPUL
ATION_SIZE);
cuando
045
046
047
private static int POPULATION_SIZE=50;
private static int CHROMOSOME_LENGTH=100;
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).
10. 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
064
065
066
067
068
069
070
071
072
073
074
075
076
077
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);
}
};
Y luego pasar este objeto como primer parámetro de algoritmo genético (o cualquier
subclase predefinido)
079
080
GeneticAlgorithm<BooleanChromosome> ga = new GeneticAlgorithm<BooleanChromosome>
(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
11. 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
083
);
084
085
086
087
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);
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
094
095
096
"));
097
098
099
100
101
102
103
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: ");
12. 104
105
106
107
108
System.out.println( solution );
System.out.format("found in %d ms.n", algostats.getExecutionTime() );
System.out.println();
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 BooleanChro
mosome(CHROMOSOME_LENGTH));
Population<BooleanChromosome> pop = new Population<BooleanChromosome>(sample, POPULATI
ON_SIZE);
Fitness<BooleanChromosome> fit = new Fitness<BooleanChromosome>(false) {
13. @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);
}
}
14. 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.
15. 065
066
IntegerChromosome chrom = new IntegerChromosome(CHROMOSOME_LENGTH,0,MAX_INT);
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
68
69
70
71
72
73
74
75
76
@Override
public void evaluate(Individual<IntegerChromosome> individual) {
IntegerChromosome chrom = individual.getChromosome();
int diff = 0;
int length = chrom.length();
for (int i = 0; i < length; i++) {
diff += Math.abs(chrom.getValue(i) - target[i]);
}
individual.setScore(diff);
}
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.
16. 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 @Override
56 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 SimpleDispense
r<IntegerChromosome>(2));
075
076
AbstractStage<IntegerChromosome> crossover1p = new OnePointCrossover<IntegerChromosome>
(0.8);
17. 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
53
54
55
56
@Override
protected boolean end() {
jenes.population.Population.Statistics stat = this.getCurrentPopulation(). getStatistics();
return stat.getGroup(Population.LEGALS).getMin()[0] <= this.fitness.precision;
}
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
114
public void onGeneration(GeneticAlgorithm ga, long time) {
Statistics stat = ga.getCurrentPopulation().getStatistics();
18. 115
116
117
118
119
Group legals = stat.getGroup(Population.LEGALS);
System.out.println("Current generation: " + ga.getGeneration());
System.out.println("tBest score: " + legals.getMin()[0]);
System.out.println("tAvg score : " + legals.getMean()[0]);
}
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 SimpleDispense
r<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
085
086
087
algorithm.addStage(selection);
algorithm.addStage(parallel);
algorithm.addStage(mutation);
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;
19. 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, POPULATIO
N_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);
20. 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.");
22. 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 @Override
53 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
@Override
68
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 }
23. 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ífico
2. 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:
24. 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 P
ermutationChromosome(cities));
128
Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample,
POPULATION_SIZE);
129
130
Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {
131
132
@Override
133
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, po
p, 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.
25. 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 lengt
h.");
094
095
096
//we choose a random city
097
int city = Random.getInstance().nextInt(0, size);
098
099
//i1, i2 are the positions of the city respectively in child1 and child2
100
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 parent1
107
int c1 = chrom_parent1.getValue(i);
108
// find the position of c1 in parent 2
109
int p2 = findPositionOf(c1, chrom_parent2);
110
// if the position is over the cross-point, it copies c1 in child2
111
if( p2 > i2 ) {
112
chrom_child2.setValue(i2 + (++j2), c1);
113
}
114
115
// similarly we process the other pair
116
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 }
26. 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
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
public void randomize(IntegerChromosome chrom, int min, int max) {
//we create a temporany array
int len = max-min+1;
int[] base = new int[len];
//we fill it with the elements within [min,max]
for(int i=0;i<len;i++)
base[i]= chrom.getValue(min+i);
//the loop ends when the temporany array is empty
for( int i = 0; len > 0; --len, ++i) {
//we choose a random position pos in the array and copy the element at pos in the chromosome
int pos = Random.getInstance().nextInt(0,len);
chrom.setValue(min+i,base[pos]);
//we removes the chosen element from the temporany array
for(int j=pos;j<(len-1);j++){
base[j]=base[j+1];
}
}
}
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;
27. 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;
28. 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, POPULAT
ION_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 P
ermutationChromosome(cities));
128
Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample,
POPULATION_SIZE);
129
130
Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {
131
132
@Override
133
public void evaluate(Individual<PermutationChromosome> individual) {
29. 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, po
p, 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
30. 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 @Override
054 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]
31. 076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102 }
* <p>
* @param chrom the individual to mutate
* @param min the lower bound
* @param max the upper bound
*/
public void randomize(IntegerChromosome chrom, int min, int max) {
//we create a temporany array
int len = max-min+1;
int[] base = new int[len];
//we fill it with the elements within [min,max]
for(int i=0;i<len;i++)
base[i]= chrom.getValue(min+i);
//the loop ends when the temporany array is empty
for( int i = 0; len > 0; --len, ++i) {
//we choose a random position pos in the array and copy the element at pos in the chromosome
int pos = Random.getInstance().nextInt(0,len);
chrom.setValue(min+i,base[pos]);
//we removes the chosen element from the temporany array
for(int j=pos;j<(len-1);j++){
base[j]=base[j+1];
}
}
}
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);
}
32. @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) {
33. 060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
h.");
094
095
096
097
098
099
100
101
102
103
104
105
106
107
super(pCross);
}
/**
* Returns the number of chromosomes (i.e. 2) this operator entails.
*/
@Override
public int spread() {
return 2;
}
private IntegerChromosome chrom_parent1 = null;
private IntegerChromosome chrom_parent2 = null;
/**
* This method implements the crossover operation.
*
* @param offsprings the chromosomes to be crossed.
*/
protected void cross(Individual<IntegerChromosome> offsprings[]) {
IntegerChromosome chrom_child1 = offsprings[0].getChromosome();
IntegerChromosome chrom_child2 = offsprings[1].getChromosome();
if( chrom_parent1 == null ) {
chrom_parent1 = chrom_child1.clone();
chrom_parent2 = chrom_child2.clone();
} else {
chrom_parent1.setAs(chrom_child1);
chrom_parent2.setAs(chrom_child2);
}
final int size = chrom_child1.length();
if( chrom_child2.length() != size )
throw new AlgorithmException("Error: the two chromosomes are required to have the same lengt
//we choose a random city
int city = Random.getInstance().nextInt(0, size);
//i1, i2 are the positions of the city respectively in child1 and child2
int i1 = findPositionOf(city, chrom_child1);
int i2 = findPositionOf(city, chrom_child2);
int j1 = 0;
int j2 = 0;
for( int i = 0; i < size; ++i ) {
// get the city c1 in position i for parent1
int c1 = chrom_parent1.getValue(i);
34. 108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 }
// find the position of c1 in parent 2
int p2 = findPositionOf(c1, chrom_parent2);
// if the position is over the cross-point, it copies c1 in child2
if( p2 > i2 ) {
chrom_child2.setValue(i2 + (++j2), c1);
}
// similarly we process the other pair
int c2 = chrom_parent2.getValue(i);
int p1 = findPositionOf(c2, chrom_parent1);
if( p1 > i1 ) {
chrom_child1.setValue(i1 + (++j1), c2);
}
}
}
/**
* Finds the position of one specific city in the chromosome.
* <p>
* @param city the city to find
* @param chrom the chromosome to search
* @return the city position
*/
private int findPositionOf(int city, IntegerChromosome chrom){
final int size = chrom.length();
for( int i = 0; i < size; ++i ) {
if( chrom.getValue(i) == city )
return i;
}
return -1;
}
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.
35. 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ífico
2. 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 P
ermutationChromosome(cities));
128
Population<PermutationChromosome> pop = new Population<PermutationChromosome>(sample,
POPULATION_SIZE);
129
130
Fitness<PermutationChromosome> fitness = new Fitness<PermutationChromosome>(false) {
131
132
@Override
133
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);
36. 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, po
p, 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 lengt
h.");
094
095
096
//we choose a random city
37. 097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
int city = Random.getInstance().nextInt(0, size);
//i1, i2 are the positions of the city respectively in child1 and child2
int i1 = findPositionOf(city, chrom_child1);
int i2 = findPositionOf(city, chrom_child2);
int j1 = 0;
int j2 = 0;
for( int i = 0; i < size; ++i ) {
// get the city c1 in position i for parent1
int c1 = chrom_parent1.getValue(i);
// find the position of c1 in parent 2
int p2 = findPositionOf(c1, chrom_parent2);
// if the position is over the cross-point, it copies c1 in child2
if( p2 > i2 ) {
chrom_child2.setValue(i2 + (++j2), c1);
}
// similarly we process the other pair
int c2 = chrom_parent2.getValue(i);
int p1 = findPositionOf(c2, chrom_parent1);
if( p1 > i1 ) {
chrom_child1.setValue(i1 + (++j1), c2);
}
}
}
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
082
083
084
085
086
087
088
public void randomize(IntegerChromosome chrom, int min, int max) {
//we create a temporany array
int len = max-min+1;
int[] base = new int[len];
//we fill it with the elements within [min,max]
for(int i=0;i<len;i++)
38. 089
090
091
092
093
094
095
096
097
098
099
100
101
base[i]= chrom.getValue(min+i);
//the loop ends when the temporany array is empty
for( int i = 0; len > 0; --len, ++i) {
//we choose a random position pos in the array and copy the element at pos in the chromosome
int pos = Random.getInstance().nextInt(0,len);
chrom.setValue(min+i,base[pos]);
//we removes the chosen element from the temporany array
for(int j=pos;j<(len-1);j++){
base[j]=base[j+1];
}
}
}
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 DoubleChro
mosome(CHROMOSOME_LENGTH,0,1));
56
Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATI
ON_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.
39. 38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Override
public void evaluate(Individual<DoubleChromosome> individual) {
DoubleChromosome chrom = individual.getChromosome();
int length = chrom.length();
double sum = 0.0;
for (int i = 0; i < length; ++i) {
sum += chrom.getValue(i);
}
double entropy = 0.0;
for (int i = 0; i < length; ++i) {
double pxi = chrom.getValue(i) / sum;
chrom.setValue(i, pxi);
entropy -= (pxi * Math.log(pxi) / Math.log(2));
}
individual.setScore(entropy);
}
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 DoubleChro
mosome(CHROMOSOME_LENGTH,0,1));
56
Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATI
ON_SIZE);
57
58
ga = new SimpleGA<DoubleChromosome>(null, pop, GENERATION_LIMIT);
59
60
System.out.println("Solving Max!:");
61
solve( EntropyFitness.MAX );
62
40. 63
64
System.out.println("Solving Min!:");
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
69
ga.setFitness(fitness);
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 Troiano
35 *
36 * @version 2.0
37 * @since 1.0
38 */
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:");
41. 52
System.out.println("Find the probability distribution that maximizes (or minimize) the Shannon's entr
opy.");
53
System.out.println();
54
55
Individual<DoubleChromosome> sample = new Individual<DoubleChromosome>(new DoubleChro
mosome(CHROMOSOME_LENGTH,0,1));
56
Population<DoubleChromosome> pop = new Population<DoubleChromosome>(sample, POPULATI
ON_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 Troiano
28 */
29 public class EntropyFitness extends Fitness<DoubleChromosome> {
30
42. 31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 }
public static EntropyFitness MAX = new EntropyFitness(true);
public static EntropyFitness MIN = new EntropyFitness(false);
private EntropyFitness(boolean maximize) {
super(maximize);
}
@Override
public void evaluate(Individual<DoubleChromosome> individual) {
DoubleChromosome chrom = individual.getChromosome();
int length = chrom.length();
double sum = 0.0;
for (int i = 0; i < length; ++i) {
sum += chrom.getValue(i);
}
double entropy = 0.0;
for (int i = 0; i < length; ++i) {
double pxi = chrom.getValue(i) / sum;
chrom.setValue(i, pxi);
entropy -= (pxi * Math.log(pxi) / Math.log(2));
}
individual.setScore(entropy);
}
43. 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
44. 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
054
055
056
057
058
private static ObjectChromosome template =
new ObjectChromosome( IntegerAlleleSet.createUniform(10, 0, 9),
IntegerAlleleSet.createUniform(21, 10, 30),
DoubleAlleleSet.createRandom(10, 0, 1),
new GenericAlleleSet<Boolean>(true, false),
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
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
@Override
public void evaluate(Individual<ObjectChromosome> individual) {
ObjectChromosome template = individual.getChromosome();
int i1 = (Integer)template.getValue(0);
int i2 = (Integer)template.getValue(1);
double d = (Double)template.getValue(2);
boolean b = (Boolean)template.getValue(3);
Color c = (Color)template.getValue(4);
double acc = b ? (3*i1 + 4*i2 + d) : i1;
switch( c ) {
case BLACK : acc += 10; break;
case RED : acc += 10; break;
case WHITE : acc += 10; break;
}
individual.setScore(acc);
}
45. 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
099
100
ga.addStage(new TournamentSelector<ObjectChromosome>(3));
ga.addStage(new OnePointCrossover<ObjectChromosome>(0.8));
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, whose
036 * 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 finding
039 * the vector of colors closest to a given sequence.
040 *
041 * This class defines the problem.
042 *
043 * @author Luigi Troiano
46. 044 *
045 * @version 2.0
046 * @since 1.0
047 */
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
@Override
063
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>(ne
w 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
47. 092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115 }
116 }
System.out.println("TUTORIAL 5:");
System.out.println("Find the sequence of colors nearest to the target.");
System.out.println();
//Random.getInstance().setStandardSeed();
ga.addStage(new TournamentSelector<ObjectChromosome>(3));
ga.addStage(new OnePointCrossover<ObjectChromosome>(0.8));
ga.addStage(new SimpleMutator<ObjectChromosome>(0.02));
ga.evolve();
Statistics stats = ga.getCurrentPopulation().getStatistics();
GeneticAlgorithm.Statistics algostats = ga.getStatistics();
Group legals = stats.getGroup(Population.LEGALS);
System.out.println("Solution: ");
System.out.println(legals.get(0));
System.out.format("found in %d ms.n", algostats.getExecutionTime() );
System.out.println();
Utils.printStatistics(stats);
Ejemplos de tema:
19 package jenes.tutorials.problem5;
20
21
22 /**
23 * Tutorial illustrating the use of object-oriented chromosomes, whose
24 * 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 finding
27 * the vector of colors closest to a given sequence.
28 *
29 * This class defines the enumeration of possible colors.
30 *
31 * @author Luigi Troiano
32 *
33 * @version 1.0
34 * @since 1.0
35 */
36 public enum Color {
37
38 RED, BLACK, WHITE;
48. 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, whose
29 * 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 finding
32 * the vector of colors closest to a given sequence.
33 *
34 * This class defines an allele set made of doubles.
35 *
36 * @author Luigi Troiano
37 *
38 * @version 1.0
39 * @since 1.0
40 */
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 cardinality
52
* @param lowerBound the min value to choose
53
* @param upperBound the max value to choose
54
* @return a new DoubleAlleleSet
55
*/
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
}
49. 63
64
return new DoubleAlleleSet(values);
65 }
66
67 /**
68
* Builds a new DoubleAlleleSet with uniformly distributed values within the range [lowerBound,upper
Bound]
69
* <p>
70
* @param size the allala set cardinality
71
* @param lowerBound the min value to choose
72
* @param upperBound the max value to choose
73
* @return a new DoubleAlleleSet
74
*/
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, whose
29 * 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 finding
32 * the vector of colors closest to a given sequence.
33 *
34 * This class defines a set of integers.
35 *
36 * @author Luigi Troiano
37 *
38 * @version 1.0
50. 39 * @since 1.0
40 */
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 cardinality
51
* @param lowerBound the min value to choose
52
* @param upperBound the max value to choose
53
* @return a new IntegerAlleleSet
54
*/
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,upper
Bound]
78
* <p>
79
* @param size the allala set cardinality
80
* @param lowerBound the min value to choose
81
* @param upperBound the max value to choose
82
* @return a new IntegerAlleleSet
83
*/
84 public static IntegerAlleleSet createUniform(int size, int lowerBound, int upperBound ) {
85
HashSet<Integer> values = new HashSet<Integer>();
86
int s0 = upperBound - lowerBound + 1;
51. 87
88
89
90
91
92
93
94
95
96 }
97
98 }
if( size > s0 ) size = s0;
double step = 1.0/(upperBound - lowerBound);
for( double x = lowerBound; x <= upperBound; x += step ) {
int i = (int) Math.round(x);
values.add(i);
}
return new IntegerAlleleSet(values);
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 Boole
anChromosome(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
054
055
@Override
public void evaluate(Individual<BooleanChromosome> individual) {
double utility = KnapsackGA.this.getUtilityOf(individual);
52. 056
057
058
059
double weight = KnapsackGA.this.getWeightOf(individual);
individual.setScore(utility);
individual.setLegal(weight <= KnapsackGA.this.capacity);
}
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
public double getUtilityOf(Individual<BooleanChromosome> individual) {
BooleanChromosome chrom = individual.getChromosome();
double utility = 0.0;
int size = chrom.length();
for(int i = 0; i < size; ++i){
utility += chrom.getValue(i) ? this.utilities[i] : 0.0;
}
return utility;
}
public double getWeightOf(Individual<BooleanChromosome> individual) {
BooleanChromosome chrom = individual.getChromosome();
double weight=0.0;
int size = chrom.length();
for(int i = 0; i < size; ++i){
weight += chrom.getValue(i) ? this.weights[i] : 0.0;
}
return weight;
}
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
077
078
079
080
081
082
083
Parallel<BooleanChromosome> parallel =
new Parallel<BooleanChromosome>(new ExclusiveDispenser<BooleanChromosome>(2){
@Override
public int distribute(Individual<BooleanChromosome> ind) {
return ind.isLegal() ? 0 :1;
}
53. 084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
});
this.setFitness(this.maximize);
Sequence<BooleanChromosome> seq_legal = new Sequence<BooleanChromosome>();
seq_legal.appendStage(new TournamentSelector<BooleanChromosome>(2));
seq_legal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));
seq_legal.appendStage(new SimpleMutator<BooleanChromosome>(0.02));
Sequence<BooleanChromosome> seq_illegal = new Sequence<BooleanChromosome>();
seq_illegal.appendStage(new TournamentSelector<BooleanChromosome>(2));
seq_illegal.appendStage(new OnePointCrossover<BooleanChromosome>(0.8));
seq_illegal.appendStage(new SimpleMutator<BooleanChromosome>(0.2));
parallel.add(seq_legal);
parallel.add(seq_illegal);
this.addStage(parallel);
seq_legal.setFitness(this.maximize);
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 in
030 * the breeding structure of Jenes.
031 *
032 * This class defines the problem to solve.
033 *
034 * @author Luigi Troiano
035 *
036 * @version 1.0
037 * @since 1.0
038 */
039 public class KnapsackProblem {
040
041 private static int POPSIZE=100;
042 private static int GENERATION_LIMIT=100;
043
59. 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"}, FOL
DER + "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.
60. 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.
61. 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<Boolean
Chromosome>() {
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<Boolean
Chromosome>() {
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
166
prb.csvlogger.close();
prb.xlslogge1.close();