Subido por More Hack

Trabajo evolutivo

Anuncio
UNIVERSIDAD DE OVIEDO
Facultad de Ciencias
Licenciatura en Matemáticas
Algoritmos Evolutivos
para la Resolución de
Sistemas de “Word Equations”
Fátima Drubi Vega
Trabajo Académico
Oviedo, julio de 2003
Índice general
1. Introducción
4
2. Los Algoritmos Genéticos
6
2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
2.2. Conceptos Básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.3. Caracterı́sticas de los Algoritmos Genéticos . . . . . . . . . . . . . . . . . .
9
2.4. El Algoritmo Genético Simple . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4.1. Codificación de los Individuos . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2. Generación de la Población Inicial . . . . . . . . . . . . . . . . . . . 14
2.4.3. Evaluación de los Individuos . . . . . . . . . . . . . . . . . . . . . . 14
2.4.4. Selección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.4.5. Cruce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4.6. Mutación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.5. Seudocódigo de un Algoritmo Genético Simple . . . . . . . . . . . . . . . . 22
2.6. El Teorema de los Esquemas
. . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.7. Otros Algoritmos Genéticos: los Algoritmos Evolutivos . . . . . . . . . . . . 34
3. El Problema de Satisfactibilidad
36
3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.2. Conceptos Básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.3. Los Algoritmos Evolutivos para Resolver SAT . . . . . . . . . . . . . . . . . 39
4. Sistemas de “Word Equations”: el Algoritmo de Makanin
48
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.2. Conceptos Básicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1
4.3. El Algoritmo de Makanin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3.1. Representación Gráfica
. . . . . . . . . . . . . . . . . . . . . . . . . 51
4.3.2. Las Ecuaciones Generalizadas . . . . . . . . . . . . . . . . . . . . . . 55
4.3.3. EL Algoritmo de Transformación . . . . . . . . . . . . . . . . . . . . 60
4.4. Casos Particulares de Sistemas de “Word Equations” . . . . . . . . . . . . . 66
5. Un Algoritmo Genético Simple para Resolver l SW ES
71
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.2. Codificación de los Individuos . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.3. Generación de la Población Inicial . . . . . . . . . . . . . . . . . . . . . . . 74
5.4. Evaluación de los Individuos . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.5. Los Operadores Genéticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6. El Algoritmo Genero Problema
88
7. Primeros Resultados Experimentales
91
7.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.2. Tamaño de Población . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
7.3. Probabilidad de mutación . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
8. Algoritmos Evolutivos para Resolver l SW ES
107
8.1. A.E. Sólo Mutación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
8.2. A.E. Búsqueda Local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
8.3. Resultados experimentales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
9. Experimento final
125
A. El Algoritmo Genético Simple
128
B. El Algoritmo Genero Problema
145
C. Los problemas propuestos
157
C.1. El problema 10-15-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
C.2. El problema 10-15-5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
C.3. El problema 10-3-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
C.4. El problema 10-5-1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
2
C.5. El problema 10-5-2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
C.6. El problema 10-8-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
C.7. El problema 10-8-5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
C.8. El problema 12-6-4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
C.9. El problema 15-12-4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
C.10.El problema 15-25-5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
C.11.El problema 15-7-5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
C.12.El problema 25-23-4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
C.13.El problema 25-8-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
C.14.El problema 25-8-5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
C.15.El problema 5-15-3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
C.16.El problema 5-3-2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
D. A.E. Sólo Mutación
168
E. A.E. Búsqueda Local
179
Bibliografı́a
204
3
Capı́tulo 1
Introducción
En este trabajo hemos intentado reunir los resultados obtenidos a lo largo del curso
que comenzamos con el objetivo de diseñar un algoritmo que proporcione una solución a
un sistema de ecuaciones conocido como sistema de “word equations”.
El capı́tulo 2 lo dedicamos al estudio del tipo de algoritmos que pretendemos usar
para resolver estos sitemas, los llamados algoritmos evolutivos. Podrı́amos decir que en
este capı́tulo se encuentran las herramientas básicas necesarias para su diseño. Además,
como aplicación de los mismos a la resolución de diferentes problemas, estudiamos en el
capı́tulo 3 los llamados problemas SAT y explicamos el funcionamiento de los algoritmos
evolutivos más conocidos para su resolución. Algunos de estos algoritmos nos servirán de
referencia a la hora de diseñar el nuestro pues, como veremos, los problemas SAT se
reducen a sistemas de “word equations”.
En el capı́tulo 4 introducimos el problema que pretendemos resolver: los sistemas de
“word equations”, e intentamos resumir la teorı́a que ya ha sido desarrollada sobre estos
sistemas, destacando las dificultades que aparecen en su resolución, pues aunque existe
un algoritmo que los resuelve, el algoritmo de Makanin, éste no se puede usar en la
práctica por ser un algoritmo con una complejidad triplemente exponencial.
En los siguientes capı́tulos explicamos las distintas propuestas que emplearemos para
el diseño del algoritmo evolutivo.
Nuestro trabajo propiamente dicho comienza en el capı́tulo 5 diseñando un algoritmo
evolutivo denominado algoritmo genético simple, cuyos primeros resultados experimentales aparecen en el capitulo 7. En el capı́tulo 6, hacemos un pequeño paréntesis
para diseñar un algoritmo que construya sistemas de “word equations”, pues no existen
4
documentos donde podamos encontrar estos problemas.
En el capı́tulo 8, obtenemos nuevos algoritmos evolutivos añadiendo algunas modificaciones al algoritmo genético simple. Los resultados experimentales nos permitirán comprobar las mejoras obtenidas al añadir búsqueda local al algoritmo genético simple.
Finalizamos exponiendo los resultados obtenidos del mejor de los algoritmos evolutivos
que hemos diseñado a lo largo de este curso.
Todos los algoritmos diseñados se encuentran en los distintos apéndices, escritos en el
lenguaje C++. También aparecen algunos de los sistemas de “word equations” que hemos
utlizado en los diferentes experimentos.
5
Capı́tulo 2
Los Algoritmos Genéticos
2.1.
Introducción
Un algoritmo genético es un proceso de búsqueda basado en los mecanismos de la
evolución biológica: selección natural, reproducción y mutación.
Fueron desarollados por John Holland [12] , que desde pequeño se preguntaba cómo
la naturaleza logra crear seres cada vez más perfectos. Aunque esto no es totalmente
cierto, resulta curioso observar que todo tiene lugar mediante interacciones locales entre
individuos.
De la lectura del libro titulado “La teorı́a genética de la selección natural” aprendió que
la evolución era una forma de adaptación más potente que el simple aprendizaje.
Los objetivos de su búsqueda han sido dos:
Abstraer y explicar rigurosamente los procesos de adaptación de los sistemas naturales.
Diseñar sistemas artificiales que conserven los mecanismos de los sistemas naturales.
Su primer monográfico sobre los algoritmos genéticos se publicó en 1975, “Adaptation in
Natural and Artificial Systems”. Algunos artı́culos y tesis posteriores establecen la validez
de la técnica en la optimización de funciones y aplicaciones de control.
Los algoritmos genéticos son computacionalmente simples, aunque potentes en su
búsqueda y mejora, y han sido aceptados como una aproximación válida a problemas
que requieren búsqueda eficiente y eficaz. Además, no están limitados por supuestas restricciones sobre el espacio de búsqueda, como la continuidad o la existencia de derivadas.
6
2.2.
Conceptos Básicos
Definición 1
Se llama alfabeto a un conjunto finito de sı́mbolos dados.
Se denotará por A.
Ejemplo 1
Los siguientes conjuntos pueden ser alfabetos
1. { 1, 3, 5, 7, 9}
2. { a, b, c,. . . , x, y, z}
3. {α, β, γ}
4. {si, no, inicio, fin}
5. { 0, 1}
Definición 2
Una cadena o palabra es una sucesión ordenada y finita de sı́mbolos de un alfabeto.
Se denotará por ω.
Definición 3
El número de sı́mbolos que constituyen una cadena ω se llama longitud de ω.
Se denotará por |ω|.
Ejemplo 2
ω = 10011 es una cadena de longitud |ω| = 5 del alfabeto binario A = { 0, 1}.
Definición 4
Se dice cadena vacia a la cadena que no contiene sı́mbolos y por lo tanto tiene longitud
cero.
Se denotará por Λ.
Definición 5
El exponente de periodicidad de una cadena ω es el máximo número p tal que
ω = uv p z
donde u, v y z son cadenas con v 6= Λ.
7
Definición 6
Dado un alfabeto A.
Se define el conjunto de todas las cadenas que se pueden formar con sı́mbolos de A como
A∗ = {Λ} ∪ A ∪ A2 ∪ . . . ∪ An ∪ . . . =
[
Ak
k≥0
siendo Ak el conjunto de cadenas de longitud k.
Definición 7
Sea A un alfabeto.
Dadas dos cadenas ω1 y ω2 sobre A se define la concatenación de ω1 y ω2 como la cadena
que resulta de colocar primero ω1 y a continuación ω2 .
Se denotará ω1 ω2 .
Propiedades de la concatenación:
Asociativa,
( ω1 ω2 ) ω3 = ω1 ( ω2 ω3 )
∀ ω1 , ω2 , ω3 ∈ A.
Existencia de elemento neutro,
Λω=ω Λ
∀ ω ∈ A.
|ω1 ω2 | = |ω1 | + |ω2 |
En general, no es conmutativa.
Ejemplo 3
Si ω1 = ab y ω2 = b entonces
ω1 ω2 = abb 6= bab = ω2 ω1
Nota 1
El alfabeto A con la operación de concatenación tiene estructura de semigrupo con identidad.
Nota 2
Será frecuente usar el término “bit” para referirnos a los sı́mbolos de un alfabeto, en
particular, cuando trabajemos con el alfabeto binario A = { 0, 1}.
8
2.3.
Caracterı́sticas de los Algoritmos Genéticos
Algunas de las principales caracterı́sticas de los algoritmos genéticos, que los diferencia
de otros procesos de optimización, son las siguientes:
Uso directo de un código.
Trabajan con una codificación del conjunto de variables del problema, no con las
propias variables.
En general, se codifican como cadenas de algún alfabeto.
Búsqueda de una población.
Buscan un conjunto de puntos, no un único punto.
Mientras que algunos métodos de optimización se mueven en el espacio de decisión
desde un único punto al siguiente usando reglas de transición, los algoritmos genéticos
trabajan simultaneamente con un conjunto de puntos, reduciéndose al máximo la
probabilidad de encontrar un falso óptimo.
Ceguedad sobre información auxiliar.
Usan la información de la función objetivo del problema de optimización, no sus
derivadas u otros conocimientos auxiliares.
Aleatoriedad controlada.
Utilizan reglas de transmisión probabilı́sticas, nunca reglas determinı́sticas.
Es importante no confundir los métodos de búsqueda estrictamente aleatorios con las
técnicas aleatorias. El algoritmo genético es un ejemplo de un proceso de búsqueda
que utiliza selección aleatoria como una herramienta para guiar una búsqueda muy
explosiva mediante una codificación sencilla de un espacio paramétrico. No estamos
hablando de un simple proceso aleatorio, es decir, no se trata de tomar una decisión a
cara o cruz, sino que definimos un método de búsqueda que aprovecha la información
histórica para especular sobre nuevos puntos.
Estas cuatro caracterı́sticas contribuyen a dar fuerza a los algoritmos genéticos y resultan de gran utilidad, por encima incluso de otras técnicas usadas con más frecuencia.
9
Además, la habilidad del algoritmo genético para explotar la información acumulada sobre un espacio de búsqueda permite su aplicación a espacios de búsqueda grandes,
complejos y poco entendidos.
Por lo tanto, los algoritmos genéticos tratan de alcanzar un equilibrio entre dos objetivos aparentemente en conflicto: explotar las buenas cualidades de las candidatas a
solución y explorar el espacio de búsqueda.
La simplicidad de las operaciones y la gran eficacia del proceso son dos de los principales
atractivos de los algoritmos genéticos. Veremos que la explicación de por qué este proceso
funciona es mucho más sutil.
2.4.
El Algoritmo Genético Simple
Antes de examinar el funcionamiento interno de un algoritmo genético simple y estudiar
su potencia, tenemos que tener claros cuáles son nuestros objetivos cuando decimos que
queremos optimizar una función o un proceso. Es evidente que el objetivo más importante
de la optimización es la mejora.
Definición 8
Se llama individuo a cualquier cadena candidata a ser solución del problema que se pretende resolver mediante un algoritmo genético.
Un conjunto finito de individuos se dice población.
Definición 9
Se llama función fitness, de calidad, o de aptitud... a aquella función que determina los
individuos mejor adaptados o, de alguna manera, más cercanos a la solución del problema.
Para seleccionar una función fitness adecuada hay que hacer un balance entre aquellas que
reflejen diferencias muy grandes (provocando una convergencia prematura de los individuos de la población hacia un óptimo local) y las que proporcionen diferencias pequeñas
(dando lugar a un estacionamiento).
En ocasiones se añade un factor de penalización para controlar los individuos que no
responden a las caracterı́sticas de las soluciones del problema.
El algoritmo genético simple está constituido principalmente por tres etapas bien diferenciadas: selección, cruce y mutación. Sin embargo, en la práctica es necesario especificar
para su diseño los siguientes aspectos:
10
la codificación de las variables del problema que constituirán los individuos
el modo de generar la población inicial
la función fitness que evaluará la población
el criterio de selección
el tipo de cruce
y la forma en que se aplicará el proceso de mutación.
2.4.1.
Codificación de los Individuos
Los operadores genéticos (selección, cruce y mutación) dependen del tipo de representación utilizado. Originalmente, las soluciones se representaban por cadenas binarias,
es decir, listas de ceros y unos. Este tipo de representación permite definir fácilmente el
proceso de cruce. Sin embargo, en algunos problemas resulta poco natural y eficiente. Por
este motivo, es importante estudiar el problema que se pretende resolver para determinar
la representación más adecuada de los individuos y, en consecuencia, de la solución que se
busca.
Ejemplo 4
Consideramos el problema del viajante dado por 5 ciudades y 20 aristas.
Hay que calcular un camino cerrado que pase por todas las ciudades, sólo una vez por
cada ciudad, y tal que la distancia recorrida sea mı́nima.
Sean C1 , C2 , C3 , C4 y C5 las ciudades que hay que recorrer.
Suponemos que el viajante parte siempre de la ciudad C1 .
Tenemos que codificar cinco variables, una por cada una de las ciudades del problema.
Proponemos codificarlas como cadenas de longitud 4 del alfabeto A = { 0, 1} pues a cada
ciudad llegan y salen cuatro aristas. De esta forma, representamos para cada ciudad las
aristas que salen de esta y le asignamos el valor uno a aquella que forma parte del camino
elegido. Ası́, la cadena
0010
|{z } 0100
|{z} 0010
|{z } 1000
|{z }
|{z } 1000
C1
C2
C3
C4
C5
representa una candidata a solución sobre las aristas ordenadas.
11
C2
0
C4
0
0 1 C3
C1
C3
1
C4
0
0
C5
0
C1
0
C5
1
1 1 C5
...
0
0
C2
Sin embargo, no resulta una representación natural y, además, no todos los individuos que
tienen cinco unos representan una solución del problema.
La cadena
11101100000000000000
ni siquiera representa un camino en el grafo de bits correspondiente, pues cada subcadena
representando a una ciudad debe contener un uno y tres ceros. Ası́ mismo, la cadena
01001000001000101000
aunque si representa un camino no permite recorrer todas las ciudades y no puede ser
aceptada como solución candidata del problema.
Con esta representación de las
variables nos vemos obligados a
controlar las cadenas generadas
para garantizar que se visitan
todas las ciudades una sóla vez
cada una. Después, comprobamos
cuál es la que hace mı́nima la
distancia recorrida.
C2
M
w
1 C3
C1 )
]
C4
C5
Observamos que las posibles soluciones son de la forma
C2
7
C2
w
C2
k
M
C3
C1 i
1 C3
/
C1
C5
C3
6
C1
C4
C5
12
C4
]
q C4
?
C5
Por lo tanto, resulta más natural representar la ruta de las ciudades por una permutación,
digamos
(5, 4, 2, 3)
sobre el alfabeto A0 = {2, 3, 4, 5} que permite definir diferentes poblaciones estables, es
decir, todos los individuos ası́ generados representan posibles soluciones.
La permutación (5, 4, 2, 3) se interprata de la siguiente forma: partiendo de la ciudad C1
el viajante se desplaza hasta la ciudad C5 , de ahı́ se mueve a C4 , después sigue hacia C3
pasando primero por la ciudad C2 y finalmente vuelve a C1 .
2
] ~3
1
9
~
4
5
3
Con esta representación sólo
tenemos que preocuparnos de
encontrar la permutación que
haga mı́nima la distancia necesaria
para recorrer todas las ciudades.
Además, el problema del viajante en su enunciado general no exige que el camino comience
en una ciudad concreta sino que se enuncia en los siguientes términos:
“Construir, si existe, un camino de coste mı́nimo que
pase por cada una de las cinco ciudades una sóla vez”
Con este enunciado y suponiendo, como hasta ahora, que todas las ciudades estan comunicadas entre sı́ en ambos sentidos, la mejor representación de los individuos está formada
por permutaciones de cinco elementos, pues la representación binaria necesitarı́a además
indicar cuál es la ciudad inicial del camino para cada individuo.
Aunque existen múltiples tipos de representación, la mayorı́a de los resultados en la
teorı́a de los algoritmos genéticos, como el teorema de los esquemas que estudiaremos
posteriormente, han sido desarrollados sobre el alfabeto binario.
Nota 3
Una codificación correcta es una de las claves más importantes para tener exito en la
resolución de los problemas.
13
2.4.2.
Generación de la Población Inicial
Una vez definida la codificación de los individuos, el primer paso en un algoritmo
genético es la creación de una población inicial de soluciones del problema, debidamente
codificada.
Normalmente, se fija el tamaño de la población y se generan de forma aleatoria los
individuos para conseguir una población inicial amplia y representativa del espacio de
búsqueda.
En los últimos tiempos, se han ido incorporando métodos heurı́sticos a este proceso con
el fin de generar individuos en la población inicial con un buen fitness. En este caso, hay
que garantizar la diversidad estructural de estos individuos para tener una representación
de la mayor parte de la población posible o, al menos, evitar la convergencia puntual.
También existen algoritmos donde el tamaño de la población es variable.
2.4.3.
Evaluación de los Individuos
Cada vez que se genera una población de individuos es necesario evaluarlos para obtener
una medida de calidad, es decir, el fitness asociado a cada individuo. Este valor es utilizado
en el proceso de selección para la obtención de las sucesivas generaciones.
Como ya se mencionó, la elección del fitness que guiará el proceso de búsqueda será una
de las claves para garantizar el buen funcionamiento del algoritmo genético.
Ejemplo 5
En el problema del viajante anterior, en su versión general, generamos de forma aleatoria
una población de tamaño tp = 3.
Como se trata de un problema de minimización podemos tomar como función fitness f la
función que nos proporciona la distacia recorrida. De este modo, un individuo será mejor
que otro si la función fitness asociada tiene un valor más pequeño.
Definimos dij la distancia entre las ciudades Ci y Cj para i, j = 1, 2, 3, 4, 5 con i 6= j.
Suponemos que
d12 = 12 Km
d13 = 40 Km
d14 = 31 Km
d15 = 65 Km
d23 = 26 Km
d24 = 13 Km
d25 = 78 Km
d34 = 43 Km
d35 = 37 Km
d45 = 52 Km
Se tiene la siguiente tabla
14
Individuo
fitness
I1 = (1, 4, 2, 3, 5)
172
I2 = (2, 3, 5, 4, 1)
158
I3 = (3, 1, 4, 2, 5)
199
Luego, la ruta que proporciona el individuo I2 es la mejor de las tres rutas de la población.
2.4.4.
Selección
La selección es un operador genético mediante el cual cada individuo es copiado de
acuerdo a los valores que le asocia la función fitness. Se trata de una versión artificial de
la selección natural, donde el ser mejor adaptado tiene mayor probabilidad de sobrevivir.
Pensemos en la función fitness como una medida de ganancia, utilidad o bondad que
queremos maximizar. Los individuos que tienen asociado un valor más alto de la función
fitness tienen una mayor probabilidad de contribuir en uno o más descendientes en la
siguiente generación.
La implementación en forma algorı́tmica del proceso de selección se realiza mediante
una ruleta, donde a cada individuo de la población le corresponde una sección circular
directamente proporcional a la probabilidad que tiene de contribuir en la siguiente generación. De esta forma, cada vez que se tiene que seleccionar un individuo se realiza una
tirada de bola en la ruleta tomando aquel asociado a la sección circular donde cayó la
bola.
La probabilidad de contribuir en la siguiente generación viene dada por
P(i) =
f itness(i)
tp
X
f itness(j)
j=1
para cada individuo i = 1, . . . , tp.
Ejemplo 6
Dada la función
f : N −→ N
x −→ x2
¿Cuál es el valor máximo que alcanza f en el intervalo entero [0, 31]?
Codificamos la única variable del problema, x, como una cadena de longitud 5 en el alfabeto A = { 0, 1}. De esta forma, podemos utilizar la numeración binaria para relacionar
el valor de x con su correspondiente codificación.
15
0 −→ 00000
4 −→ 00100
17 −→ 10001
31 −→ 11111
Como se trata de un problema de optimización (maximizar f ) podemos tomar como
función fitness la propia función f .
Suponemos un tamaño de población tp = 4.
Consideramos la población inicial, generada de forma aleatoria,
I1 = 01010
I2 = 01001
I3 = 11100
I4 = 00010
Al evaluar estos indivios se tiene la siguiente tabla
Individuo
Pob. Inicial
Valor de x
f (x) = x2
I1
01010
10
100
0.19
I2
01001
18
324
0.6
I3
11100
7
49
0.09
I4
00010
8
64
0.12
P (i) = fi /
P
fj
Se deduce que el individuo I2 es el que más probabilidad tiene de contribuir en la generación siguiente, por eso, la sección circular que le corresponde en la ruleta es la más grande.
qqqqq
qqqqqqqqqqqqqq qqqqqqqqqqqqqq
qqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqq
q
q
q
q
qqqqqq qqqq
qqqq qqqqq
qqqqq qqqq
qqq qqqqq
qqqq qqq
qq qq qqqqq 19 %
12 % qqqqq qqqqq
qqq qq
qqq qqq
q q
qq qq
9 % qqqqq qqqq
qqqq qqqq
q q
qqq qq
qq qqqq
qqq qqqq
q
qq qq
qqq qq
qqq qqqq
qq qqqq
q
60
%
q
qqq qqqqq
qqqq qq
qqqq qqqqq
qqqqq qqqqqqq
qqqqq qqqqqqqqq
q
q
q
q
q
q
q
qqqqqqq qqqqqqqqqqqqqqqqqqqq qqqqqqqq
qqqqqqqqqq
q
qqqqqqqqqqqqqqqqqqqqqqqqq
Nota:
La selección es con reemplazamiento,
es decir, un mismo individuo puede
ser copiado varias veces. Por lo tanto,
los individuos mejor adaptados tienen
mayor probabilidad de sobrevivir.
Existen otras técnicas para la selección de los individuos, como las denominadas:
Torneo: Se seleccionan dos individuos aleatoriamente y se elige el más apto con una
probabilidad p fijada. (el menos apto se elige con probabilidad (1 − p))
16
Ranqueo: Se ordena la población según el fitness de cada individuo y se le asigna la
probabilidad de selección de acuerdo a la posición que ocupa.
2.4.5.
Cruce
Un sencillo proceso de cruce, denominado cruce en un punto, se realiza en dos etapas.
En la primera, los individuos de la población elegidos de acuerdo al criterio de selección
establecido son emparejados al azar.
Recuérdese que una pareja puede estar formada por el mismo individuo repetido.
En la segunda etapa, cada par de individuos experimenta un cruce como sigue:
1. Se selecciona al azar una posición entera entre uno y la longitud de la cadena, l,
menos uno.
Sea k ∈ {1, . . . , l − 1} la posición seleccionada al azar.
2. Se obtienen dos nuevas cadenas al intercambiar las subcadenas situadas entre las
posiciones k + 1 y l, ambas inclusive.
Ejemplo 7
Consideramos las cadenas w1 y w2 de la población inicial en el ejemplo anterior,
w1 = 01010
w2 = 01001
Como la longitud de estas cadenas es l = 5, tenemos que elegir la posición k como valor
entero entre 1 y 4.
Suponemos k = 4 (como indica el sı́mbolo separador | )
w1 = 0101|0
w2 = 0100|1
El cruce resultante produce dos nuevas cadenas,
w10 = 01011
w20 = 01000
Ası́ se genera una nueva población de individuos que hereda parte de la información de la
población anterior.
17
Nota 4
Se pueden seleccionar tantos individuos de la población actual como sean necesarios para
generar una nueva población que reemplace completamente a la actual.
Sin embargo, en muchas ocasiones se copian los N mejores individuos de la población actual
a la siguiente de modo que sólo se seleccionan los individuos necesarios para completar la
nueva población. Este tipo de selección se denomina elitista.
Normalmente, se suele copiar el mejor o los dos mejores individuos de cada población a la
siguiente. De esta forma se mantiene siempre en la población al mejor individuo alcanzado
hasta el momento.
Existen muchos otros procesos de cruce que se van adaptando a las necesidades concretas de cada problema. A continuación definimos algunos de los más conocidos.
Definición 10 (Cruce en n puntos)
Se dice cruce en n puntos cuando dadas dos cadenas de longitud l se eligen n posiciones
enteras
{ki }i=1,...,n tal que ki ∈ {1, . . . , l − 1}
y se intercambian las subcadenas situadas entre estas posiciones.
Se trata de la generalización del cruce en un punto.
Ejemplo 8
Cosideramos dos individuos de longuitud l = 15 dados por
010101000001000
100100101011101
Vamos a aplicar un cruce de 4 puntos. Para ello seleccionamos al azar cuatro posiciones
enteras entre 1 y 14.
Supongamos que k1 = 3, k2 = 7, k3 = 9 y k4 = 13, entonces
010|1010|00|0010|00
: 100|1010|01|0010|01
100|1001|01|0111|01
z 010|1001|00|0111|00
Hemos obtenido dos nuevos individuos,
100101001001001
010100100011100
18
Definición 11 (Cruce uniforme)
Se dice cruce uniforme cuando se cruzan dos cadenas dadas de longitud l mediante un
proceso que compara los sı́mbolos que ocupan las mismas posiciones en ambas cadenas, es
decir, para cada posición i = 0, . . . , l se compara el sı́mbolo que ocupa la i ésima posición
de la primera cadena con el que ocupa la i ésima posición de la segunda cadena.
El más utilizado cruce uniforme, genera una nueva cadena cruzando dos cadenas como
sigue:
para cada i = 0, . . . , l,
si los simbolos que ocupan la i ésima posición coinciden, entonces se copia dicho sı́mbolo
a la posición i ésima de la nueva cadena, en otro caso, se elige uno de los dos sı́mbolos
comparados (con probabilidad p se elige el de la primera cadena y con probabilidad (1-p)
el de la segunda) y se copia a la i ésima posición de la nueva cadena.
Ejemplo 9
Dadas dos cadenas sobre el alfabeto A = { 0, 1}
ω1 = 01010111
ω2 = 00101111
Obtenemos una nueva cadena aplicando un cruce uniforme que elige el sı́mbolo de ω1
con probabilida 0,3 y el sı́mbolo de ω2 con probabilidad 0,7 cuando los sı́mbolos que se
comparan no coinciden.
Comparamos los sı́mbolos que ocupan la primera posición, como coinciden se copia ese
sı́mbolo a la nueva cadena.
ω1 = 01010111
-
ω2 = 00101111
0
···
Al comparar los sı́mbolos que ocupan la segunda posición, y teniendo en cuenta la probabilidad de cruce, ha resultado que el sı́mbolo de ω1 se copia a la nueva cadena.
ω1 = 01010111
-
ω2 = 00101111
0
1
···
Repitiendo el proceso hasta la última posición de ambas cadenas podrı́a obtenerse
ω1 = 01010111
-
ω2 = 00101111
19
0
1
1
0
1
1
1
1
Existen otros criterios para definir el operador de cruce uniforme, como por ejemplo:
Cuando dos sı́mbolos no coincidan se intercambian entre sı́ con una determinada
probabilidad, dando lugar a dos nuevas cadenas.
Fijada una de las dos cadenas, se recorre sı́mbolo a sı́mbolo. Cada vez que se encuentre un uno se compara con el sı́mbolo que ocupa la misma posición en la otra
cadena. Si no coinciden, entonces se intercambian estos dos sı́mbolos entre sı́, dando
lugar a dos nuevas cadenas. Además, se puede imponer una probabilidad de cambio.
Ejemplo 10
Dadas dos cadenas sobre el alfabeto A = { 0, 1}
ω1 = 11001001
ω2 = 10011101
Para aplicar el cruce uniforme consideramos el criterio que intercambia dos sı́mbolos distintos con probabilidad p = 0,5.
Empezamos a recorrer los sı́mbolos de ambas cadenas y encontramos los dos primeros que
no coinciden
11001001
10011101
Al aplicar la probabilidad de intercambio resulta que no se cambian.
Seguimos recorriendo las dos cadenas. Llegamos al siguiente punto donde los sı́mbolos de
ambas cadenas no coinciden
11001001
10011101
Ahora sı́ se intercambian. Se obtienen dos nuevas cadenas
11001001
:
11011001
10011101
z
10001101
Este proceso se repite hasta que hayamos recorrido todos los sı́mbolos.
Los nuevos individuos que resultan de aplicar el cruce uniforme vienen dados por
11011101
10001001
20
Los procesos de selección y cruce son sorprendentemente sencillos, involucrando generación aleatoria de números, copias de cadenas y algún intercambio parcial de cadenas.
No obstante, el énfasis combinado de selección, aleatoriedad controlada e intercambio
de información del cruce dan a los algoritmos genéticos gran parte de su potencia. Puede
parecer sorprendente que dos operaciones tan simples resulten útiles en algún caso. Incluso,
parece raro que el azar juege un papel tan fundamental en un proceso de búsqueda directo.
2.4.6.
Mutación
El operador de mutación juega un papel secundario en el algoritmo genético simple.
Según algunos estudios, la frecuencia de mutación que permite obtener buenos resultados
en la mayor parte de los problemas es del orden de una mutación por mil bits. Se sabe
que en la naturaleza la probabilidad de mutación es aún más pequeña. Por eso podemos
concluir que la mutación es considerada, apropiadamente, un mecanismo secundario. Sin
embargo, este proceso nos permite dar un salto en otra dirección en el espacio de busqueda,
evitando la convergencia prematura a óptimos locales. Se trata de un mecanismo generador
de diversidad.
Uno de los procesos de mutación más sencillos y utilizados consiste en reemplazar el
valor de un bit de la población con cierta probabilidad. Otra forma de introducir nuevos
individuos en una población es la recombinación de éstos tomados al azar, sin tener en
cuenta el fitness.
21
2.5.
Seudocódigo de un Algoritmo Genético Simple
Una vez estudiados los elementos necesarios para construir un algoritmo genético simple, vamos a describir el funcionamiento del mismo.
Fijados el tamaño de la población tp y la función fitness f que guiará el proceso, se
genera una población inicial P0 donde cada individuo se obtiene de forma aleatoria. En la
siguiente etapa, se evalúan los individuos de la población P0 para conocer el fitness asociado
a cada uno. Finalmente, se realiza un bucle de iteraciones que consiste en la generación de
nuevas poblaciones a partir de las anteriores mediante los operadores genéticos. De esta
forma, en la iteración k ésima se genera la población Pk a partir de los individuos de la
población Pk−1 usando selección, cruce y mutación.
Cuando finaliza la ejecución del algoritmo, se obtiene una población formada por
buenos individuos, siendo el mejor de ellos la solución que se propone.
Seudocódigo de un AGS
P0 ← generar población inicial
E0 ← evaluar (P0 )
para k = 1 hasta NIter hacer
para j = 1 hasta tp hacer
Ij (1) ← seleccionar de Pk−1
Ij (2) ← seleccionar de Pk−1
Ij ← cruzar (Ij (1), Ij (2))
Ij ← mutar (Ij )
Ej ← evaluar (Ij )
Pk ← guardar (Ij )
fin para
fin para
Podı́amos estar interesados en conservar los N mejores individuos de cada población
en la siguiente, es decir, aplicar una selección elitista.
En el siguiente seudocódigo, guardamos el mejor individuo generado hasta el momento
en la nueva población.
22
Seudocódigo de un AGS elitista
P0 ← generar población inicial
E0 ← evaluar (P0 )
para k = 1 hasta NIter hacer
I ← mejor individuo de Pk−1
Pk ← guardar (I)
para j = 1 hasta tp − 1 hacer
Ij (1) ← seleccionar de Pk−1
Ij (2) ← seleccionar de Pk−1
Ij ← cruzar (Ij (1), Ij (2))
Ij ← mutar (Ij )
Ej ← evaluar (Ij )
Pk ← guardar (Ij )
fin para
fin para
Para finalizar esta sección, estudiaremos el funcionamiento de un algoritmo genético
simple elitista aplicado a un problema concreto.
Ejemplo 11
Se pretende maximizar el valor de la función
f : [ −2, 2 ] −→ R
x −→
exp( x2 ) cos(5x)
(x+3)2
+1
Codificación de los individuos
Tenemos que codificar una única variable x que toma valores reales entre −2 y 2.
Para poder codificarlo como cadena del alfabeto binario A = { 0, 1} definimos la
siguiente traslación:
f˜ : [ 0, 4 ] −→ [ −2, 2 ] −→ R
x̃ −→ x̃ − 2 −→
) cos(5(x̃−2))
exp( x̃−2
2
((x̃−2)+3)2
+1=
exp( x̃2 −1) cos(5x̃−10)
(x̃+1)2
Suponemos que la solución viene dada con tres cifras decimales. Luego,
0, 000 ≤ x̃ ≤ 4, 000
23
+1
Sabemos que
2048 = 211 < 4000 < 212 = 4096
Por lo tanto, podemos codificar la variable x̃ como una cadena ω de longitud |ω| = 12
en el alfabeto A = { 0, 1}.
Nótese que
111111111111 = 4, 095 > 4, 000
Ası́ que cuando generemos los individuos de la población tendremos que controlar
que el valor asociado a esa cadena es menor o igual que cuatro.
Población inicial
Trabajaremos con poblaciones de tamaño tp = 4.
La población inicial será generada de forma totalmente aleatoria.
Fitness
Como se trata de un problema de maximizar f podemos tomar como fitness la propia
función f .
Criterio de selección
Aplicamos en cada iteración selección elitista proporcional al fitness y que mantega
en la población a los dos mejores individuos alcanzados hasta ese momento.
Cruce
Cada par de individuos seleccionados será cruzado en un punto.
Sólo el mejor individuo obtenido de los dos que resultan del proceso de cruce se
guardará en la nueva población .
Mutación
La probabilidad de que un individuo sea mutado será de 0,7 y la probabilidad de
que un bit cambie su valor será de 1/12 = 0,083.
Empezamos a resolver el problema ejecutando el algoritmo genético simple ası́ diseñado.
Obtenemos la pobalción inicial P0 dada por los individuos:
24
I1 = 011001010010
I2 = 110000100101
I3 = 101110100100
I4 = 010000100001
Se tiene la siguiente tabla asociada a la población inicial P0
Individuo
P0
x̃
f˜(x̃)
P
P (Ii ) = f˜i / f˜j
I10
011001010010
1.190
0.914
0.23
I20
110000100101
2.627
0.896
0.22
I30
101110100100
0.605
1.149
0.28
I40
010000100001
2.114
1.092
0.27
Los dos mejores individuos de P0 , I30 y I40 , pasan directamente a la siguiente población,
P1 .
Individuo
P1
x̃
f˜(x̃)
I11
101110100100
0.605
1.149
I21
010000100001
2.114
1.092
···
···
···
···
Tenemos que generar otros dos individuos mediante los operadores genéticos para completar la población P1 .
Generación de I31 :
I30 = 10|1110100100
: 01|1110100100 con f˜(0,606) = 1,149
I10 = 01|1001010010
z 10|1001010010 con f˜(1,189) = 0,915
Luego, de los procesos de selección y cruce se tiene que I31 = 011110100100. Además, este
individuo es sometido al proceso de mutación. Sin embargo, ninguno de sus bits muta.
Generación de I41 :
I30 = 1011101|00100
: 1100001|00100 con f˜(0,579) = 1,134
I20 = 1100001|00101
z 1011101|00101 con f˜(2,653) = 0,897
25
Luego, de los procesos de selección y cruce se tiene que I41 = 110000100100. Este individuo
también es seleccionado para la mutación. En esta ocasión mutan los sı́mbolos situados en
la sexta y duodécima posición. Se obtiene
I41 = 110001100101
La nueva población P1 queda como sigue
Individuo
P1
x̃
f˜(x̃)
P
P (Ii ) = f˜i / f˜j
I11
101110100100
0.605
1.149
0.27
I21
010000100001
2.114
1.092
0.25
I31
011110100100
0.606
1.149
0.27
I41
110001100101
2.659
0.897
0.21
Ahora, generamos la población P2 .
En primer lugar, copiamos los dos mejores individuos de P1 , I11 y I31 .
Después, para completar la población P2 , generamos dos individuos aplicando los operadores genéticos a P1 .
I31 = 0111|10100100
: 0100|10100100 con f˜(0,594) = 1,143
I21 = 0100|00100001
z 0111|00100001 con f˜(0,126) = 1,088
I11 = 10111010|0100
: 101110100100 con f˜(0,605) = 1,149
I11 = 10111010|0100
z 101110100100 con f˜(0,605) = 1,149
De los procesos de selección y cruce se tiene que
I32 = 010010100100
I42 = 101110100100
Nótese que para obtener I42 hemos cruzado el mismo individuo, I11 .
Además, al individuo I32 se le aplica el operador de mutación. En este caso, los bits situados
en la quinta, sexta y última posición mutan. Se obtiene
I32 = 010001100101
Sin embargo, el individuo I42 no es sometido al proceso de mutación.
Por lo tanto, la nueva población P2 ya está completamente generada.
26
Individuo
P2
x̃
f˜(x̃)
P
P (Ii ) = f˜i / f˜j
I12
101110100100
0.605
1.149
0.26
I22
011110100100
0.606
1.149
0.26
I32
010001100101
2.658
0.897
0.22
I42
101110100100
0.605
1.149
0.26
Para generar la siguiente población, tenemos que elegir los dos mejores individuos de P2 .
Como el mejor fitness se tiene para los individuos I12 , I22 y I42 , elegimos al azar dos. Estos
serán los que pasen directamente a la siguiente población, P3 .
Para completar la población aplicamos los operadores genéticos a la población P2 .
Se obtiene la siguiente tabla para la población P3 .
Individuo
P3
x̃
f˜(x̃)
I13
011110100100
0.606
1.149
0.25
I23
101110100100
0.605
1.149
0.25
I33
011110100100
0.606
1.149
0.25
I43
010010100100
0.594
1.143
0.25
P (Ii ) = f˜i /
P˜
fj
Repetimos los pasos para generar la población P4 .
Individuo
P4
x̃
f˜(x̃)
P
P (Ii ) = f˜i / f˜j
I14
101110100100
0.605
1.149
0.25
I24
011110100100
0.606
1.149
0.25
I34
111110100100
0.607
1.150
0.25
I44
011111100100
0.638
1.163
0.25
Finalizamos este desarrollo generando la población P5 .
Individuo
P5
x̃
f˜(x̃)
P
P (Ii ) = f˜i / f˜j
I15
011111100100
0.638
1.163
0.25
I25
111110100100
0.607
1.150
0.25
I35
111110100100
0.607
1.150
0.25
I45
011011100100
0.630
1.160
0.25
27
Tras estas generaciones, la solución que se propone es
ω = 011111100100 −→ x̃ = 0,638 ∈ [0, 4] −→ x = x̃ − 2 = −1,362 ∈ [−2, 2]
tal que f (x) = f˜(x̃) = 1,163 es el valor máximo alcanzado.
Con ayuda de MATLAB hemos representado la función f en el intervalo de definición
[−2, 2] y hemos calculado el valor máximo de f en dicho intervalo. De modo que se tiene
la siguiente gráfica
Figura 2.1:
tal que
max{f (x) : x ∈ [−2, 2 ]} = 1,177
Con este ejemplo hemos intentado terminar de comprender el funcionamiento del algoritmo genético y al mismo tiempo ver el buen comportamiento del método. En este caso
particular, en pocas iteraciones nos hemos aproximado al óptimo de la función mediante
unas operaciones muy sencillas. Sin embargo, la convergencia al óptimo en este problema
se podrı́a mejorar, pues hemos aplicado un proceso de selección elitista que mantenı́a los
dos mejores individuos encontrados hasta ese momento para una población de tamaño
tp = 4, resulta más interesante mantener en las nuevas poblaciones sólo al mejor individuo
encontrado hasta ese momento con el fin de aumentar la diversidad de las poblaciones
generadas.
Nota 5
En la práctica, el tamaño de población, el número de iteraciones, la probabilidad de mutación... son determinados de forma experimental para cada problema concreto.
28
2.6.
El Teorema de los Esquemas
Uno de los resultados más importantes de la teorı́a de los algoritmos genéticos es el
teorema de los esquemas. Este resultado justifica la convergencia del método usado por
los algoritmos genéticos sobre un alfabeto binario.
Antes de enunciar el teorema tenemos que introducir un nuevo concepto denominado
esquema.
Definición 12
Se llama esquema a una cadena que representa un conjunto de cadenas con similares caracterı́sticas, es decir, cadenas que tienen los mismos sı́mbolos en determinadas posiciones.
El sı́mbolo ∗ se utiliza para indicar las posiciones donde no es obligada la coincidencia, es
decir, indica las posiciones donde puede ir cualquier elemento del alfabeto.
Ejemplo 12
Consideramos las cadenas
01001001
01000010
01101001
01100011
Es evidente que las posiciones 1, 2, 4 y 6 de todas estas cadenas coinciden. Por lo tanto,
la cadena
01 ∗ 0 ∗ 0 ∗ ∗
es el esquema que las representa.
Nota 6
Un mismo conjunto de cadenas puede pertenecer a distintos esquemas.
Definición 13
Sea H un esquema.
Se llama orden de H al número de sı́mbolos en H que son distintos de *.
Se denotará por o(H).
Se llama longitud de H al número de sı́mbolos del alfabeto { 0, 1, ∗ } más uno que hay
entre la primera y la última de las posiciones distintas de *.
Se denotará por δ(H).
29
Nota 7
Si el esquema H posee una única posición distinta de * entonces δ(H) = 0.
Ejemplo 13
Sea el alfabeto binario A = { 0, 1}.
Se tiene la siguiente tabla
Esquema
Orden
Longitud
H1 = 0 1 ∗ ∗ 1 ∗ 0
o(H1 ) = 4
δ(H1 ) = 6
H2 = ∗ 1 ∗ 1 ∗ 1∗
o(H2 ) = 3
δ(H2 ) = 4
H3 = 0 ∗ ∗ 0 1 0 1
o(H3 ) = 5
δ(H3 ) = 6
H4 = ∗ ∗ 0 ∗ 1 ∗ ∗
o(H4 ) = 2
δ(H4 ) = 2
H5 = ∗0 ∗ ∗ ∗ ∗∗
o(H5 ) = 1
δ(H5 ) = 0
H6 = ∗ ∗ ∗11 ∗ ∗
o(H6 ) = 2
δ(H6 ) = 1
Teorema 1 (Teorema de los Esquemas)
Sea H un esquema.
El número de individuos pertenecientes a H en la población k + 1 que resulta de aplicar los
operadores genéticos (selección, cruce y mutación) sobre la población k puede aproximarse
por la desigualdad
m(H, k + 1) ≥ m(H, k) ·
δ(H)
f (H)
· [1 − pc ·
− o(H) · pm ]
L−1
f¯
donde f (H) es el fitness medio de los individuos de m(H, k),
f¯ es el fitness medio de la población Pk ,
pc es la probabilidad de cruce,
pm es la probabilidad de que un bit mute,
L es la longitud de los individuos y
m(H, k) es el número de individuos pertenecientes al esquema H contenidos en la
población k ésima.
Demostración 1
Para obtener esta estimación tenemos que estudiar el efecto individual y combinado de los
operadores genéticos sobre los esquemas contenidos en una población.
30
Sabemos que durante la selección la probabilidad de que un individuo Ii sea seleccionado
viene dada por la expresión P (Ii ) = fi /Σfj . Luego, en n selecciones con reemplazamiento
en la población Pk se obtiene que el número de individuos del esquema H que han sido
escogidos en esas n selecciones viene dado por
m(H, k + 1) = m(H, k) · n · f (H)/Σfj
P
Pero, como f¯ =
fj /n denota el fitness medio de la población, podemos escribir la
estimación anterior como sigue
m(H, k + 1) = m(H, k) · f (H)/f¯
Por la tanto, el número de individuos pertenecientes al esquema H en la siguiente generación es directamente proporcional al fitness medio del esquema H e inversamente
proporcional al fitness medio de la población. Esto quiere decir que los esquemas con fitness
medio mayor que el fitness medio de la población aumentarán su número de individuos
en la siguiente generación, y viceversa. De hecho, el crecimiento de los “buenos” esquemas
será exponencial, mientras que los esquemas “malos” tienden a desaparecer.
En particular, cuando el fitness medio de un determinado esquema H se mantiene por
encima del fitness medio de la población una cantidad cf¯, entonces se tiene
m(H, k + 1) = m(H, k) ·
f¯ + cf¯
= (1 + c) · m(H, k)
f¯
Si suponemos un valor constante de c a lo largo de las distintas generaciones resulta que
m(H, k) = m(H, 0) · (1 + c)k
∀k
Por otro lado, resulta sencillo deducir que la probabilidad de supervivencia de un esquema
H tras el proceso de cruce viene dada por
ps = 1 −
δ(H)
L−1
Si se considera una probabilidad de cruce pc para cada par de individuos emparejados,
entonces la probabilidad de supervivencia de un esquema H se aproxima por
ps ≥ 1 − pc ·
δ(H)
L−1
Ahora, combinando el efecto de la selección y del cruce sobre un esquema H, y teniendo
en cuenta que ambas operaciones son independientes, se obtiene
31
·
¸
f (H)
δ(H)
m(H, k + 1) ≥ m(H, k) · ¯ · 1 − pc ·
L−1
f
Es decir, el esquema H crece o decrece según un factor multiplicativo que, por ahora,
depende de la calidad (su fitness medio) y de la longitud del esquema. Ası́, los esquemas
con fitness medio por encima del fitness medio de la población y de longitud pequeña
crecerán en la siguiente generación.
Finalmente, estudiamos el efecto de la mutación sobre un esquema H.
Es evidente que para que un esquema H sobreviva al proceso de mutación no deben cambiar
ninguno de los sı́mbolos situados en las posiciones fijas, es decir, los sı́mbolos distintos de
*. Por lo tanto, la probabilidad de que un esquema H sobreviva a una mutación viene dada
por
(1 − pm )o(H)
donde o(H) es el número de posiciones fijas de H y (1 − pm ) es la probabilidad de que una
posición fija no mute.
En particular, para valores muy pequeños de pm la expresión anterior puede aproximarse
por (1 − o(H) · pm ).
Para concluir la demostración, tenemos que tener encuenta el efecto conjunto de los
tres operadores genéticos sobre un esquema H, de modo que el número de individuos
pertenecientes a H en la siguiente generación puede aproximarse mediante la siguiente
desigualdad
µ
¶
f (H)
δ(H)
m(H, k + 1) ≥ m(H, k) · ¯ · 1 − pc ·
· (1 − o(H) · pm )
L−1
f
Si despreciamos los términos pequeños, obtenemos la desigualdad buscada
¶
µ
f (H)
δ(H)
m(H, k + 1) ≥ m(H, k) · ¯ · 1 − pc ·
− o(H) · pm
L−1
f
32
Ejemplo 14
Veamos con un sencillo ejemplo el efecto del cruce (en un punto) sobre los esquemas.
Consideramos una cadena de longitud 7,
ω = 0111000
Sean H1 y H2 dos esquemas de ω,
H1 = ∗ 1 ∗ ∗ ∗ ∗ 0
H2 = ∗ ∗ ∗ 1 0 ∗ ∗
Suponemos que el punto de cruce es k = 3.
H1 = ∗ 1 ∗ | ∗ ∗ ∗ 0
H2 = ∗ ∗ ∗ | 1 0 ∗ ∗
Observese que si el individuo que se cruza con ω no posee un 0 en la última posición o
un 1 en la segunda posición, entonces el esquema H1 se destruye al aplicar el operador de
cruce.
Sin embargo, el esquema H2 sobrevive al proceso de cruce pues al menos uno de sus hijos
pertenece a H2 .
Si aplicamos el cruce en otros puntos, podemos concluir que el esquema H1 tiene mayor
probabilidad de desaparecer pues existen muchos puntos que rompen la estructura que
caracteriza dicho esquema. Es decir, existe una relación directa entre la desaparición de
un esquema y lo que hemos definido como su longitud.
En este caso concreto, se tiene que δ(H1 ) = 5 y δ(H2 ) = 1.
Como el punto de cruce se elige aleatoriamente, podemos decir que las probabilidades de
que dichos esquemas sean destruidos vienen dadas por
δ(H1 )/(L − 1) = 5/6
δ(H2 )/(L − 1) = 1/6
En general, la probabilidad de supervivencia de un esquema H tras aplicarle el operador
de cruce viene dada por
1−
δ(H)
L−1
33
2.7.
Otros Algoritmos Genéticos: los Algoritmos Evolutivos
Hasta ahora hemos estudiado el funcionamiento del algoritmo genético simple y hemos
descrito cada uno de los operadores necesarios para su diseño. Estos operadores pueden
redefinirse para adaptarlos a las necesidades concretas de cada problema, pero el algoritmo
genético simple deberá de conservar su estructura interna: generación de una población
inicial y generación de nuevas poblaciones mediante los operadores genéticos. Cualquier
modificación que afecte a la estructura del algoritmo genético darı́a lugar a un algoritmo
de búsqueda denominado algoritmo evolutivo.
En general, se llama algoritmo evolutivo a cualquier algoritmo probabilı́stico que partiendo de una población inicial evoluciona, dando lugar a nuevas poblaciones, con la esperanza de que se hereden las buenas cualidades de las poblaciones anteriores. Por lo
tanto, el algoritmo genético simple es un caso particular de algoritmo evolutivo donde los
operadores de selección, cruce y mutación actúan sobre cada población para generar una
nueva.
Partiendo de un algoritmo genético simple vamos a definir algunas modificaciones que
dan lugar a diferentes algoritmos evolutivos.
Sólo mutación
Si consideramos poblaciones de tamaño tp = 1, no tiene sentido definir el operador de
cruce para generar nuevas poblaciones. En este caso, se tiene un algoritmo evolutivo
que en cada generación muta el único individuo de la población. El nuevo individuo
reemplazará al anterior si el fitness asociado es mejor.
Información heurı́stica
En muchas poblaciones es posible conocer algunas de las caracterı́sticas que deben
tener las candidatas a solución. Por ejemplo, en algunos problemas se puede saber a
priori que una cadena que sea solución del problema debe de empezar por uno. Esa
información puede ser usada en la generación de la población inicial e incluso en los
operadores genéticos.
Convergencia Prematura
Es posible evitar que una población converga a un valor que no nos permita mejorar
para alcanzar la solución. Para ello, una posibilidad es inyectar de forma artificial
34
diversidad mediante la generación aleatoria de nuevos individuos que reemplacen
parcial o totalmente la población actual.
Búsqueda local
En muchos algoritmos genéticos se introduce un nuevo operador que por si sólo constituye un algoritmo de búsqueda. Se trata de un proceso que busca puntos próximos
a uno dado con la esperanza de encontrar el óptimo o, por lo menos, mejorar la
solución. Existen muchas tecnicas para diseñar la búsqueda local, una de las más
extendidas consiste en la modificación de los valores de los sı́mbolos que constituyen
un individuo. Cada vez que se cambia el valor de un sı́mbolo se compara el nuevo
individuo con el anterior, tomando el mejor de los dos. Podrı́amos repetirlo para todos
los valores del individuo. Esta búsqueda local viene a ser un proceso de mutación
dirigido.
En general, la búsqueda local mejora considerablemente los algoritmos. Puede ser
utilizada para mejorar la población inicial de modo que el algoritmo arranca con una
población de óptimos locales. Incluso se puede aplicar a cada población generada.
Ejemplo 15 (Sólo Muatación)
Vamos a construir un algoritmo evolutivo para una población de tamaño tp = 1 que usa
búsqueda local.
Seudocódigo
I0 ← generar población inicial
I0 ← busqueda local (I0 )
I0 ← evaluar (I0 )
para k = 1 hasta NIter hacer
I1 ← copiar (I0 )
I1 ← mutar (I1 )
I1 ← busqueda local(I1 )
E1 ← evaluar (I1 )
I0 ← mejor individuo (I0 , I1 )
fin para
35
Capı́tulo 3
El Problema de Satisfactibilidad
3.1.
Introducción
Una de las muchas aplicaciones de los algoritmos estudiados en el capı́tulo anterior
es la resolución de los llamados problemas de satisfactibilidad o simplemente problemas
SAT.
A lo largo de este capı́tulo analizaremos algunos de los algoritmos evolutivos más importantes para la resolución de SAT. Empezamos introduciendo un poco de teorı́a relacionada
con este problema.
3.2.
Conceptos Básicos
Definición 14 (Problema NP)
Se dice que un problema P es No Determinı́stico Polinomial si existe un algoritmo no
determinı́stico que lo resuelve en tiempo polinomial, es decir, dada una solución del problema P comprobar que efectivamente es solución lleva un tiempo polinomial.
Usaremos la notación NP para referirnos a un problema No Determinı́stico Polinomial.
Definición 15
Se dice que un problema P se reduce a otro problema Q si existe una función f computable
que transforma entradas de P en entradas de Q y tal que x es solución de P si y sólo sı́ f (x)
es solución de Q.
En este caso, un algoritmo para resolver Q proporciona otro algoritmo para resolver P.
Definición 16 (Problema NP duro)
Se dice que un problema P es NP duro cuando cualquier otro problema NP se reduce a él.
36
Definición 17 (Problema NP completo)
Se dice que un problema P es completo para NP o que es NP completo si es NP y, además,
cualquier otro problema NP se reduce a él.
Definición 18 (Variable booleana)
Se dice que una variable es booleana cuando sólo puede tomar los valores verdadero o
falso.
El conjunto booleano viene dado por
B = { falso, verdadero } = { 0, 1}
Por lo tanto, si x es una variable booleana entonces el valor de x está en B.
Definición 19
El problema de satisfactibilidad (SAT) consiste en encontrar un elemento
x = (x1 , . . . , xn ) ∈ Bn tal que f (x) = 1
siendo f : Bn −→ B una función booleana dada.
Si ∃ x ∈ Bn tal que f (x) = 1 entonces el problema se dirá satisfactible. En otro caso, se
dirá insatisfactible.
Proposición 1 El problema SAT es NP completo.
Definición 20
Sea Ω un conjunto finito de variables, Ω = {x1 , . . . , xn }.
Se dice literal sobre una variable xi a su afirmación o negación, denotándose respectivamente por xi o x̄i .
Se llama cláusula a cualquier conjunto finito de disyunciones de literales sobre variables.
Ejemplo 16
Dadas tres variables x1 , x2 y x3 , se pueden definir las siguientes cláusulas:
C1 = x1 ∨ x2 ∨ x3 , formada por tres literales
C2 = x1 ∨ x1 , formada por dos literales sobre la misma variable
Definición 21
Se dice que una función booleana f : Bn −→ B está en forma normal si se expresa como
una conjunción de cláusulas, es decir, si
f (x) = C1 (x) ∧ C2 (x) ∧ · · · ∧ Cm (x)
donde C1 , . . . , Cm son m cláusulas.
37
Ejemplo 17 (Un problema SAT)
Dada la función booleana en forma normal
f : B4 −→ B
x −→ f (x) = C1 (x)∧C2 (x)
donde C1 (x) = x1 ∨ x2 ∨ x3 y C2 (x) = x2 ∨ x3 ∨ x4 .
¿ ∃ x ∈ B4 tal que f (x) = 1 ?
Si tomamos x = (0, 0, 1, 1) ∈ B4 entonces
f (x) = C1 (x) ∧ C2 (x) = (0 ∨ 1 ∨ 0) ∧ (0 ∨ 1 ∨ 0) = 1 ∧ 1 = 1
Por lo tanto, este problema SAT es satisfactible.
Nota 8
Cualquier función de un problema SAT puede expresarse en forma normal sin que esto
suponga pérdida de generalidad. Por ello, toda función booleana asociada al problema
SAT que se considere a partir de ahora estará expresada en forma normal.
Definición 22
Un problema SAT se dice de clase k si todas las cláusulas contienen exactamente k literales
distintos.
Para simplificar la notación hablaremos del problema k SAT en lugar del problema SAT
de clase k.
Observaciones:
1. Mientras que los problemas 2 SAT son resolubles en tiempo polinomial, los problemas
k SAT son NP completos para k ≥ 3.
2. Los algoritmos exactos pueden dar una respuesta definitiva (ser satisfactible o insatisfactible) a algunos problemas concretos, pero tienen una complejidad de tipo
exponencial en el mejor de los casos.
3. Los algoritmos heurı́sticos, es decir, aquellos que aprovechan la información histórica, pueden encontrar soluciones para los problemas SAT, pero no garantizan una
respuesta definitiva a todos los problemas, ya que no pueden determinar con seguridad si el problema es o no satisfactible.
38
3.3.
Los Algoritmos Evolutivos para Resolver SAT
Los algoritmos evolutivos son algoritmos heurı́sticos que han sido utilizados para resolver problemas SAT y otros muchos problemas NP completos.
Como hemos visto en el capı́tulo anterior, el primer paso será la codificación de las
variables del problema y la elección de la función fitness más adecuada para obtener una
solución.
A continuación describimos distintos métodos de codificación para el problema SAT.
Respresentación cadena de bits
El proceso inmediato y, hasta ahora, más seguro para representar una solución candidata de un problema SAT es una cadena de bits de longitud igual al número de
variables, de manera que a cada variable se le asocia un bit.
Para poder implementar el algoritmo genético tenemos que decidir cuál será nuestra
función fitness. La función booleana f del problema SAT puede ser usada como
función fitness pues las soluciones del problema SAT corresponden al óptimo global
de f . Sin embargo, esta aproximación falla porque el algoritmo genético degenera en
pura búsqueda aleatoria cuando todas las soluciones candidatas toman el valor cero
en la función objetivo, a menos que una solución sea encontrada.
Por este motivo se introduce la formulación MAXSAT, donde la función fitness representa el número de cláusulas que se verifican, es decir,
fM AXSAT (x) = C1 (x) + · · · + Cm (x)
siendo Ci (x) el valor de verdad de la i ésima cláusula, para i = 1, . . . , m.
Ejemplo 18
Consideramos el problema SAT dado por la función
f : B6 −→ B
x −→ f (x) = C1 (x)∧C2 (x)
donde C1 (x) = {x1 , x2 , x1 } y C2 (x) = {x3 , x4 , x6 }.
Observamos que la cláusula C1 se verifica siempre, pues aparece x1 y x̄1 .
Proponemos como solución el elemento
x = (x1 , x2 , x3 , x4 , x5 , x6 ) = (1, 0, 0, 1, 1, 0) ∈ B6
39
La codificación de esta solución como cadena de bits es 100110.
La función fitness que guiará el proceso de búsqueda se define como
fM AXSAT (x) = C1 (x) + C2 (x) = 1 + C2 (x)
∀x
El elemento que se propone como solución es en realidad solución del problema pues
C2 (101010) = 1. Por lo tanto,
fM AXSAT (101010) = 2
que es el valor máximo que puede tomar el fitness, es decir, se verifican todas las
cláusulas.
La función fM AXSAT es usada como función fitness en la mayorı́a de los algoritmos
evolutivos para SAT, sin embargo, las dificultades que han ido apareciendo, incluso
para resolver pequeños problemas SAT, han motivado la definición de nuevas funciones fitness que dependen de los mecanismos de adaptación utilizados, con el fin
de lograr una distinción entre las distintas soluciones candidatas.
Respresentación punto flotante
En 1998, Bäck [3] propone transformar los problemas SAT en problemas de optimización continuos, de manera que la optimización numérica de estos problemas se
pueda abordar mediante técnicas clásicas.
Ası́, las soluciones candidatas son representadas por vectores continuos
y ∈ [−1, 1]n
y la función objetivo se define de modo que el óptimo global corresponde directamente a soluciones factibles para el problema SAT. Para ello, se reemplazan las
variables xj por (yj − 1)2 y xj por (yj + 1)2 para cada j = 1, . . . , n. Además, se define
la función fitness
g(x) =
m
X


i=1
siendo m el número de








hij (x) =







n
Y

hij (x)
j=1
cláusulas y
(yj − 1)2
si xj ∈ Ci (x) , x̄j 6∈ Ci (x)
(yj + 1)2
si x̄j ∈ Ci (x) , xj 6∈ Ci (x)
(yj − 1)2 · (yj + 1)2
si x̄j , xj ∈ Ci (x)
1
en otro caso
40
Ahora el objetivo es minimizar la función g. De hecho, valores de g nulos se corresponden con soluciones.
Ejemplo 19
Consideramos el problema 3 SAT dado por la fórmula booleana
f (x) = C1 (x) ∧ C2 (x) ∧ C3 (x)
donde C1 (x) = x1 ∨ x2 ∨ x4 , C2 (x) = x1 ∨ x3 ∨ x4 , y C3 (x) = x2 ∨ x3 ∨ x4 .
La función f : B4 −→ B se transforma en la función continua g : [−1, 1]4 −→ R
definida por
g(y) = (y1 −1)2 (y2 +1)2 (y4 −1)2 +(y1 +1)2 (y3 −1)2 (y4 +1)2 +(y2 +1)2 (y3 +1)2 (y4 −1)2
y nuestro objetivo es minimizar el valor de g.
Ahora, los valores booleanos 0 y 1 son asociados a los valores -1 y 1.
En la implementación del algoritmo, para comprobar si una solución para el problema SAT es realmente representada, se convierten los vectores continuos a -1 y 1,
redondeando los valores negativos y positivos, respectivamente.
A pesar de lo prometedora y original que resulta esta representación, los resultados
obtenidos no mejoran los de la representación como cadena de bits.
Respresentación clausal
Propuesta por Hao [10] en 1995, esta representación resalta los efectos locales de las
variables en las cláusulas.
Para ello, se seleccionan asignaciones de valores localmente consistentes para las
distintas variables en cada cláusula con el fin de encontrar asignaciones globalmente
consistentes.
Ejemplo 20
Volviendo a la función boolena del ejemplo anterior
f (x) = C1 (x) ∧ C2 (x) ∧ C3 (x)
donde C1 (x) = x1 ∨ x2 ∨ x4 , C2 (x) = x1 ∨ x3 ∨ x4 , y C3 (x) = x2 ∨ x3 ∨ x4 .
Existen 8 asignaciones posibles para las variables en la primera cláusula, pero sólo
una de ellas no es viable. En efecto,
sı́ (x1 , x2 , x4 ) = (0, 1, 0) entonces C1 (x) = x1 ∨ x2 ∨ x4 = 0 ∨ 0 ∨ 0 = 0
41
En el resto de los casos, es fácil comprobar que C1 (x) = 1.
Razonando de la misma forma, obtenemos las asignaciones posibles para las otras
cláusulas.
Una combinación de asignaciones posibles para las distintas cláusulas viene dada por
{(x1 , x2 , x4 ) = (1, 0, 0); (x1 , x3 , x4 ) = (1, 1, 0); (x2 , x3 , x4 ) = (0, 1, 1)}
Esta serı́a una candidata a solución, sin embargo, contiene asignaciones inconsistentes para la variable x4 , pues por un lado se tiene que x4 = 0 en la cláusula C1 y,
por otro lado, x4 = 1 para la cláusula C3 . Por lo tanto, esta candidata a solución no
es globalmente consistente.
Siguiendo con este razonamiento, se llega a la candidata a solución
{(x1 , x2 , x4 ) = (1, 0, 0); (x1 , x3 , x4 ) = (1, 1, 0); (x2 , x3 , x4 ) = (0, 1, 0)}
que es globalmente consistente y, por lo tanto, x = (1, 0, 1, 0) es solución del problema.
En este caso, el algoritmo genético tiene que ser guiado por una función objetivo
que refleje la cantidad de inconsistencias entre las distintas asignaciones de variables
para las cláusulas. Esto implica un proceso de búsqueda centrado en las relaciones
entre las variables que, al mismo tiempo, están relacionadas entre sı́ por las distintas
cláusulas.
Aunque Hao argumentó que la eliminación de las asignaciones inviables reduce el
espacio de búsqueda, la representación clausal induce un espacio de búsqueda mucho más grande incluso que el obtenido en la representación cadena de bits para
problemas con muchas cláusulas.
Respresentación “path”
Sugerida por Gottlieb y Voss [6] en 1998, se basa en el hecho de que una solución
viable debe satisfacer al menos una variable en cada cláusula.
La idea consiste en ir seleccionando una variable en cada cláusula mediante un
proceso que recorre todas las cláusulas exactamente una vez cada una. Si no hay
inconsistencia en este proceso, podemos construir un vector de asignaciones.
42
Una función fitness razonable para este tipo de representación debe medir el número
de inconsistencias.
Ejemplo 21
Dado el problema SAT, encontrar x = (x1 , x2 , x3 , x4 ) ∈ B4 tal que
f (x) = 1
siendo
f (x) = (x1 ∨ x2 ∨ x4 ) ∧ (x1 ∨ x3 ∨ x4 ) ∧ (x2 ∨ x3 ∨ x4 )
El camino (x1 , x4 , x3 ) es factible y nos permite construir los vectores de asignación
x = (1, 0, 0, 0) y x0 = (1, 1, 0, 0), mientras que el camino (x1 , x4 , x4 ) contiene una
inconsistencia.
Una caracterı́stica de este proceso, que lo distingue de la representación clausal,
es que permite representar una familia de soluciones factibles en lugar de intentar
determinar exactamente una.
Aunque esta representación parece la más idónea, los resultados obtenidos no han
mejorado los de la representación cadena de bits.
Después de esta breve descripción podemos concluir que la mayorı́a de los algoritmos
evolutivos para resolver problemas SAT utilizan como representación la cadena de bits.
La diferencia se encuentra en el tipo de función fitness utilizada. Además, algunos algoritmos evolutivos incorporan otras caracterı́sticas, no genéticas, como la búsqueda local y
la adaptación.
A continuación describimos algunos de los algoritmos evolutivos más conocidos para
SAT.
SAWEA ( stepwise adaptation of weights )
En 1997, Eiben y van der Hauw [4] proponen una función fitness para resolver
los problemas SAT, principalmente 3 SAT . El algoritmo evolutivo que la utiliza
se denominó SAW y la función fitness se definió como
fSAW (x) = ω1 · C1 (x) + · · · + ωm · Cm (x)
donde los pesos ωi ∈ N, para i = 1, . . . , m, son adaptados para identificar las cláusulas difı́ciles de satisfacer en la actual fase de búsqueda.
43
En la primera fase todos los pesos son inicializados a uno, ωi = 1 para i = 1, . . . , m,
con lo que se utiliza fM AXSAT .
En las siguientes fases, cada 250 evaluaciones de la función fitness, los pesos son
ajustados de acuerdo a la siguiente expresión
ωi ← ωi + 1 − Ci (x∗ )
donde x∗ es el actual individuo mejor adaptado. De esta forma, se incrementan sólo
los pesos que corresponden a cláusulas que no se verifican con x∗ , es decir, los pesos
reflejan la “dureza” de las cláusulas asociadas obligando a enfocar hacia ellas la
búsqueda evolutiva.
En 1998, Bäck identifico las mejores caracterı́sticas de este algoritmo evolutivo y que
se conoce por SAWEA. Estas caracterı́sticas son: tamaño de población tp = 1, un operador de mutación que mute exactamente un bit y un esquema de reemplazamiento
en el cual de cada individuo se generan λ hijos mediante mutaciones, sustituyendo
el mejor de todos ellos a su padre, este tipo de reemplazamiento se denota como
(1, λ∗ ).
Más tarde, Jong y Kosters sugieren aplicar un operador adicional a los individuos
obtenidos de la mutación. Este operador selecciona aleatoriamente algunas cláusulas
y, en aquellas que todavı́a no se verifiquen, muta una variable elgida aleatoriamente.
Esta mejora se conoce por LSAWEA.
RFEA ( refining functions )
En 1998, Gottlieb y Voss [7] introducen una nueva función fitness con el fin de
distinguir las distintas cadenas binarias que tienen asociado un mismo valor fitness
fM AXSAT .
Para ello, definen una función
r : Bn −→ [0, 1)
que guarda el conocimiento heurı́stico adicional y utilizan la función fitness redefinida
fREF (x) = C1 (x) + · · · + Cm (x) + α · r(x)
para guiar el proceso de búsqueda.
44
En la función fREF la constante α > 0 controla la influencia de r. De esta forma, si
usamos un nivel de influencia α ∈ [0, 1), podemos distinguir cadenas que satisfacen
el mismo número de cláusulas.
Una versión mejorada de este algoritmo evolutivo, denominada RFEA, usa una
población de tamaño tp = 4, selección por torneo, y un esquema de reemplazamiento basado en la eliminación del peor individuo. Además, un nuevo individuo es
rechazado si ya está en la población. El operador de mutación consiste en seleccionar
una cláusula de entre las que no se verifican y cambiar el valor de una de las variables
de la cláusula, elegida aleatoriamente.
El principal componente del algoritmo evolutivo, junto con el operador de mutación,
es la función de referencia


n
X
K(xj )vj 


1
j =1


r(x) = 1 +

n
X


2

1+
|vj | 
j =1
donde vj ∈ R es el peso asociado a la variable xj y K : B −→ {−1, 1} viene definida
por K(0) = −1 y K(1) = 1.
La adaptación de vj tiene como objeto escapar del óptimo local y se define como
sigue
vj ← vj − K(x∗j ) · |Uj (x∗ )|
donde x∗ es el actual individuo mejor adaptado y |Uj (x∗ )| denota el cardinal del
conjunto de cláusulas que no se satisfacen y que contienen la correspondiente variable
x∗j .
Existen otras formas de aproximar los pesos pero que no vamos a mencionarlas pues
no serán de utilidad en nuestro trabajo.
FlipGA ( Flip Heuristic )
Se trata de un algoritmo evolutivo con búsqueda local, desarrollado por Marchiori y
Rossi [17] en 1999, que genera nuevos individuos mediante los operadores genéticos
usuales y luego los mejora aplicando un proceso de búsqueda local.
45
Este algoritmo utiliza un tamaño de población tp = 10, proceso de selección proporcional al fitness, y un esquema de reemplazamiento generacional elitista, copiando
los dos mejores individuos de la población actual en la población siguiente. Siempre
se aplica cruce uniforme y un proceso de mutación con probabilidad 0,9, de modo
que, cada bit del individuo que muta cambia de valor con probabilidad 0,5.
La clave de este algoritmo es la aplicación de un proceso de búsqueda local a cada
individuo después de realizar el cruce y la mutación. Este proceso consiste en recorrer de izquierda a derecha los bits de un individuo y cambiar el valor de cada bit
seleccionado si el número de cláusulas que se satisfacen después del cambio menos el
número de cláusulas que se satisfacen antes de cambiar el valor de dicho bit es mayor
o igual que cero. Cuando se han considerado todos los bits del individuo, se mira si
el fitness asociado a ese individuo ha mejorado, en ese caso, se repite el proceso.
La idea de este algoritmo es conseguir explotación y exploración mediante dos módulos bien diferenciados: búsqueda local y operadores genéticos. De esta forma, se puede
controlar mejor el efecto de los distintos módulos y modificarlos facilmente para la
investigación experimental.
ASAP ( Flip Heuristic and Adaptation )
Es una versión de FlipGA, introducida por Rossi [22] en 2000, denominada como
algoritmo evolutivo adaptado para el problema de satisfactibilidad. Se obtiene de
FlipGA al considerar un único individuo, esto es, tamaño de población tp = 1, un
esquema de reemplazamiento, denotado por (1 + 1), en el cual de cada individuo se
genera un hijo mediante mutación que sustituye al padre si es mejor y un mecanismo
adaptado para controlar la diversificación en el proceso de búsqueda.
ASAP actua sobre el individuo como sigue: primero, se aplica siempre el proceso
de mutación y se cambia, para cada j ∈ {1 . . . n}, el valor del j ésimo bit con probabilidad µj ∈ [0, 0,5], donde µj es adaptado durante la ejecución. Luego, el nuevo
individuo es mejorado mediante búsqueda local como en FlipGA. Además, un mecanismo de adaptación basado en la búsqueda tabu es usado para prohibir que cambie
el valor de algunas variables y para controlar la probabilidad de mutación µj .
En la siguiente tabla aparecen las principales caracterı́sticas de los algoritmos evolutivos, más conocidos, para resolver SAT.
46
Caracterı́sticas
SAWEA
RFEA
FlipGA
ASAP
Reemplazamiento
(1, λ∗ )
elimina el peor
generacional
(1 + 1)
Selección
—
torneo
proporcional al fitnes
—
fitness
fSAW
fREF
fM AXSAT
fM AXSAT
Inicialización
aleatorio
aleatorio
aleatorio
aleatorio
Cruce
—
—
uniforme
—
Mutación
muta uno
muta uno
aleatorio
aleatorio adaptado
Búsqueda local
—
—
heurı́stico
heurı́stico
Adaptación
fitness
fitness
—
lista tabu
Nota 9
Todos los algoritmos propuestos utilizan poblaciones iniciales puramente aleatorias.
Por último, hablaremos de la búsqueda local para los problemas SAT. Con este proceso
se pretende explorar el espacio de búsqueda de las asignaciones de valores para encontrar
una solución que maximice el número de cláusulas que se verifican. Se empieza con una
asignación generada de forma aleatoria y se van generando nuevas asignaciones al cambiar
el valor de verdad de una variable simple. La fase crucial de este proceso es la selección de
la variable que cambia. Existen muchas teorı́as y todas incluyen aleatoriedad y memoria.
Uno de los más populares métodos de búsqueda local para SAT es WSAT. Fue desarrollado por Selman [23] en 1994 y McAllester [18] en 1997. En este caso, la selección de
una variable para ser cambiada se realiza en dos etapas. Primero, una cláusula de entre las
que no se verifican es elegida aleatoriamente. Luego, una de las variables de esta clausula
se selecciona para cambiar su valor (pasa de cero a uno o de uno a cero) obteniendose una
nueva asignación de valores.
Para finalizar, observese que WSAT y FlipGA, y en consecuencia ASAP, adoptan
diferentes estrategias de búsqueda. Mientras que una iteración de WSAT actua localmente
sobre una cláusula que no se verifica, modificandola sin afectar al resto de cláusulas que
si se verifican, en FlipGA, cada iteración actua globalmente sobre el problema con el fin
de reducir el número total de cláusulas que no se verifican.
47
Capı́tulo 4
Sistemas de “Word Equations”: el
Algoritmo de Makanin
4.1.
Introducción
A lo largo de este capı́tulo estudiaremos el tipo de problemas que pretendemos resolver
con ayuda de la teorı́a desarrollada sobre los algoritmos genéticos.
Introduciremos el concepto de sistema de “word equations” y algunos de los resultados
conocidos hasta la fecha para su resolución. (ver [9, 21])
Pensemos en un sencillo problema que consiste en determinar si dos cadenas dadas
sobre un alfabeto coinciden. No resulta difı́cil diseñar métodos capaces de resolver este
problema.
Ejemplo 22
Sea A = { a, b} un alfabeto.
Consideramos las cadenas
ω1 = abbabbab
ω2 = abbabab
Es evidente que ω1 6= ω2 .
Una idea tan sencilla como la de recorrer ambas cadenas sı́mbolo a sı́mbolo (de forma
simultánea) y comparar ambos sı́mbolos, nos permite diseñar un método que determina si
ω1 coincide con ω2 .
48
ω1
-
a
b
b
a
b
b
a
···
ω2
-
a
b
b
a
b
b
b
···
⇒ ω1 6= ω2
6
Un problema más difı́cil surge cuando se pretenden encontrar patrones en cadenas
dadas. Pensemos en ecuaciones formadas por cadenas que contienen los sı́mbolos de un
alfabeto dado, y las variables que representan los patrones a determinar. El caso más
sencillo se dá cuando en un lado de la ecuación sólo hay sı́mbolos del alfabeto y las
variables aparecen en el otro lado.
Ejemplo 23
Tenemos que encontrar dos cadenas ω1 y ω2 sobre el alfabeto A = { a, b} tal que al
reemplazar x por ω1 e y por ω2 en la ecuación
xabbxy = aabbaabaaa
ambos lados de la igualdad sean idénticos.
De nuevo, existen muchas formas de calcular ω1 y ω2 . En este caso, es evidente que ω1 = a
y ω2 = abaaa son solución de la ecuación. Pero, no tiene por que ser la única posibilidad.
Nota 10
La búsqueda de patrones en ecuaciones donde uno de los lados es constante (formado
por la cadena texto) y en el otro aparecen los patrones a encontrar (representados por
variables) ha sido muy estudiado y existen algoritmos bastante eficientes que lo resuelven.
En general, podemos plantear el problema de encontrar la solución de una ecuación
formada por cadenas de un alfabeto y tal que en ambos lados aparecen variables a determinar. Como veremos, la resolución de estos problemas no es tan sencilla.
Ejemplo 24
Determinar la solución de la ecuación
xaxbya = bybyax
no resulta una tarea fácil.
Soluciones parciales de este problema se conocen desde hace tiempo. En 1972, Lentin [15],
Plotkin [20] y Siekmann [25] diseñaron procedimientos de semi decisión, es decir, algoritmos que encuentran una solución si existe alguna pero que podrı́an no terminar si no existe
49
solución. En 1971 Hmelevski [11] resuelve el problema para ecuaciones con tres variables.
Finalmente, en 1977 Makanin [16] resuelve el problema mediante el primer algoritmo (y
único conocido hasta la fecha) que encuentra solución si existe alguna y además es capaz
de determinar la no existencia de solución.
Los documentos originales de Makanin estaban enfocados a determinar si la existencia
de solución de este tipo de ecuaciones, denominadas “word equation”, es un problema
decidible. No estaba interesado en la complejidad o en la implementación del algoritmo.
Aunque posteriormente se han realizado mejoras (Pécuchet [19], Abdulrab [1], Jaffar [13], Schuld [24], Koscielski, Pachóski [14], entre otros, consiguieron simplificar algunos
de los detalles técnicos de la demostración, empezaron a aproximar el problema desde un
punto de vista computacional y realizaron un estudio sistemático de su complejidad) el
algoritmo de Makanin sigue siendo inviable en la práctica.
El desarrollo teórico del algoritmo de Makanin que aparece en este documento nos
permitirá comprender su funcionamiento y nos dará una idea de su complejidad computacional.
4.2.
Conceptos Básicos
Sean A = { a1 , . . . , ar } un alfabeto y V = { v1 , v2 , . . .} un conjunto infinito de variables tal que A ∩ V = ∅.
Definición 23
Se llama “word equation” al par
(L, R) ∈ (A ∪ Ω)∗ × (A ∪ Ω)∗
donde Ω ⊆ V es un conjunto finito (de variables).
Se denotará por L = R.
Definición 24
Se llama longitud de la ecuación E ≡ (L, R) al número dado por |E| = |L| + |R|.
Nota 11
Aunque V es un conjunto infinito de variables, en una ecuación E aparecen sólo un número
finito de variables que, en genral, denotaremos por Ω = {x1 , . . . , xn } ⊆ V.
50
Definición 25
Se llama sistema de “word equations” a cualquier conjunto de ecuaciones
{ L1 = R1 , . . . , Lk = Rk }
donde (Li , Ri ) ∈ (A ∪ Ω)∗ × (A ∪ Ω)∗ para cada i = 1, . . . , k.
Definición 26
Un homomorfismo
σ : (A ∪ Ω)∗ −→ A∗
se dice solución de un sistema de “word equations” cuando deja invariantes los elementos
del alfabeto A y además
σ(Li ) = σ(Ri )
para todo i = 1, . . . , k.
Definición 27
Una solución σ de un sistema se dice mı́nimal si la suma
X
|σ(x)| es mı́nima en el
x∈ Ω
conjunto de longitudes de las soluciones.
Definición 28
Dado un sistema de “word equations” sobre un alfabeto A, de conjunto de variables
Ω =
{x1 , . . . , xn }, se llama exponente de periodicidad de una solución σ al máximo
exponente de periodicidad de las cadenas σ(xi ) para i = 1, . . . , n.
4.3.
4.3.1.
El Algoritmo de Makanin
Representación Gráfica
Será muy importante comprender la siguiente representación de las “word equations”
para justificar los conceptos utilizados en el algoritmo de Makanin, ası́ como para entender
su desarrollo. Esta representación es el punto de arranque de dicho algoritmo.
Consideramos la ecuación
xaby = ybax
sobre el alfabeto A = {a, b} y donde x e y representan las variables. Gráficamente, la
cadena xaby será representada por
51
x
b
a
y
siendo la longitud de las lineas horizontales desconocida en cada caso, excepto, las correspondientes a las constantes que son siempre de longitud uno. Las lineas verticales se
llamaran fronteras.
La ecuación posee solución si existe una forma consistente de superponer ambos lados
de la igualdad de manera que los trozos (representados mediante segmentos horizontales)
entre las fronteras coincidan.
En general, existen muchas posibilidades para hacer las superposiciones. Para la ecuación
dada
xaby = ybax
se tienen por ejemplo estas dos posibilidades, entre muchas otras,
x
b
x
a
b
y
y
a
a
b
y
x
y
a
b
x
Nota 12
Dibujamos los sı́mbolos en distintos niveles para destacar las fronteras de cada uno.
En la siguiente etapa, se eliminan los sı́mbolos que coinciden desde la izquierda hasta
la derecha. Por ejemplo, en la representación
x
b
a
y
y
a
b
52
x
podemos reemplazar y = xa en el otro suceso de y, para obtener
b
a
b
x
a
x
=⇒
=⇒
a
b
xa = ax
a
x
b
x
La idea básica del funcionamiento del algoritmo de Makanin es como sigue:
1. Suponer un orden de fronteras, es decir, cuál viene primero, cuál es la segunda, . . .
para todas las fronteras iniciales de ambos lados de la ecuación.
2. Proceder de izquierda a derecha reemplazando igualdades por igualdades.
Sin embargo, este ingenioso método tiene algunos inconvenientes:
El número de veces que aparece una variable puede aumentar después del reemplazamiento.
No siempre son evidentes los reemplazamientos. En el siguiente ejemplo,
x
b
a
y
y
a
b
x
no existe un reemplazamiento evidente.
Este proceso puede entrar en un bucle infinito. En uno de los ejemplos anteriores, se
llega a ax = xa.
53
Por estos motivos es necesario un estudio más elaborado de la representación.
Empezamos construyendo, para cada “word equation”, una representación como la
anterior. Es conveniente, para evitar que el número de ocurrencias de una variable aumente
con los reemplazamientos, trabajar con un sistema equivalente de ecuaciones en el cual
cada variable aparece no más de dos veces en cada ecuación.
Nota 13
Siempre es posible pasar de un sistema de “word equations” dado a otro equivalente donde
todas las variables ocurren a lo sumo dos veces.
Ejemplo 25
Sea la ecuación bxyx = yaxz.
Las variables de esta ecuación son
x que ocurre tres veces
y que ocurre dos veces
z que ocurre una vez
Construimos el sistema equivalente
bx1 yx1 = yax2 z
x1 = x2
para evitar que la variable x aparezca tres veces. Ahora, todas las variables suceden no
más de dos veces en cada ecuación. Una posible representación de este sistema equivalente,
con fronteras ordenadas, vendrı́a dada como sigue:
b
y
x1
bx1 yx1 = yax2 z
x1
x1
=⇒
y
x1 = x2
x2
a
=⇒
x2
z
Notar que ambas ecuaciones pueden ser representada en la misma gráfica de la siguiente
forma:
54
b
y
x1
x1
x2
y
x2
a
1
2
3
z
4
5
6
7
Además, resulta conveniente tener exactamente dos copias de cada variable en la representación. En este caso, tenemos que añadir una copia de la variable z. Ası́, la representación final del sistema viene dada por
b
y
x1
x1
x2
z
y
x2
a
1
2
3
z
4
5
6
7
Esta representación es el punto de arranque del algoritmo de Makanin.
4.3.2.
Las Ecuaciones Generalizadas
Introducimos los conceptos utilizados por Makanin [16, 13, 24] para poder comprender
dicho algoritmo.
Definición 29
Sea (BD, ≤) un conjunto finito de números naturales que denominaremos conjunto de
fronteras.
Se llama base a una tupla
bs = (t, (e1 , . . . , en ))
donde n ≥ 2, t ∈ A ∪ Ω y Ebs = (e1 , . . . , en ) es una secuencia de fronteras ordenados por
≤.
BS denotará un conjunto finito de bases.
55
Definición 30
Una base bs = (t, Ebs ) ∈ BS se dice constante si t ∈ A. En otro caso, es decir, si t ∈ Ω se
dirá base variable.
Definición 31
Sea bs ∈ BS, cualquiera.
Se llama frontera izquierda de la base bs al primer elemento en Ebs .
Se denotará Izq(bs).
Se llama frontera derecha de la base bs al último elemento en Ebs .
Se denotará Der(bs).
Nota 14
Las letras i, j,. . . se usarán para denotar las fronteras.
Ejemplo 26
Sea BD = {1, 2, 3, 4, 5, 6}, A = {a, b} y Ω = {x, y, z}.
Consideramos las bases
bs1 = (a, (1, 3, 5, 7))
bs2 = (y, (1, 2, 4, 6))
Resulta que bs1 es una base constante con Izq(bs1 ) = 1 y Der(bs1 ) = 7, mientras que bs2
es una base variable con Izq(bs2 ) = 1 y Der(bs2 ) = 6.
Definición 32
Se llama ecuación generalizada (EG) a la tupla (A, Ω, BD, BS) tal que
1. Para cada x ∈ Ω hay exactamente dos bases, llamadas duales y denotadas por x y
x̄, respectivamente.
Además, las secuencias de fronteras asociadas, Ex y Ex̄ , deben tener la misma longitud.
2. La secuencia de fronteras de cada base constante tiene exactamente dos elementos
que son consecutivos en el orden ≤.
Ejemplo 27
Sea A = {a, b} y Ω = {x, y}.
Se tiene la siguiente ecuación generalizada:
56
x
y
a
b
b
a
y
1
2 3
x
4
5
6
7
siendo el conjunto de fronteras BD = {1, 2, 3, 4, 5, 6, 7} y el conjunto de bases
BS = {(x, (1, 3)), (x, (5, 7)), (y, (4, 6)), (y, (2, 4)), (a, (3, 4)), (a, (4, 5)), (b, (6, 7)), (b, (1, 2))}.
Definición 33
Se llama columna de una EG a un par (i, j) de fronteras tal que i ≤ j.
Para todo i, la columna (i, i) se dice vacı́a y la columna (i, i+1) se dice indescomponible.
Dada una base bs, cualquiera. Se define
col(bs) = (Izq(bs), Der(bs))
Por lo tanto, una base es vacı́a si su columna es vacı́a.
Definición 34
Una EG se dice que está resuelta si todas sus bases variables son vacı́as.
Definición 35
Se llama unificador de una EG a una función σ que asigna a cada columna indescomponible
de la EG una cadena ω ∈ A ∪ Ω (extendida por concatenación a todas las columnas no
vacı́as de la EG) verificando las siguientes propiedades:
1. Para cada base constante bs = (a, Ebs ),
σ(col(bs)) = a
2. Para todo par de variables duales, x y x̄, y para todo elemento ej ∈ Ex ,
σ(e1 , ej ) = σ(ē1 , ēj )
En particular, σ(col(x)) = σ(col(x̄)).
Nota 15
Si ej ∈ Ex entonces ēj ∈ Ex̄ .
57
Veamos algunas de las propiedades que verifica el unificador σ de una EG.
Si σ(i, i + 1) es no vacı́o para todo i ∈ BD entonces σ se dirá unificador estricto.
|σ(b1 , bm )|, donde b1 y bm denotan el primer y último elemento de BD, respectivamente, es el ı́ndice del unificador σ.
El exponente de periocidad σ es el máximo exponente de periocidad de las cadenas
σ(col(x)), donde x es una variable base.
Para comprender la complejidad de resolver las ecuaciones generalizadas, veremos
que el problema de resolución de sistemas de ecuaciones lineales diofánticas, que es NPcompleto, se reduce a resolver una EG. Por lo tanto, encontrar solución de una EG es un
problema NP duro.
Definición 36
Dada una EG.
Para cada constante a ∈ A se define el sistema asociado de ecuaciones lineales diofánticas,
L(GE, a), dado de la siguiente forma:
1. para cada columna indescomponible (i, i+1) de la EG introducimos una variable zi .
2. para cada par de bases de variables duales
(x, (e1 , . . . , en ) y (x, (ē1 , . . . , ēn )
construimos (n-1) ecuaciones dadas por
X
ej ≤i<ej+1
X
zi =
zi
j = 1,. . . ,n-1
ēj ≤i<ēj+1
3. para cada base constante (t,(i,i+1)) construimos la ecuación

 1 si t = a
zi =
 0 si t 6= a
Lema 1
Si existe unificador de la EG entonces el sistema asociado L(EG, a) es resoluble para cada
constante a ∈ A.
Sabemos que la resolución de los sistemas lineales diofánticos es un problema decidible
pero en cualquier caso NP completo.
58
Definición 37
Una EG se dice que es admisible si su sistema asociado L(EG, a) es resoluble para cada
a ∈ A.
Para finalizar esta sección veremos que cualquier “word equation” se puede escribir
como un conjunto finito de ecuaciones generalizadas.
Lema 2
Dada E una “word equation”, existe un algoritmo GEN que proporciona un conjunto finito
de ecuaciones generalizadas GEN(E) verificando las siguientes propiedades:
1. E tiene un unificador con exponente de periocidad p sı́ y sólo si alguna EG ∈ GEN (E)
posee un unificador estricto con exponente de periocidad p.
2. Para cada EG ∈ GEN (E), toda frontera es la frontera derecha o izquierda de una
base.
Además, toda secuencia de fronteras contiene exactamente a estas dos fronteras.
3. Para cada EG ∈ GEN (E) el número de bases de la EG no supera 2|E|.
4. Toda EG ∈ GEN (E) es admisible.
Ejemplo 28
Sean A = {a, b} y Ω = {x, y, z}.
La “word equation” E ≡ (bxyx, yaxz) tiene asociado el conjunto finito de ecuaciones
generalizadas GEN(E).
Antes de determinar este conjunto GEN(E) notar que es conveniente trabajar con un
sistema equivalente de ecuaciones en el que cada variable ocurre no más de dos veces en
cada ecuación. Esto siempre es posible, basta con redefinir las variables.
En este caso, la ecuación E es equivalente al sistema
bx1 yx1 = yax2 z
x1 = x2
donde cada variable ocurre a lo sumo dos veces.
Por lo tanto, una EG viene dada por
A = {a, b}
Ω̃ = {x1 , x2 , y, z}
59
BD = {1, . . . , 7}
BS = {(b, (1, 2)), (a, (3, 4)), (x1 , (2, 3)), (x1 , (5, 7)), (y, (3, 5)),
(y, (1, 3)), (x2 , (4, 6)), (x2 (5, 7)), (z, (6, 7)), (z, (6, 7))}
Gráficamente se representa como sigue:
b
y
x1
x1
x2
z
y
x2
a
1
4.3.3.
2
3
z
4
5
6
7
EL Algoritmo de Transformación
Como toda “word equation” E posee un conjunto de ecuaciones generalizadas GEN(E)
equivalente, podemos reducir nuestro problema al estudio de la resolución de ecuaciones
generalizadas.
Dada una EG la idea del algoritmo de transformación es ir reemplazando sucesivamente, de izquierda a derecha, las variables iguales. Se empieza con la variable situada
más a la izquierda y más grande, que se denomina portadora, y se transporta toda su
columna a la posición de su dual. Sin embargo, no siempre es posible mover todas las
columnas sin perder parte de la información esencial.
Ejemplo 29
Consideramos la representación de la ecuación xaby = ybax dada por
x
b
a
y
y
a
b
1
2
x
3
4 5
60
6
7
8
El conjunto de bases viene dado por
BS = {(x, (1, 5)), (x, (4, 8)), (y, (7, 8)), (y, (1, 2)), (a, (5, 6)), (a, (3, 4)), (b, (2, 3)), (b, (6, 7))}
Las variables situadas más a la izquierda son x e y, pero la más grande es x. Ası́ que,
tomamos x como variable portadora y al intentar trasladar toda su columna, (1, 5), a
la posición de su dual nos encontramos con dificultades en cuanto a la identificación de
nuevas fronteras. Dichas dificultades motivan las siguientes definiciones:
Definición 38
Se llama portadora de la EG a la base de variable no vacı́a con frontera izquierda más
pequeña.
Si hay más de una, se toma la que tiene mayor frontera derecha.
Si todavı́a hay más de una candidata, se elige una al azar entre estas.
Se denotará por xc la portadora de la EG. Sus fronteras serán lc = Izq(xc ) y rc = Der(xc ).
Se define la frontera crı́tica de la EG como sigue

 min{Izq(y) : rc ∈ col(y)} si {Izq(y) : rc ∈ col(y)} 6= ∅
cr =
 r
si {Izq(y) : rc ∈ col(y)} = ∅
c
Definición 39
Dada bs una base cualquiera de EG tal que bs no es portadora.
Se dice que bs es superfluo si col(bs) = (i, i) < lc .
Se dice que bs es transporte si lc ≤ Izq(bs) < cr o bien col(bs) = (cr , cr ).
En otro caso, la base bs se dice fija.
Nota 16
Todas las bases variables con Izq(x) < lc son vacias como consecuencia de la definición
de base portadora. Además, cada base, excepto la portadora, es exactamente superflua,
transporte o fija, dependiendo de la región que ocupe por debajo de su frontera derecha
en el diagrama.
b1
lc
superfluo
transporte
(col(b1 ) < lc )
(lc ≤ Izq(bs) < cr )
61
cr
rc
fijo
fijo
bm
Ejemplo 30
1. En este diagrama
x
b
a
y
y
a
b
1
2
3
x
4
5
6
la variable portadora es y.
Para esta variable, lc = 1 y rc = 3. Por lo tanto,
{Izq(y) : rc ∈ col(y)} = {Izq(y) : 3 ∈ col(y)} = ∅
⇒
cr = rc = 3,
es decir, la frontera crı́tica de esta EG es cr = 3.
2. En el siguiente diagrama
x
b
a
y
y
a
b
1
2
x
3
4 5
6
7
8
la variable portadora es x.
Para esta variable, lc = 1 y rc = 5. Por lo tanto,
{Izq(y) : rc ∈ col(y)} = {Izq(y) : 5 ∈ col(y)} = {Izq(x) : 5 ∈ col(x) = (4, 8)} = 4
es decir, la frontera crı́tica de esta EG es cr = 4.
Hemos obtenido un método para determinar las bases que deberı́amos mover, las bases
transporte. Ahora, vamos a estudiar hacı́a donde se deben trasladar estas bases.
Definición 40
Se llama impresión de una EG a una ordenación lineal ≤ en el conjunto
BD ∪ {itr : i ∈ [lc , rc ]}
62
verificando las siguientes propiedades:
1. ≤ extiende el orden de BD y además j tr < k tr para lc ≤ j < k ≤ rc .
2. tr(Ec ) = Ēc , es decir, la estructura de la portadora superpone a su dual.
3. Si x es transporte y x̄ es fijo, entonces se cumple
etr
i = ēi para algún ei ∈ Ex ⇒ tr(Ex ) = Ēx
Es decir, el orden ≤ es consistente con la información de secuencias de fronteras.
4. Si (c, (i, j)) es una base constante, entonces i, j son consecutivos en el orden ≤.
Además, itr y j tr tambien son consecutivos cuando i, j ∈ [lc , rc ].
Es decir, las constantes se conservan.
Nota 17
Para cada frontera i ∈ [lc , rc ] denotamos por itr al nuevo lugar que ocupa la frontera i tras
el transporte. En general,
tr
tr(Ex ) = tr(e1 , · · · , en ) = (etr
1 , · · · , en )
Una vez realizada la clasificación de las bases y una impresión de la EG, es decir, una
guı́a de a donde vá cada frontera transportada, se dejan las bases fijas (intactas) y se
mueven todas las bases transporte. De esta forma estamos reemplazando igualdades por
igualdades de izquierda a derecha.
Por lo tanto, el algoritmo de Makanin se guı́a mediante la variable portadora y su dual.
Ejemplo 31
Consideramos la siguiente EG:
u
x̄c
xc
ū
y
ȳ
1
2
3
4 5
6
7 8
donde BS = {(u, (1, 3)), (ū, (6, 8)), (xc , (1, 5)), (x̄c , (5, 8)), (y, (3, 5, 7)), (ȳ, (2, 4, 5))}
63
xc es la variable portadora tal que lc = 1 y rc = 5.
Calculamos la frontera crı́tica,
cr = min{Izq(y) : rc ∈ col(y)} = 3
Por lo tanto,
lc
cr
rc
u
x̄c
xc
ū
y
ȳ
1
2
3
4 5
6
7 8
Veamos como se transportan las variables transporte (ver [9]):
Como cr < rc , entonces xc y x̄c se reducen de la siguiente forma,
Ec = Ec ∩ {i ∈ BD : cr = 3 ≤ i} = {3, 4, 5}
Ēc = Ēc ∩ {i ∈ BD : ctr
r = 7 ≤ i} = {7, 8}
Hemos obtenido las nuevas bases de la variable portadora y de su dual.
lc
cr
rc
u
x̄c
xc
ū
y
ȳ
1
2
3
4 5 6
7
8
Recordar que si cr = rc entonces Ec = tr(Ec ), es decir, en este caso xc es transportada
completamente.
Estudiamos el comportamiento de las bases transporte bs,
Ec = Ec ∪ {i : i ∈ Ebs y cr = 3 < i} = {3, 4, 5} ∪ {4} = {3, 4, 5}
Ēc = Ēc ∪ {itr : i ∈ Ebs y cr < i} = {7, 8} ∪ {4tr } = {7, 4tr , 8}
Hemos introducido una nueva frontera, 4tr .
Notar que 1tr = 5 y 5tr = 8.
64
lc
cr
rc
u
x̄c
xc
ū
y
ȳ
1
2
3
4 5 6
4tr 8
7
Ya sólo queda por transportar u y ȳ. Como las variable y y su dual tienen un segmento
en común, se pierde parte de la información al transportar ȳ. Teniendo en cuenta que
1tr = 5, 2tr = 6, 3tr = 7
obtenemos la gráfica
lc
cr
rc
x̄c
u
xc
ū
y
ȳ
1
2 3
4 5
6
7
4tr 8
Ahora, podemos eliminar las fronteras inferiores a la frontera crı́tica.
Definición 41 (El Árbol de Makanin)
Dada E una “word equation”, se define recursivamente el árbol de Makanin T (E) asociado
a E como sigue:
1. La raiz de T (E) es E.
2. Los hijos de E son GEN (E).
3. Para cada nodo EG (distinto de la raiz), el conjunto de sus hijos es un número finito
de ecuaciones generalizadas, T RAN S(EG).
65
Teorema 2
Sea E una “word equation”.
E tiene un unificador si y sólo si T (E) tiene un nodo etiquetado con una ecuación generalizada resoluble.
Este teorema proporciona el siguiente procedimiento de semi decisión:
Examinar todos los nodos del árbol T (E) para averiguar si E tiene solución.
Nota 18
En general, el árbol de Makanin asociado a una “word equation” puede ser infinito. Sin
embargo está demostrado que existe un número finito kE que acota el número de nodos
que hay que visitar. Si no se encuentra solución tras la visita de dichos kE nodos, se puede
concluir que no existe solución. kE es de carácter doblemente exponencial en la longitud
de la ecuación E.
4.4.
Casos Particulares de Sistemas de “Word Equations”
Para finalizar este capı́tulo vamos a exponer algunos resultados conocidos para casos
particular de sistemas de “word equations”.
Definición 42
Un sistema de “word equations” se dice cuadrático si cada variable aparece a lo sumo dos
veces en el sistema.
Hablaremos de sistemas cuadráticos para referirnos a los sistemas de “word equations” de
tipo cuadrático.
El algoritmo más sencillo que los resuelve usa un espacio lineal no determinı́stico. En la
primera etapa se comprueba qué variables pueden ser reemplazadas por la palabra vacı́a,
de modo que podemos suponer que las ecuaciones son de la forma
x... = y...
donde y es una variable tal que x 6= y.
Además, si asumimos que x es un prefijo de y podemos reemplazar y (al menos dos veces) por xy, y cancelar x en la parte izquierda de la primera ecuación. Ahora, comprobamos
si y puede ser sustituido por la palabra vacia, Λ.
66
Repetimos el proceso sucesivamente.
Aunque el tamaño del sistema cuadrático nunca decrece, la longitud de una solución
mı́nima decrece en cada etapa. Por lo tanto, el algoritmo no determinı́stico encontrará una
solución, si la hay.
Teorema 3
Sea |A| ≥ 2.
El problema de decidir la existencia de solución de una “word equation” cuadrática es
NP duro.
Demostración 2
Consideramos un problema 3 SAT dado por la función
F = C0 ∧ · · · ∧ Cm−1
donde cada cláusula tiene la forma
Ci = (X̃3i ∨ X̃3i+1 ∨ X̃3i+2 )
para la familia {X̃j : j = 0, . . . , 3m − 1}.
Sea V el conjunto de variables del problema.
Podemos reescribir este problema como un sistema de “word equations” introduciendo
nuevas variables
ci , di
para i = 0, . . . , m − 1
xj
para j = 0, . . . , 3m − 1
yv , zv
para cada variable v ∈ V
Tomamos las constantes a y b del alfabeto A tal que a 6= b.
Para cada cláusula Ci definimos dos ecuaciones:
ci x3i x3i+1 x3i+2 = a3m
ci di = a3m−1
Para cada v ∈ V consideramos el conjunto de posiciones {i1 , . . . , ik } tal que
v = X̃i1 = · · · = X̃ik
67
y el conjunto de posiciones {j1 , . . . , jn } talque
ṽ = X̃j1 = · · · = X̃jn
Suponemos que k ≤ n; el caso n ≤ k es simétrico.
Para cada v definimos dos nuevas ecuaciones:
yv zv = b
xi1 · · · xik yv an bxj1 · · · xjn zv = an ban b
Por lo tanto, la fórmula F es satisfactible si y sólo si el sistema cuadrático
ci x3i x3i+1 x3i+2 = a3m
ci di = a3m−1
yv zv = b
xi1 · · · xik yv an bxj1 · · · xjn zv = an ban b
tiene solución.
Pero, cualquier sistema de k “word equations”, con k ≥ 1
L1 = R1 , . . . , Lk = Rk
donde R1 , . . . , Rk ∈ {a, b}∗ , es equivalente a una ecuación de la forma
L1 c · · · Lk−1 cLk = R1 c · · · Rk−1 cRk
siendo c una constante.
Luego, podemos reescribir el sistema cuadrático como una “word equation” sin más que
aumentar el tamaño del alfabeto en una constante. Incluso, podemos eliminar esta nueva
constante c, sin incrementar el número de veces que aparece cada variable del sistema,
codificando las tres letras como aba, abba y abbba y reemplazando cada variable xi del
problema por axi a.
Por lo tanto, hemos reducido un problema 3 SAT , cualquiera, a una “word equation”
cuadrática y sabemos que 3 SAT es NP completo, es decir, hemos probado que el problema
de existencia de solución de una “word equation” cuadrática sobre un alfabeto binario es
NP duro.
68
Destacar que es posible reducir un problema de tipo 3 SAT a un sistema de “word
equations”, de forma análoga al esquema utilizado en la anterior demostración.
Proposición 2 Todo problema 3 SAT se reduce a un sistema de “word equations” sobre
el alfabeto unario A = {1}.
Demostración 3
Consideramos un problema 3 SAT dado por la función
F = C0 ∧ · · · ∧ Cm−1
donde cada cláusula tiene la forma
Ci = X̃3i ∨ X̃3i+1 ∨ X̃3i+2
para la familia de literales {X̃j : j = 0, . . . , 3m − 1}.
Sea V el conjunto de variables del problema.
Podemos reescribir este problema como un sistema de “word equations” sobre el alfabeto
unario A = {1} introduciendo nuevas variables:
ci , di
yv , zv
para i = 0, . . . , m − 1
para cada variable v ∈ V
Para cada v ∈ V definimos la ecuación:
yv zv = 1
Para cada cláusula Ci = X̃3i ∨ X̃3i+1 ∨ X̃3i+2 definimos dos ecuaciones:
ci v3i v3i+1 v3i+2 = 111
ci di = 11
donde

 yv
vj =
 z
si X̃j = v
si X̃j = ṽ
v
En estas condiciones se puede ver que la fórmula F es satisfactible si y sólo si el sistema
formado por las ecuaciones
ci v3i v3i+1 v3i+2 = 111
69
ci di = 11
yv zv = 1
para i = 0, . . . , m − 1 y v ∈ V tiene solución.
Por último, consideramos un problema particular de sistema de “word equations” que
se obtiene al imponer restricciones en la longitud de las soluciones.
Definición 43
Dado un sistema S de n “word equations” sobre un alfabeto A y con variables en el
conjunto Ω = {x1 , . . . , xm }.
Se llama l sistema de “word equations” al problema de determinar si existe una solución
de S
σ : (A ∪ Ω)∗ −→ A∗
tal que |σ(xi )| ≤ l, para cada i = 1, . . . , m.
Esta problema se denotará por l SW ES.
Proposición 3 El problema l SW ES es NP completo para l ≥ 2.
Demostración 4
Es fácil ver que l SW ES está en NP.
Tomando como cota l = 2, se obtiene el resultado de forma análoga a como se probó la
proposición 2, pero en este caso se pueden suprimir del sistema equivalente a F las ecuaciones de la forma ci di = 11 para cada i = 0, . . . , m − 1.
En consecuencia 3 SAT se reduce ha 2 SW ES.
Nota 19
Nuestro trabajo se centrará en la búsqueda de solución de los problemas l SW ES sobre
el alfabeto binario A = {0, 1}.
70
Capı́tulo 5
Un Algoritmo Genético Simple
para Resolver l SW ES
5.1.
Introducción
Una vez estudiado el problema de encontrar solución de un sistema de “word equations”
y tras observar la enorme complejidad (triplemente exponencial) del algoritmo de Makanin,
intentaremos construir un algoritmo genético que proporcione una solución si existe.
La motivación de este trabajo son los buenos resultados que se han obtenido en la
resolución de los problemas SAT mediante el uso de los algoritmos evolutivos y el hecho
de que los problemas SAT se reducen a sistemas de “word equations”, como hemos visto.
Empezamos nuestro trabajo diseñando un algoritmo genético simple con la esperanza
de obtener resultados más o menos satisfactorios.
5.2.
Codificación de los Individuos
Sea A = {0, 1} el alfabeto binario y Ω = {x1 , . . . , xm } el conjunto de variables del
problema l SW ES formado por n “word equations”
S = { L1 = R1 , . . . , Ln = Rn }
siendo l una cota para la longitud de las variables.
Recordar que un homomorfismo σ es una solución candidata de S si |σ(xi )| ≤ l para
cada i = 1, . . . , m. Por lo tanto, tenemos que codificar m variables como cadenas de
71
longuitud menor o igual que l sobre el alfabeto A.
Definimos un nuevo sı́mbolo B, que llamaremos sı́mbolo blanco, de modo que, todas las
variables del problema l SW ES se pueden codificar como cadenas de longitud exactamente
igual a l sobre el nuevo alfabeto B = {0, 1, B} de la siguiente forma:
para cada i = 1, . . . , m, sea αi la codificación de la variable xi sobre B, esta codificación
es una cadena formada por sı́mbolos de B que se lee de izquierda a derecha y cada vez que
se encuentra el sı́mbolo blanco B se pasa al siguiente sı́mbolo sin hacer nada.
Ejemplo 32
Las cadenas sobre B = {0, 1, B} dadas por
α1 = 01BB0B011BBB
α2 = BB01BB0BB0BB
α3 = 111B11BB0
se leen como
β1 = 010011
β2 = 0100
β3 = 111110
Además, cadenas distintas sobre el alfabeto B pueden representar la misma cadena
sobre el alfabeto A.
Ejemplo 33
Las siguientes cadenas sobre B
α1 = 01B011BBB
α2 = B01BB0B1BB1
α3 = 0101B1BB
α4 = BB0B1B0B1B1B
representan la misma cadena sobre A dada por
01011
Por lo tanto, una misma variable candidata a formar parte de la solución del problema
puede tener distintas codificaciones. Ası́ que, con el fin de unificar la codificación de las
variables y para poder simplificar los procesos de cruce y mutación que estudiaremos más
72
tarde, reordenaremos de la siguiente forma las codificaciones:
recorriendo de izquierda a derecha la codificación desplazamos los sı́mbolos B al final de
la cadena.
Con esta reordenación conseguimos construir un representante de todas las posibles
codificaciones de una misma variable.
Ejemplo 34
Sea la codificación dada por
0B10B00B1BB1B001
tras la reordenación obtenemos la cadena
0100011001BBBBBB
que representa todas las posibles codificaciones de la variable 0100011001.
Nota 20
La cadena formada sólo por sı́mbolos blancos representa la cadena vacia, Λ.
Una vez codificadas las variables del problema, definimos un individuo como la concatenación de las cadenas αi para i = 1, . . . , m que se obtienen al codificar las m variables
x1 , . . . , xm .
Ejemplo 35
Supongamos que tenemos que codificar 4 variables de longitud menor o igual a 6 sobre el
alfabeto binario A. Sean
α1 = B01B10
α2 = BB1BB0
α3 = 11BB1B
Lo primero que tenemos que hacer es reordenar los sı́mbolos, obteniendo
α1 = 0110BB
α2 = 10BBBB
α3 = 111BBB
Un individuo o solución candidata del problema viene dada por la cadena
I = α1 α2 α3 = 0110BB
| {z } 10BBBB
| {z } 111BBB
| {z }
α1
α2
73
α3
Nota 21
No tiene sentido reordenar las cadenas que representan individuos de la población, pues
perderı́amos la información de las variables que componen esas posibles soluciones del
problema.
Para finalizar esta sección, queremos destacar que con esta codificación de los individuos conseguimos que todos tengan la misma longitud, m · l. Además, todas las variables
son codificadas como cadenas de longitud igual a l, aunque la longitud real de la variable
que se representa es menor o igual que l, pues el sı́mbolo B no aporta nada al valor de las
variables. Por lo tanto, buscamos soluciones en un espacio de búsqueda de tamaño
à l
!m
X
2i
= (2l+1 − 1)m
i=0
5.3.
Generación de la Población Inicial
Trabajaremos con poblaciones de tamaño fijo, tp. Aunque el algoritmo que diseñaremos
permitirá al usuario elegir el tamaño de la población con el que quiere resolver el problema,
uno de nuestro primeros objetivos será determinar el tamaño de la población más adecuado
para la resolución de los problemas l SW ES.
Una vez fijado el tamaño tp, la población inicial será generada de forma aleatoria.
Para la implementación de este proceso proponemos dos métodos.
Método 1.
Cada individuo de la población inicial se genera como sigue:
para cada i = 1, . . . , m eligimos de forma aleatoria un número entre 0 y l, que denotaremos por lr (i). Después, construimos aleatoriamente una cadena βi de longitud
lr (i) sobre el alfabeto A y una cadena β̄i formada por el sı́mbolo B de longitud igual
a l − lr (i). La cadena αi se obtiene como la concatenación de βi y β̄i .
αi = βi β̄i
Finalmente, definimos el individuo
I = α1 · · · αm
Ejemplo 36
Generamos una población de tamaño tp = 3 para resolver un problema 4 SW ES
con dos variables. (l = 4)
74
Empezamos definiendo el primer individuo de la población inicial, I1 .
Para i = 1 elegimos al azar la longitud de la subcadena β1 , sea lr (1) = 2 ∈ {0, 1, . . . , 4}.
Construimos aleatorimente β1 = 01. Definimos β̄1 = BB.
Se obtiene la cadena α1 = β1 β̄1 = 01BB.
Para i = 2 elegimos al azar la longitud de la subcadena β2 , sea lr (2) = 4 ∈ {0, 1, . . . , 4}.
Construimos aleatoriamente β2 = 0110. Definimos β̄2 = Λ.
Se obtiene la cadena α2 = β2 β̄2 = 0110Λ = 0110.
Por lo tanto, se tiene que
I1 = α1 α2 = 01BB0110
Definimos ahora el segundo individuo de la población inicial, I2 .
Para i = 1 elegimos al azar la longitud de la subcadena β1 , sea lr (1) = 1 ∈ {0, 1, . . . , 4}.
Construimos aleatoriamente β1 = 1. Definimos β̄1 = BBB.
Se obtiene la cadena α1 = β1 β̄1 = 1BBB.
Para i = 2 elegimos al azar la longitud de la subcadena β2 , sea lr (2) = 0 ∈ {0, 1, . . . , 4}.
Construimos aleatoriamente β2 = Λ. Definimos β̄2 = BBBB.
Se obtiene la cadena α2 = β2 β̄2 = ΛBBBB = BBBB.
Por lo tanto, se tiene que
I2 = α1 α2 = 1BBBBBBB
Finalmente, generamos el último individuo, I3 .
Para i = 1 elegimos al azar la longitud de la subcadena β1 , sea lr (1) = 3 ∈ {0, 1, . . . , 4}.
Construimos aleatoriamente β1 = 101. Definimos β̄1 = B.
Se obtiene la cadena α1 = β1 β̄1 = 101B.
Para i = 2 elegimos al azar la longitud de la subcadena β2 , sea lr (2) = 3 ∈ {0, 1, . . . , 4}.
Construimos aleatoriamente β2 = 111. Definimos β̄2 = B.
Se obtiene la cadena α2 = β2 β̄2 = 111B.
Por lo tanto, se tiene que
I3 = α1 α2 = 101B111B
De esta forma, hemos generado al azar una población inicial de tamaño tp = 3.
Método 2.
Cada individuo de la población inicial se genera como sigue:
para cada i = 1, . . . , m construimos al azar una cadena αi de longitud l sobre el
75
alfabeto B y luego reordenamos dicha cadena para que todos los sı́mbolos blancos
estén situados al final de la misma. Finalmente, definimos el individuo
I = α1 · · · αm
Ejemplo 37
Generamos una población de tamaño tp = 3 para resolver un problema 4 SW ES
con dos variables. (l = 4)
Empezamos definiendo el primer individuo de la población inicial, I1 .
Para i = 1 construimos aleatorimente la cadena α1 = B1B0, después de la reordenación se tiene que α1 = 10BB.
Para i = 2 construimos aleatoriamente la cadena α2 = BB1B, después de la reordenación se tiene que α2 = 1BBB.
Por lo tanto,
I1 = α1 α2 = 10BB1BBB
Definimos ahora el segundo individuo de la población inicial, I2 .
Para i = 1 construimos aleatorimente la cadena α1 = 1BB1, después de la reordenación se tiene que α1 = 11BB.
Para i = 2 construimos aleatoriamente la cadena α2 = 0B01, después de la reordenación se tiene que α2 = 001B.
Por lo tanto,
I1 = α1 α2 = 11BB001B
Finalmente, generamos el último individuo, I3 .
Para i = 1 construimos aleatorimente la cadena α1 = 0000, después de la reordenación se tiene que α1 = 0000.
Para i = 2 construimos aleatoriamente la cadena α2 = BBBB, después de la reordenación se tiene que α2 = BBBB.
Por lo tanto,
I1 = α1 α2 = 0000BBBB
De esta forma, hemos generado al azar una población inicial de tamaño tp = 3.
76
5.4.
Evaluación de los Individuos
Este es un paso muy importante en el diseño del algoritmo genético, pues tenemos
que encontrar una función fitness sencilla, es decir, con poco coste a la hora de evaluar
los individuos, y que al mismo tiempo nos permita guiar el proceso de búsqueda de las
soluciones de la forma más eficiente posible.
Un individuo I = α1 · · · αm es solución del problema l SW ES si al sustituir las subcadenas αi por las variables xi , para i = 1, . . . , m, en el sistema de “word equations” se
obtienen identidades.
Ejemplo 38
Consideramos el problema 3 SW ES dado por el sistema
01x1 1 = x2 1x1
x1 01x2 = 10x2 1
Generamos una población de tamaño tp = 2 dada por
I1 = 11B01B
I2 = 1BB01B
Veamos si alguno de los individuos de esta población es solución del problema. Empezamos
sustituyendo el individuo I1 .
Como,
I1 = 11B01B
resulta que
α1 = 11B
-
x1 = 11
α2 = 01B
-
x2 = 01
-
01x1 1 = x2 1x1
-
01111 = 01111
x1 01x2 = 10x2 1
-
110101 6= 10011
Es decir, I1 no es solución.
Ahora, comprobamos si I2 es solución.
Como,
I2 = 1BB01B
-
α1 = 1BB
-
x1 = 1
α2 = 01B
-
x2 = 01
77
resulta que
01x1 1 = x2 1x1
-
0111 = 0111
x1 01x2 = 10x2 1
-
10101 6= 10011
I2 tampoco es solución.
Observamos que aunque ninguno de los dos individuos de la población son solución, ambos
hacen que se verifique la primera de las ecuaciones del sistema. Sin embargo, parece estar
más proximo a la solución el individuo I2 pues sólo dos sı́mbolos de la segunda ecuación
del sistema no coinciden.
Este ejemplo, nos sugiere definir el fitness asociado a un individuo como el número
de sı́mbolos que no coinciden al sustituir dicho individuo en el sistema. De esta forma,
nuestro objetivo será minimizar el valor de la función fitness, es decir, conseguir que nigún
sı́mbolo sea distinto del correspondiente situado en el otro lado de la ecuación.
Definición 44
Sea S = {L1 = R1 , . . . , Ln = Rn } un l SW ES.
Se define el fitness asociado a un individuo I como el valor dado por
n
X
(max{lk , rk } − ck )
f (I) =
k=1
siendo lk = |Lk (I)|, rk = |Rk (I)| y ck el número de sı́mbolos que coinciden al sustituir el
individuo I en la k ecuación del sistema, para cada k = 1, . . . , n.
Nota 22
Al sustituir en cada ecuación Lk = Rk el individuo I, obtenemos dos cadenas Lk (I) y
Rk (I) sobre el alfabeto A cuyas longitudes hemos denotado por lk y rk , respectivamente.
El individuo I será solución del sistema si Lk (I) = Rk (I) para cada k = 1, . . . , n.
Ejemplo 39
Volviendo al sistema del anterior ejemplo
01x1 1 = x2 1x1
x1 01x2 = 10x2 1
Tenı́amos una población dada por
I1 = 11B01B
I2 = 1BB01B
78
Veamos quien es el fitness asociado a cada uno de estos individuos:
Al sustituir I1 en el sistema se obtiene
L1 (I1 ) = 01111
con
l1 = 5
R1 (I1 ) = 01111
con
r1 = 5
L2 (I1 ) = 110101
con
l2 = 6
R2 (I1 ) = 10011
con
r2 = 5
Como L1 (I1 ) = R1 (I1 ), todos los sı́mbolos del lado izquierdo de la ecuación coinciden con
los del lado derecho, por lo tanto, c1 = 5. Luego, f1 (I1 ) = max{l1 , r1 } − c1 = 5 − 5 = 0.
En la segunda ecuación resulta que L2 (I1 ) 6= R2 (I1 ). Contamos los sı́mbolos que ocupando
la misma posición en ambas cadenas coinciden.
L2 (I1 ) = 1 1 01 01
R2 (I1 ) = 1 0 01 1
Se obtiene que c2 = 3. Luego, f2 (I1 ) = max{l2 , r2 } − c2 = max{6, 5} − 3 = 6 − 3 = 3.
Por lo tanto,
f (I1 ) =
2
X
k=1
fk (I1 ) =
2
X
(max{lk , rk } − ck ) = 0 + 3 = 3
k=1
Al sustituir I2 en el sistema se obtiene
L1 (I2 ) = 0111
con
l1 = 4
R1 (I2 ) = 0111
con
r1 = 4
L2 (I2 ) = 10101
con
l2 = 5
R2 (I2 ) = 10011
con
r2 = 5
Como L1 (I2 ) = R1 (I2 ) todos los sı́mbolos del lado izquierdo de la ecuación coinciden con
los del lado derecho, por lo tanto, c1 = 4. Luego, f1 (I2 ) = max{l1 , r1 } − c4 = 4 − 4 = 0.
En la segunda ecuación resulta que L2 (I2 ) 6= R2 (I1 ). Contamos los sı́mbolos que ocupando
la misma posición en ambas cadenas coinciden.
L2 (I2 ) = 10 10 1
R2 (I2 ) = 10 01 1
79
Se obtiene que c2 = 3. Luego, f2 (I2 ) = max{l2 , r2 } − c2 = max{5, 5} − 3 = 5 − 3 = 2.
Por lo tanto,
f (I2 ) =
2
X
fk (I2 ) =
k=1
2
X
(max{lk , rk } − ck ) = 0 + 2 = 2
k=1
Como el fitness asociado a I2 es menor que el asociado a I1 podemos concluir que es mejor
candidata a solución la cadena I2 .
Nota 23
Cuando sustituimos un individuo en una ecuación obtenemos dos cadenas sobre el alfabeto
A, las correspondientes al lado derecho e izquierdo de la ecuación, que no tienen por que
tener la misma longitud. Una condición necesaria para que ambas cadenas coincidan es
que tengan la misma longitud, por eso a la hora de calcular el fitness no sólo contamos
los sı́mbolos que ocupando las mismas posiciones en ambas cadenas no coinciden sino que
también contamos los sı́mbolos que hacen que una cadena tenga mayor longitud, es decir,
los sı́mbolos situados entre la posición min{lk , rk } + 1 y max{lk , rk }.
5.5.
Los Operadores Genéticos
Selección
Usaremos un criterio de selección elitista que mantiene en la población al mejor
individuo alcanzado hasta ese momento. Además, la selección de los individuos que
darán lugar a una nueva población se hará de acuerdo al fitness asociado mediante
el método de la ruleta. En este caso, pretendemos minizar la función fitness, por lo
que los individuos con un fitness más pequeño son los que mayor probabilidad tiene
de contribuir en la siguinte población.
La probabilidad de contribuir en la siguiente población viene dada por
tp
X
P(i) =
f (j) − f (i)
j=1
tp
tp
XX
(
f (j) − f (l))
l=1 j=1
para cada individuo i = 1, . . . , tp.
Ejemplo 40
Supongamos que tenemos una población de tamaño tp = 4 y hemos evaluado los
individuos obteniendo la siguiente tabla
80
Individuo
fitness
I1
2
I2
12
I3
5
I4
7
Es evidente que el individuo con mayor probabilidad de contribuir en la siguiente
generación es I1 . Luego, la probabilidad de contribuir en la siguiente población es
inversamente proporcional al fitness.
Construimos una ruleta de la siguiente forma:
sea sum la suma de los fitness de la población,
sum =
4
X
f (Ii )
i=1
definimos para cada individuo un nuevo fitness dado por
fe(Ii ) = sum − f (Ii )
Ahora, la probabilidad de que un individuo contribuya en la siguiente población es
directamente proporcional al nuevo fitness. En este ejemplo,
sum = 2 + 12 + 5 + 7 = 26
Individuo
fitness
f^
itness
P
fe(Ii )/ 4j=1 fe(Ij )
I1
2
24
24/78 = 0,31
I2
12
14
14/78 = 0,18
I3
5
21
21/78 = 0,27
I4
7
19
19/78 = 0,24
Cruce
Para cada par de individuos
1
I1 = α11 · · · αm
2
I2 = α12 · · · αm
se define el operador de cruce como un proceso que cruza cada subcadena αi1 = βi1 β̄i1
con la subcadena αi2 = βi2 β̄i2 , para i = 1, . . . , m, en dos etapas, obteniendose un
81
nuevo individuo.
Recordar que cada subcadena αi sobre el alfabeto B = {0, 1, B} está formada por
dos subcadenas βi y β̄i siendo βi una cadena de longitud menor o igual que l sobre
el alfabeto A = {0, 1} y β̄i una cadena de sı́mbolos blancos.
En la primera etapa, se aplica un cruce uniforme como sigue:
1. Se calcula la posición k = min{|βi1 |, |βi2 |}
2. Para cada j = 1, . . . , k elijo con probabilidad p el sı́mbolo que ocupa la posición
j ésima de αi1 y con probabilidad 1 − p tomamos el de la subcadena αi2 , este
sı́mbolo pasa a ocupar la posición j ésima de la subcadena αi en el nuevo
individuo, es decir, estamos aplicando un cruce uniforme a las cadenas αi1 (1, k)
y αi2 (1, k).
En nuestro algoritmo tomaremos p = 0,5.
Ejemplo 41
Vamos a aplicar la primera etapa del cruce a las subcadenas
BB
αi1 = 011101
| {z } |{z}
αi2
βi1
β̄i1
= 1101
| {z }
|{z } BBBB
βi2
β̄i2
Calculamos la posición
k = min{|βi1 |, |βi2 |} = min{6, 4} = 4
Por lo tanto, aplicamos un cruce uniforme a las cadena
αi1 (1, 4) = 0111
αi2 (1, 4) = 1101
Obteniendo, por ejemplo, la cadena
αi (1, 4) = 0101
En la segunda etapa, aplicamos un cruce en un punto como sigue:
1. Elegimos una posición q ∈ {k, . . . , l}, siendo l la longitud de las cadenas αi1 y
αi2 , para i = 1, . . . , m.
82
2. Si una de las dos cadenas αi1 (k + 1, l) ó αi2 (k + 1, l) contiene algun sı́mbolo
distinto de B, definimos αi (k + 1, q) con las q − k primeras posiciones de esa
cadena y completamos la subcadena αi con l − q sı́mbolos blancos, es decir,
αi (q + 1, l) = B l−q .
En otro caso, se define αi (k + 1, l) = B l−k
Nota 24
Si q = k entonces αi = (k + 1, l) = B l−k
Ejemplo 42
En el ejemplo anterior aplicamos la primera etapa del cruce a las subcadenas
αi1 = 011101BB
αi2 = 1101BBBB
Obtuvimos αi (1, 4) = 0101. Ahora aplicamos la segunda etapa del cruce para definir
αi (5, 8). Para ello, tenemos que eligir una posición entre 4 y 8, sea q = 7.
Como αi1 (5, 8) = 01BB tiene sı́mbolos distintos del blanco, definimos αi (k+1, q) = αi (5, 7)
con los q − k = 3 primeros sı́mbolos de αi1 (5, 8) = 01BB, es decir, αi (5, 7) = 01B.
Finalmente, completamos la cadena αi con l − q = 1 sı́mbolos blancos.
Hemos construido la subcadena
αi = 010101BB
Repitiendo este proceso para cada par de subcadenas αi1 y αi2 conseguimos definir
un nuevo individuo a partir de los individuos I1 e I2 .
Mutación
Cada individuo de la población será mutado con probabilidad p, fijada. La probabilidad de mutación será otro de los datos que el usuario fija para resolver los
problemas. En nuestro caso, buscaremos la probabilidad de mutación más adecuada
para resolver los problemas l SW ES.
Si un individuo I = α1 · · · αm es seleccionado para la mutación, entonces se aplica un
proceso de mutación a cada subcadena αi = βi β̄i de la siguiente forma: cada sı́mbolo
de la subcadena αi cambia con probabilidad 1l , siendo l la longitud de la subcadena
αi .
83
Recordar que la subcadena αi toma valores en el alfabeto B. Ası́ pues, tras el proceso
de mutación es necesario reordenar de nuevo para situar los sı́mbolos blancos al final.
Hemos definido todas los elementos necesarios para el diseño del algoritmo genético
simple. En el apendice A se encuentra el código de este algoritmo escrito en el lenguaje
C++.
Veamos con un sencillo ejemplo su funcionamiento.
Ejemplo 43
Consideramos el problema 3 SW ES dado por las ecuaciones
x2 x1 = 10x2
x2 10x1 = 1x1 0x1
Codificación de los individuos
Cada individuo se obtiene como la concatenación de dos cadenas de longitud l = 3
sobre el alfabeto B.
Población inicial
Trabajaremos con poblaciones de tamaño tp = 4.
La población inicial será generada usando el método 2.
Usaremos el fitness y los operadores genéticos que acabamos de definir.
Empezamos a resolver el problema ejecutando el algoritmo genético simple ası́ diseñado.
Generamos la población inicial:
α11 = 0B1
-
α11 = 01B
-
α21
-
α12 = 011
-
α22
α13 = 101
-
α13 = 101
α23 = BB0
-
α23 = 0BB
α21
= B11
α12 = 011
α22
= B1B
= 11B
= 1BB
84
-
I10 = 01B11B
-
I20 = 0111BB
-
I30 = 1010BB
α14 = BB1
-
α14 = 1BB
α24 = 1B0
-
α24 = 10B
I40 = 1BB10B
-
Obtenemos la pobalción inicial P0 dada por los individuos:
I10 = 01B11B
I20 = 0111BB
I30 = 1010BB
I40 = 1BB10B
Evaluamos la población inicial:
x2 10x1 = 1x1 0x1
I10
111001 6= 101001
I10
1101 6= 1011
I20
110011 6= 10110011
I20
1011 6= 101
I30
010101 6= 11010101
I30
0101 6= 100
I40
10101 6= 1101
I40
101 6= 1010
-
x2 x1 = 10x2
-
x2 10x1 = 1x1 0x1
-
x2 x1 = 10x2
-
x2 10x1 = 1x1 0x1
-
x2 x1 = 10x2
-
x2 10x1 = 1x1 0x1
-
x2 x1 = 10x2
-
-
f1 = 3
-
f2 = 8
-
f3 = 7
- f4 = 5
Se tiene la siguiente tabla asociada a la población inicial P0
Individuo
P0
fitness
f^
itness
P (Ii )
I10
01B11B
3
20
0.29
I20
0111BB
8
15
0.22
I30
1010BB
7
16
0.23
I40
1BB10B
5
18
0.26
El mejor individuo de P0 , I10 , pasa directamente a la siguiente población, P1 .
Individuo
P1
fitness
I11
01B11B
3
···
···
···
85
Tenemos que generar otros tres individuos mediante los operadores genéticos para completar la población P1 .
Generación de I21 :
Selección
I10 = 01B11B
−→
α11 = 01B
α21 = 11B
I30 = 1010BB
−→
α13 = 101
α23 = 0BB
Cruce
Empezamos aplicando el cruce a las cadenas α11 = 01B y α13 = 101.
De aplicar el cruce uniforme a las subcadenas 01 y 10 resulta la subcadena 00.
De aplicar el cruce en un punto a las subcadenas B y 1 resulta la subcadena B.
Por lo tanto, hemos obtenido
α13 = 101
-
α11 = 01B
α1 = 00B
Repetimos el proceso para las cadenas α21 = 11B y α23 = 0BB.
De aplicar el cruce uniforme a las subcadenas 1 y 0 resulta la subcadena 0.
De aplicar el cruce en un punto a las subcadenas 1B y BB resulta la subcadena 1B.
Por lo tanto, hemos obtenido
α23 = 0BB
-
α21 = 11B
α2 = 01B
Luego, I21 = 00B01B.
Mutación
Este individuo es sometido al proceso de mutación, resultando
00B −→ B0B −→ 0BB
01B −→ 01B −→ 01B
-
I21 = 0BB01B
Generación de I31 :
Selección
I20 = 0111BB
−→
α12 = 011
α22 = 1BB
I10 = 10B11B
−→
α11 = 01B
α21 = 11B
86
Cruce
Empezamos aplicando el cruce a las cadenas α12 = 011 y α11 = 01B.
De aplicar el cruce uniforme a las subcadenas 01 y 01 resulta la subcadena 01.
De aplicar el cruce en un punto a las subcadenas 1 y B resulta la subcadena 1.
Por lo tanto, hemos obtenido
α11 = 01B
α12 = 011
-
α1 = 011
Repetimos el proceso para las cadenas α22 = 1BB y α21 = 11B.
De aplicar el cruce uniforme a las subcadenas 1 y 1 resulta la subcadena 1.
De aplicar el cruce en un punto a las subcadenas BB y 1B resulta la subcadena BB.
Por lo tanto, hemos obtenido
α21 = 11B
α22 = 1BB
-
α2 = 1BB
Luego, I21 = 0111BB.
Mutación
Este individuo es sometido al proceso de mutación, resultando
011 −→ B01 −→ 01B
0BB −→ 0B1 −→ 01B
-
I21 = 01B01B
Repitiendo este proceso generamos los otros individuos de la población. Tras la evaluación
de los nuevos individuos se obtine la siguiente tabla para la población P1 ,
Individuo
P0
fitness
I11
01B11B
3
I21
01B01B
4
I31
1BB11B
5
I41
01B10B
0
Como uno de los individuos de la nueva población tiene fitness 0, el algoritmo ha finalizado.
La solución propuesta es
x1 = 01
x2 = 10
87
Capı́tulo 6
El Algoritmo Genero Problema
Estamos en condiciones de empezar a experimentar con el algoritmo genético simple,
pero nos encontramos con un inconveniente: no existen documentos donde aparezcan problemas del tipo de los que pretendemos resolver, ası́ que nos vemos obligados a construir
los problemas l SW ES.
Para facilitar este trabajo hemos decidido diseñar un algoritmo que construye un sistema de “word equations” sobre el alfabeto A = {0, 1} con un número de ecuaciones y de
variables fijado. Este algoritmo también proporcionará una solución del problema, pero
no tiene por que ser la única.
La primera idea para el diseño de este algoritmo consistı́a en realizar los siguientes
pasos:
1. Generar un sistema de identidades sobre el alfabeto A.
2. Generar las variables como cadenas de longitud menor o igual que lmax sobre el
alfabeto A.
3. Para cada variable generada, recorrer el sistema buscando una subcadena que coincida con la variable. Si la encuentro, sustituyo con probabilidad p la subcadena por la
letra que representa a la variable y lo sigo recorriendo en busca de otra coincidencia.
Sin embargo, esta idea tan sencilla no nos garantiza que, al final, el sistema generado
contenga todas las variables pues, por ejemplo, una variable puede ser generada como una
cadena sobre A que no es subcadena en ninguna de las ecuaciones del sistema.
Logicamente, fijado un número de ecuaciones, n, y un número de variables, m, nuestro
interés es construir un sistema de n “word equations” sobre el alfabeto A que contenga
88
exactamente m variables, x1 , . . . , xm . Por lo tanto, nos vemos obligados a modificar esta
primera idea y construimos un algoritmo con los siguientes pasos:
1. Generar un sistema de identidades sobre el alfabeto A = {0, 1}.
2. Para cada variable xi ,
a)
elegimos una ecuación del sistema,
b)
elegimos el lado derecho o izquierdo de la ecuación,
c)
definimos la variable xi como una subcadena de la parte elegida (derecha o
izquierda) de la ecuación, esta subcadena será de longitud menor o igual que
lmax sobre A,
d)
sustituimos la subcadena de la ecuación por la letra que representa a la variable
generada. De esta forma se consigue que cada variable generada aparezca al
menos una vez en el sistema.
3. Una vez que se han generado todas las variables, recorremos el sistema en busca de
coincidencias para cada variable xi , i = 1, . . . , m. Si se encuentra una subcadena en el
sistema que coincida con la variable xi , se cambia con probabilidad p la subcadena
por la letra que representa a la variable. Con este paso pretendemos aumentar el
número de veces que aparece una variable en el sistema.
Nota 25
A medida que se van generando las variables, el sistema de identidades sobre el alfabeto
A se convierte en un sistema de ecuaciones sobre el conjunto A ∪ {x1 , . . . , xm }.
El paso crı́tico de este algoritmo se encuentra al definir las variables del sistema. Intentaremos explicar un poco más este paso. El algoritmo que diseñamos necesita, además
del número de ecuaciones y el número de variables, dos cotas le y lmax . La primera de ellas,
le , indica la longitud máxima de las cadenas que generamos sobre A para construir las
identidades (paso 1). La segunda cota, indica la longitud máxima de las variables. Por lo
tanto, una vez que se ha elegido el lado de la ecuación donde se vá a definir la variable xi ,
se selecciona al azar una posición q de la misma y se cuentan los sı́mbolos (consecutivos)
del alfabeto A que hay a la derecha de q. Si denotamos por lq al número de sı́mbolos del
alfabeto A que hay a la derecha de q, entonces la variable xi se define como un subcadena
89
de longitud lr ∈ {0, min{lmax , lq }} que coincide con el trozo de subcadena de la ecuación
que vá de q a q + lr .
Notar que si lr = 0, entonces la variable xi es la palabra vacı́a.
Nota 26
En general, el problema(n m lmax ) denotará un sistema de n “word equations” con m
variables, de modo que cada variable (al menos en una solución del sistema) tiene longitud
menor o igual que lmax .
En la última versión de este algoritmo, hemos añadido un proceso para controlar
el tipo de ecuaciones que constituyen el sistema. Con este proceso se pretenden evitar
las ecuaciones donde el lado derecho coincide con el izquierdo. Por ejemplo, la ecuación
0x1 10 = 0x1 10 puede ser eliminada del sistema por tratarse de una igualdad. De modo
que una vez generado el sistema lo sometemos a los siguientes pasos:
1. Recorremos el sistema en busca de ecuaciones con la misma cadena en ambos lados.
2. Para ecuación de este tipo,
a)
Recorremos la ecuación para cada variable xi buscando una coincidencia. Si
hay coincidencia, se sustituye con probabilidad p la subcadena de la ecuación
por la letra que representa dicha variable.
b) Si depués de recorrer la ecuación para cada variable ambos lados siguen siendo
iguales, sustituimos dicha ecuación por una nueva identidad sobre el alfabeto
A y volvemos al paso a).
De esta forma conseguimos que el sistema contenga exactamente n ecuaciones (con
ambos lados distintos).
En el apéndice C se encuentran algunos de los problemas que hemos generado con este
algoritmo. Además, en el apéndice B se encuentra el código del algoritmo Genero Problema
escrito en el lenguaje C++.
90
Capı́tulo 7
Primeros Resultados
Experimentales
7.1.
Introducción
Cada experimento consistirá en la resolución, cincuenta veces, de un problema dado y
con unos parámetros fijos. Estos parámetros serán:
1. El tamaño de la población. (tp)
2. La longitud máxima de las variables (l). Ası́, cada individuo estará constituido por
subcadenas de longitud l sobre el alfabeto B.
3. El número máximo de evaluaciones a realizar en una ejecución. (NMax. Eval.)
4. La probabilidad de mutación. (Mutación)
5. La selección del método para la generación de la población inicial. (Método)
Nota 27
Con el fin de comparar los resultados obtenidos hemos acotado el número de evaluaciones
que puede realizar el algoritmo para resolver cada problema.
Llamaremos Media Eval. al número medio de evaluaciones que fueron necesarias para
encontrar solución en las pruebas exitosas de las cincuenta realizadas.
Llamaremos Éxito al número de veces que se encuentra solución para un problema. Se
expresará en tanto por cien.
91
7.2.
Tamaño de Población
Nuestro primer objetivo es determinar el tamaño de población más adecuado para la
resolución de los diferentes problemas.
No sabemos nada acerca de cuál es la mejor probabilidad de mutación, ası́ que hemos
decidido tomar 0,6. Acotamos el proceso con 150000 evaluaciones y tomamos l = 5.
Aprovecharemos también este experimento para establecer, si es posible, una diferencia
entre el método 1 y el método 2 usados para generar la población inicial.
Resolvemos el Problema15-12-4
Tamaño del espacio de búsqueda: aprox. 260
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
22 %
106887
2
2
5
60 %
150000
34 %
107556
3
1
5
60 %
150000
12 %
108542
3
2
5
60 %
150000
6%
109582
4
1
5
60 %
150000
2%
88213
4
2
5
60 %
150000
0%
0
6
1
5
60 %
150000
0%
0
6
2
5
60 %
150000
0%
0
8
1
5
60 %
150000
0%
0
8
2
5
60 %
150000
0%
0
10
1
5
60 %
150000
0%
0
10
2
5
60 %
150000
0%
0
Los mejores resultados obtenidos corresponden a tamaño de población igual a dos.
Además, no se observa una diferencia entre el método 1 y el método 2 que nos permita
decir que uno es mejor que el otro, al menos en este problema concreto.
Nota 28
Siempre encuentra la misma solución y la mayorı́a de las variables que la forman son la
cadena vacı́a, Λ.
92
Resolvemos el Problema25-8-5
Tamaño del espacio de búsqueda: aprox. 248
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
88 %
27667.5
2
2
5
60 %
150000
94 %
22222.3
3
1
5
60 %
150000
86 %
23132.5
3
2
5
60 %
150000
74 %
29488.7
4
1
5
60 %
150000
70 %
26295.2
4
2
5
60 %
150000
70 %
23354.7
6
1
5
60 %
150000
62 %
48727
6
2
5
60 %
150000
64 %
48273.5
8
1
5
60 %
150000
62 %
65029.6
8
2
5
60 %
150000
56 %
75479.5
10
1
5
60 %
150000
40 %
87881.9
10
2
5
60 %
150000
32 %
95393.1
Otra vez los mejores resultados se obtienen para tamaño de población igual a dos.
Tampoco parece que sea determinante la elección del método que genera la población
inicial.
Los mejores resultados corresponden a
Usando el método 1
Usando el método 2
tp
Exito
Media Eval.
tp
Exito
Media Eval.
2
88 %
27667.5
2
94 %
22222.3
3
86 %
23132.5
3
74 %
29488.7
4
70 %
26295.2
4
70 %
23354.7
Nota 29
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
93
Resolvemos el Problema10-3-3
Tamaño del espacio de búsqueda: aprox. 212
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
100 %
278.08
2
2
5
60 %
150000
100 %
223.78
3
1
5
60 %
150000
100 %
345.96
3
2
5
60 %
150000
100 %
282.84
4
1
5
60 %
150000
100 %
332.08
4
2
5
60 %
150000
100 %
407.8
6
1
5
60 %
150000
100 %
649.7
6
2
5
60 %
150000
100 %
615.6
8
1
5
60 %
150000
100 %
845.9
8
2
5
60 %
150000
100 %
757.98
10
1
5
60 %
150000
100 %
1033.48
10
2
5
60 %
150000
100 %
1261.54
Problema aparentemente sencillo que nos permite comprobar que mayor tamaño de
población no mejora los resultados pero sı́ produce un aumento en el número de evaluaciones, por ejemplo, pasamos de 1261 evaluaciones para una población tp = 10 a tan sólo
223 evaluaciones para una población tp = 2.
Parece que el método 2 reduce el número de evaluaciones pero no es una diferencia lo
suficientemente grande como para decantarnos por este método en lugar del método 1.
Los mejores resultados corresponden a
Usando el método 1
Usando el método 2
tp
Exito
Media Eval.
tp
Exito
Media Eval.
2
100 %
278.08
2
100 %
223.78
4
100 %
332.08
3
100 %
282.84
3
100 %
345.96
4
100 %
407.8
Nota 30
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
94
Resolvemos el Problema10-5-2
Tamaño del espacio de búsqueda: aprox. 215
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
100 %
1118.56
2
2
5
60 %
150000
100 %
1323.76
3
1
5
60 %
150000
100 %
2261.4
3
2
5
60 %
150000
100 %
2412.48
4
1
5
60 %
150000
100 %
4590.82
4
2
5
60 %
150000
100 %
4229.5
6
1
5
60 %
150000
100 %
13627.5
6
2
5
60 %
150000
100 %
16416.7
8
1
5
60 %
150000
100 %
27001.4
8
2
5
60 %
150000
100 %
31674.5
10
1
5
60 %
150000
100 %
36888.2
10
2
5
60 %
150000
100 %
47870.9
Como en el problema anterior, el algoritmo ha encontrado solución en todas las ocasiones pero el número de evaluaciones ha ido creciendo a medida que hemos aumentado
el tamaño de la población. Y este aumento es considerable, pues hemos pasado de 2261.4
evaluaciones con una población tp = 3 a 36888.2 evaluaciones con una población tp = 10.
Respecto a la elección del método para la generación de la población inicial nos encontramos en la misma situación del problema10-3-3.
Los mejores resultados corresponden a
Usando el método 1
Usando el método 2
tp
Exito
Media Eval.
tp
Exito
Media Eval.
2
100 %
1118.56
2
100 %
1323.76
3
100 %
2261.4
3
100 %
2412.48
4
100 %
4590.82
4
100 %
4229.5
Nota 31
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
95
Resolvemos el Problema12-6-4
Tamaño del espacio de búsqueda: aprox. 230
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
100 %
706.18
2
2
5
60 %
150000
100 %
702.72
3
1
5
60 %
150000
100 %
1089.4
3
2
5
60 %
150000
100 %
1080.08
4
1
5
60 %
150000
100 %
2279.8
4
2
5
60 %
150000
100 %
2313.4
6
1
5
60 %
150000
100 %
5529.3
6
2
5
60 %
150000
100 %
6020.5
8
1
5
60 %
150000
100 %
9966.48
8
2
5
60 %
150000
100 %
10437
10
1
5
60 %
150000
100 %
15989.1
10
2
5
60 %
150000
100 %
17786.8
Como en los dos casos anteriores el problema12-6-4 ha resultado ser sencillo y el número
de evaluaciones aumenta a medida que crece el tamaño de la población.
Los mejores resultados corresponden a
Usando el método 1
Usando el método 2
tp
Exito
Media Eval.
tp
Exito
Media Eval.
2
100 %
706.18
2
100 %
702.72
3
100 %
1089.4
3
100 %
1080.08
4
100 %
2279.8
4
100 %
2313.4
Nota 32
Encuentra distintas soluciones.
96
Resolvemos el Problema5-3-2
Tamaño del espacio de búsqueda: aprox. 29
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
60 %
150000
100 %
338.6
2
2
5
60 %
150000
100 %
611.4
3
1
5
60 %
150000
100 %
622.68
3
2
5
60 %
150000
100 %
890.12
4
1
5
60 %
150000
100 %
858.7
4
2
5
60 %
150000
100 %
1664.44
6
1
5
60 %
150000
100 %
1487
6
2
5
60 %
150000
100 %
2822.7
8
1
5
60 %
150000
100 %
3280.5
8
2
5
60 %
150000
100 %
5467.16
10
1
5
60 %
150000
100 %
4207.6
10
2
5
60 %
150000
100 %
6347.44
Otra vez el número de evaluaciones aumenta a medida que crece el tamaño de la
población.
En esta ocasión si existe una diferencia clara entre los dos métodos propuestos para
generar la población inicial, llegando a ser de casi la mitad de evaluaciones con el método
1.
Los mejores resultados corresponden a
Usando el método 1
Usando el método 2
tp
Exito
Media Eval.
tp
Exito
Media Eval.
2
100 %
338.6
2
100 %
611.4
3
100 %
622.68
3
100 %
890.12
4
100 %
858.7
4
100 %
1664.44
Nota 33
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
97
Conclusión 1
Después de este primer contacto con el algoritmo genético resulta evidente que el tamaño
de población más adecuado es tp = 2 pues a medida que crece el número de individuos de
la población disminuye la probabilidad de éxito y aumenta el número de evaluaciones.
Sin embargo, no estamos en condiciones de afirmar que uno de los dos métodos propuestos para la generación de la población inicial sea mejor que el otro. Por eso, seguiremos
realizando los experimentos con los dos métodos.
Resumen de los mejores resultados obtenidos:
Problema
Esp. Búsq.
tp
Exito
Media Eval.
Método
15-12-4
260
2
34 %
107556
2
25-8-5
248
2
94 %
22222.3
2
10-3-3
212
2
100 %
223.78
2
10-5-2
215
2
100 %
1118.56
1
12-6-4
230
2
100 %
702.72
2
5-3-2
29
2
100 %
338.6
1
Nota 34
En los sucesivos experimentos tomaremos siempre tamaño de población tp = 2 y acotaremos los procesos por 1.5 millones de evaluaciones.
98
7.3.
Probabilidad de mutación
Nuestro segundo objetivo es determinar los valores de la probabilidad de mutación
más adecuados para la resolución de los problemas l SW ES. Para ello, fijamos todos los
parámetros excepto la mutación que irá variando desde el 20 % al 100 %.
Resolvemos el Problema10-15-5
Tamaño del espacio de búsqueda: aprox. 290
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
0%
0
2
2
5
20 %
1.5e+06
0%
0
2
1
5
40 %
1.5e+06
2%
1.06558e+06
2
2
5
40 %
1.5e+06
0%
0
2
1
5
70 %
1.5e+06
0%
0
2
2
5
70 %
1.5e+06
2%
1.12725e+06
2
1
5
80 %
1.5e+06
2%
1.37572e+06
2
2
5
80 %
1.5e+06
0%
0
2
1
5
90 %
1.5e+06
2%
801145
2
2
5
90 %
1.5e+06
2%
1.0302e+06
2
1
5
100 %
1.5e+06
4%
1.04644e+06
2
2
5
100 %
1.5e+06
2%
253817
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
100 %
4%
1.04644e+06
100 %
2%
253817
90 %
2%
801145
90 %
2%
1.0302e+06
Nota 35
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
99
Resolvemos el Problema10-5-1
Tamaño del espacio de búsqueda: aprox. 210
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
100 %
22751.7
2
2
5
20 %
1.5e+06
100 %
41875.7
2
1
5
40 %
1.5e+06
100 %
11364.6
2
2
5
40 %
1.5e+06
100 %
17492.3
2
1
5
70 %
1.5e+06
100 %
7952.02
2
2
5
70 %
1.5e+06
100 %
7800.06
2
1
5
80 %
1.5e+06
100 %
7923.1
2
2
5
80 %
1.5e+06
100 %
8119.02
2
1
5
90 %
1.5e+06
100 %
8101.9
2
2
5
90 %
1.5e+06
100 %
6422.6
2
1
5
100 %
1.5e+06
100 %
5594.96
2
2
5
100 %
1.5e+06
100 %
5600.88
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
100 %
100 %
5594.96
100 %
100 %
5600
80 %
100 %
7923.1
90 %
100 %
6422.6
Aunque el algoritmo siempre encuentra solución, el número de evaluaciones disminuye
considerablemente para valores de la probabilidad de mutación altos.
Nota 36
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
100
Resolvemos el Problema10-8-5
Tamaño del espacio de búsqueda: aprox. 248
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
98 %
381874
2
2
5
20 %
1.5e+06
94 %
304587
2
1
5
40 %
1.5e+06
98 %
203427
2
2
5
40 %
1.5e+06
100 %
180120
2
1
5
70 %
1.5e+06
100 %
129486
2
2
5
70 %
1.5e+06
100 %
179024
2
1
5
80 %
1.5e+06
100 %
134950
2
2
5
80 %
1.5e+06
100 %
114512
2
1
5
90 %
1.5e+06
98 %
90263.3
2
2
5
90 %
1.5e+06
100 %
93544.5
2
1
5
100 %
1.5e+06
100 %
86991.9
2
2
5
100 %
1.5e+06
100 %
73362.4
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
100 %
100 %
86991.9
100 %
100 %
73362.4
70 %
100 %
129486
90 %
100 %
93544.5
En esta ocasión, los valores de la probabilidad de mutación hacen que el algoritmo no
siempre encuentre solución y además aumentan el número de evaluaciones.
Nota 37
Encuentra distintas soluciones.
Una de las variables del problema puede tomar distintos valores, el resto de las variables
siempre toman el mismo valor.
101
Resolvemos el Problema25-8-3
Tamaño del espacio de búsqueda: aprox. 232
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
100 %
117905
2
2
5
20 %
1.5e+06
100 %
121339
2
1
5
40 %
1.5e+06
100 %
47327.7
2
2
5
40 %
1.5e+06
100 %
60389.5
2
1
5
70 %
1.5e+06
100 %
41581.5
2
2
5
70 %
1.5e+06
100 %
32645.8
2
1
5
80 %
1.5e+06
100 %
31538.3
2
2
5
80 %
1.5e+06
100 %
44212.9
2
1
5
90 %
1.5e+06
100 %
31687.5
2
2
5
90 %
1.5e+06
100 %
28286.8
2
1
5
100 %
1.5e+06
100 %
26726.1
2
2
5
100 %
1.5e+06
100 %
32858.1
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
100 %
100 %
26726.1
90 %
100 %
28286.8
80 %
100 %
31538.3
70 %
100 %
32645.8
Aunque siempre encuentra solución, el número de evaluaciones que fueron necesarias
para encontrar solución disminuye para valores de la probabilidad de mutación entre el
70 % y el 100 %.
Nota 38
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
102
Resolvemos el Problema25-8-5
Tamaño del espacio de búsqueda: aprox. 248
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
100 %
140364
2
2
5
20 %
1.5e+06
100 %
98455.6
2
1
5
40 %
1.5e+06
100 %
57316.9
2
2
5
40 %
1.5e+06
100 %
79764.9
2
1
5
70 %
1.5e+06
100 %
26307.5
2
2
5
70 %
1.5e+06
100 %
36819.7
2
1
5
80 %
1.5e+06
100 %
32292.8
2
2
5
80 %
1.5e+06
100 %
30747.8
2
1
5
90 %
1.5e+06
100 %
20602.3
2
2
5
90 %
1.5e+06
100 %
34846.2
2
1
5
100 %
1.5e+06
100 %
35748.3
2
2
5
100 %
1.5e+06
100 %
29859.6
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
90 %
100 %
20602.3
100 %
100 %
29859.6
70 %
100 %
26307.5
80 %
100 %
30747.8
De nuevo, el éxito es del 100 % para todos los experimentos realizados con este problema, pero el número de evaluaciones mejora para valores de la probabilidad de mutación
entre el 70 % y el 100 %.
Nota 39
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
103
Resolvemos el Problema5-15-3
Tamaño del espacio de búsqueda: aprox. 260
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
4%
881354
2
2
5
20 %
1.5e+06
0%
0
2
1
5
40 %
1.5e+06
4%
750816
2
2
5
40 %
1.5e+06
0%
0
2
1
5
70 %
1.5e+06
2%
91868
2
2
5
70 %
1.5e+06
10 %
273665
2
1
5
80 %
1.5e+06
8%
681722
2
2
5
80 %
1.5e+06
0%
0
2
1
5
90 %
1.5e+06
2%
374402
2
2
5
90 %
1.5e+06
10 %
511213
2
1
5
100 %
1.5e+06
10 %
743411
2
2
5
100 %
1.5e+06
4%
1.00708e+06
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
100 %
10 %
743411
70 %
10 %
273665
80 %
8%
681722
90 %
10 %
511213
El algoritmo genético simple no siempre encuentra solución. Notar que el tamaño del
espacio de búsqueda es de aprox. 260 . A pesar de ello, los valores de probabilidad de
mutación entre el 70 % y el 100 % proporcionan los mejores resultados.
Nota 40
Encuentra distintas soluciones.
La gran mayorı́a de las variables, aproximádamente 10 de las 15, pueden tomar distintos
valores.
104
Resolvemos el Problema15-12-4
Tamaño del espacio de búsqueda: aprox. 260
tp
Método
l
Mutación
NMax Eval.
Exito
Media Eval.
2
1
5
20 %
1.5e+06
76 %
638268
2
2
5
20 %
1.5e+06
80 %
658522
2
1
5
40 %
1.5e+06
84 %
386301
2
2
5
40 %
1.5e+06
86 %
496717
2
1
5
70 %
1.5e+06
96 %
324814
2
2
5
70 %
1.5e+06
98 %
313743
2
1
5
80 %
1.5e+06
92 %
277741
2
2
5
80 %
1.5e+06
98 %
288551
2
1
5
90 %
1.5e+06
100 %
263719
2
2
5
90 %
1.5e+06
100 %
204263
2
1
5
100 %
1.5e+06
98 %
295557
2
2
5
100 %
1.5e+06
96 %
255491
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Mutación
Exito
Media Eval.
Mutación
Exito
Media Eval.
90 %
100 %
263719
90 %
100 %
204263
100 %
98 %
295557
80 %
98 %
288551
Podemos observar que los valores de probabilidad de mutación entre el 80 % y el 100 %
no sólo mejoran el éxito sino que también reducen el número de evaluaciones necesarias
para encontrar solución.
Nota 41
Siempre encuentra la misma solución y coincide con la propuesta por Genero Problema.
La mayorı́a de las variables son la cadena vacia, Λ.
105
Conclusión 2
Resumen de los mejores resultados obtenidos:
Usando el método 1
Problema
Esp. Búsq.
Mutación
Exito
Media Eval.
10-15-5
290
100 %
4%
1.04644e+06
10-5-1
210
100 %
100 %
5594.96
10-8-5
248
100 %
100 %
86991.9
25-8-3
232
100 %
100 %
26726.1
25-8-5
248
90 %
100 %
20602.3
5-15-3
260
100 %
10 %
743411
15-12-4
260
90 %
100 %
263719
Usando el método 2
Problema
Esp. Búsq.
Mutación
Exito
Media Eval.
10-15-5
290
100 %
2%
253817
10-5-1
210
100 %
100 %
5600
10-8-5
248
100 %
100 %
73362.4
25-8-3
232
90 %
100 %
28286.8
25-8-5
248
100 %
100 %
29859.6
5-15-3
260
70 %
10 %
273665
15-12-4
260
90 %
100 %
204263
Se de duce que los mejores resultados se obtienen con una probabilidad de mutación
comprendida entre el 70 % y el 100 %.
Aunque puede parecer que la probabilidad de mutación es muy alta, hemos obtenido
valores similares a los propuestos en la resolución de los problemas SAT , que en definitiva
es nuestra única referencia.
106
Capı́tulo 8
Algoritmos Evolutivos para
Resolver l SW ES
A partir del algoritmo genético simple, definido en el capı́tulo 5, vamos a construir dos
algoritmos evolutivos.
La codificación de los individuos, la generación de la población inicial, el fitness y
los operadores genéticos se definen de la misma forma. Además, seguiremos acotando la
resolución de los problemas por 1,5 millones de evaluaciones y buscaremos soluciones cuyas
variables sean de longitud menor o igual que cinco.
8.1.
A.E. Sólo Mutación
Como consecuencia de los primeros resultados experimentales, el alto valor que toma
la probabilidad de mutación y teniendo en cuenta los algoritmos evolutivos que fueron
propuestos para resolver SAT (ver capı́tulo 2), resulta interesante modificar nuestro algoritmo genético para obtener un algoritmo evolutivo que trabaje con una población de
tamaño tp = 1.
Este algoritmo evolutivo usará sólo el operador genético de mutación.
Si los resultados que proporcionan este algoritmo mejoran los del algoritmo genético
simple, seguiremos nuestro estudio con este nuevo algoritmo o bien intentaremos modificar
el operador de cruce para mejorar el algoritmo genético simple.
El esquema de funcionamiento de este algoritmo es el siguiente
107
Seudocódigo
I0 ← generar población inicial
I0 ← evaluar (I0 )
para k = 1 hasta NIter hacer
I1 ← copiar (I0 )
I1 ← mutar (I1 )
E1 ← evaluar (I1 )
I0 ← mejor individuo (I0 , I1 )
fin para
En el apéndice D se encuentra este algoritmo escrito en C++.
En las siguientes tablas comparamos los resultados obtenidos al resolver un mismo
problema l SW ES con el algoritmo genético simple (AGS) y con este nuevo algoritmo
evolutivo (AE).
Usando el método 1 para generar la población inicial se obtuvo
Problema
Esp. Búsq.
Exito(AGS)
Exito(AE)
Eval.(AGS)
Eval.(AE)
10-5-1
210
100 %
100 %
5594.96
5599.28
10-8-5
248
100 %
78 %
86991.9
366416
25-8-3
232
100 %
88 %
26726.1
293262
25-8-5
248
100 %
66 %
35748.3
39866
5-15-3
260
10 %
6%
743411
461058
15-12-4
260
98 %
100 %
263719
203817
Usando el método 2 para generar la población inicial se obtuvo
Problema
Esp. Búsq.
Exito(AGS)
Exito(AE)
Eval.(AGS)
Eval.(AE)
10-5-1
210
100 %
100 %
5600
5155.18
10-8-5
248
100 %
66 %
73362.4
405752
25-8-3
232
100 %
88 %
32858.1
385027
25-8-5
248
100 %
60 %
29859.6
14243.1
5-15-3
260
4%
0%
1.00708e+06
0
15-12-4
260
96 %
100 %
255491
250908
108
Resulta evidente que el AE no mejora los resultados obtenidos con el AGS. Por lo
tanto, el operador de cruce que hemos definido tiene influencia en la resolución de los
sistemas.
8.2.
A.E. Búsqueda Local
Añadimos al algoritmo genético simple un proceso de búsqueda local que se aplicará a
cada individuo de las sucesivas poblaciones generadas, con el fin de mejorar (localmente)
el fitness de dichos individuos.
Para la implementación de este proceso definimos dos métodos:
Búsqueda Local 1
Dado un individuo I = α1 · · · αm , cualquiera. Se realizan dos etapas:
1. Cada subcadena αi , para i = 1, . . . , m, se somete a un proceso de busqueda local
clásico que afecta unicamente a las cadena βi , para i = 1, . . . , m. (Recordar que
αi = βi β̄i siendo βi ∈ A∗ y β̄i ∈ {B}∗ )
Este proceso consiste en recorrer de izquierda a derecha los sı́mbolos de las
cadenas βi , para i = 1, . . . , m, cambiando cada sı́mbolo, es decir, se pasa de
cero a uno o viceversa. Cada vez que se cambia un sı́mbolo se evalua el nuevo
individuo y nos quedamos con el mejor, es decir, si al cambiar un sı́mbolo de una
de las cadenas βi el fitness del nuevo individuo mejora o iguala al del individuo
anterior, entonces nos quedamos con el nuevo y pasamos a cambiar el siguiente
sı́mbolo situado más a la derecha (que pertenezca a una cadena βi ). Pero si
el fitness del individuo no mejora al cambiar un sı́mbolo, entonces se recupera
el sı́mbolo original y se pasa al siguiente. De esta forma, se recorren todos los
sı́mbolos de βi , para i = 1, . . . , m y se cambiando aquellos sı́mbolos que mejoren
el fitness del individuo.
Si al final de este proceso el fitness del individuo ha mejorado, entonces volvemos
a aplicar la búsqueda local clásica a las nuevas cadenas βi .
2. Cuando la búsqueda local clásica no permite mejorar el fitness del individuo
I = α1 · · · , αm , se aplica una búsqueda local que permite modificar en una
unidad la longitud de las cadenas βi , para i = 1, . . . , m.
109
Notar que hasta ahora lo único que hacı́amos era cambiar los sı́mbolos, pasando
de cero a uno o viceversa, pero la longitud de las cadenas βi , para i = 1, . . . , m
no cambiaba.
Para modificar la longitud de la cadena βi , para i = 1, . . . , m en una unidad,
procedemos como sigue:
a)
Disminuimos en una unidad la longitud de βi convirtiendo el último sı́mbolo
de esta cadena en un sı́mbolo blanco, de modo que la longitud de la cadena
β̄i aumenta una unidad.
Ejemplo 44
Sea βi β̄i = 0010BBB con |βi | = 4 y |β̄i | = 3.
Al disminuir la longitud de βi en una unidad se obtiene
βi β̄i = 001BBBB
siendo ahora |βi | = 3 y |β̄i | = 4.
b)
Aumentamos en una unidad la longitud de la cadena βi convirtiendo el
primer sı́mbolo blanco de la cadena β̄i en un sı́mbolo del alfabeto A, de
modo que la longitud de β̄i disminuye en una unidad.
Como A = {0, 1}, tenemos dos posibilidades para aumentar la longitud de
βi :
1) convertir en un cero el primer sı́mbolo blanco de β̄i ,
2)
convertir en uno el primer sı́mbolo blanco de β̄i .
Ejemplo 45
Sea βi β̄i = 0010BBB con |βi | = 4 y |β̄i | = 3.
Al aumentar la longitud de βi en una unidad se obtiene dos cadenas
00100BB
00101BB
siendo ahora |βi | = 5 y |β̄i | = 2.
Luego, para cadena αi obtenemos tres posibilidades al modificar en una unidad
la longitud de βi , si alguna de estas nuevas cadenas mejora el fitness del individuo, entonces αi es sustituida por la nueva cadena.
110
Cuando terminamos de modificar las longitudes de todas las cadenas βi en busca de un individuo mejor, comprobamos si el fitness ha mejorado, en ese caso
se vuelve a el paso 1. Si no hay mejora, se termina el proceso de búsqueda local
1 aplicado al individuo I.
Búsqueda Local 2
Dado un individuo I = α1 · · · αm , cualquiera.
A cada subcadena αi , se le aplica el proceso de búsqueda local clásica (que afecta
unicamente a la cadena βi ) y después se le aplica el proceso de búsqueda local que
permite modificar en una unidad la longitud de βi , tomando aquella que mejore el
fitness del individuo.
Una vez que hemos aplicado estos dos procesos a todas las subcadenas αi del individuo I, si el fitness del individuo ha mejorado volvemos a repetir el proceso. En
otro caso, la búsqueda local 2 aplicada al individuo I finaliza.
Nota 42
Observese que aunque ambos procedimientos de búsqueda local puedan parecer iguales,
hay una importante diferencia:
mientras que en el proceso de búsqueda local 1 realizamos sucesivas veces (hasta que no
se produzca mejora) lo que hemos denominado búsqueda local clásica antes de tratar de
modificar las longitudes, en el proceso de búsqueda local 2 realizamos en cada iteración
una vez la búsqueda local clásica seguida de una posible modificación en una unidad de
las longitudes.
8.3.
Resultados experimentales
Hasta ahora hemos resuelto los problemas con ayuda de un algoritmo genético simple.
En esta sección vamos a estudiar los resultados obtenidos al añadir búsqueda local.
Se intentará establecer, si fuese posible, cuál de los dos métodos implementados para
el proceso de búsqueda local es mejor.
También resultará interesante determinar si la mutación sigue siendo importante cuando se usa búsqueda local. Para ello iremos tomando distintos valores de probabilidad de
mutación.
111
Finalmente, compararemos los resultados obtenidos al aplicar un algoritmo genético
simple para resolver los problemas l SW ES y los resultados obtenidos al utilizar un A.E.
con búsqueda local.
Para poder comparar los resultados seguimos manteniendo tamaño de pobalción tp = 2,
longitud máxima de las variables que forman los individuos igual a 5 y acotamos la ejecución del algoritmo por 1.5 millones de evaluaciones.
Resolvemos el Problema10-3-3
Tamaño del espacio de búsqueda: aprox. 218
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
85.34
100 %
50.9
2
5
70 %
100 %
79.94
100 %
50.26
2
5
90 %
100 %
77.1
100 %
50.26
2
5
100 %
100 %
77.1
100 %
50.26
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
90.06
100 %
44.06
2
5
70 %
100 %
73.72
100 %
46.9
2
5
90 %
100 %
75.96
100 %
50.32
2
5
100 %
100 %
75.96
100 %
50.32
Parece que la probabilidad de mutación no influye sobre los resultados. Sin embargo,
si queda reflejado la superioridad de la búsqueda local 2 frente a la 1.
Nota 43
Siempre encuentra la misma solución.
112
Resolvemos el Problema10-8-5
Tamaño del espacio de búsqueda: aprox. 248
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
26705.3
100 %
19301.1
2
5
70 %
100 %
15371.6
100 %
10368.7
2
5
90 %
100 %
15947.3
100 %
10164.8
2
5
100 %
100 %
16017.8
100 %
8220.76
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
27036.5
100 %
21981.9
2
5
70 %
100 %
16879.7
100 %
12433.7
2
5
90 %
100 %
15947.3
100 %
9702.92
2
5
100 %
100 %
15585
100 %
9629.52
Se observa que la búsqueda local 2 reduce el número de evaluaciones. Además, a medida
que aumenta la probabilidad de mutación disminuye el número de evaluaciones.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
100 %
100 %
8220.76
2
100 %
100 %
9629.52
2
90 %
100 %
10164.8
2
90 %
100 %
9702.92
Nota 44
Admite distintas soluciones.
Una de las variables del problema puede tomar distintos valores, el resto de las variables
siempre toman el mismo valor.
113
Resolvemos el Problema10-8-3
Tamaño del espacio de búsqueda: aprox. 248
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
35040.2
100 %
22728.9
2
5
70 %
100 %
19519.9
100 %
16844.7
2
5
90 %
100 %
23727.3
100 %
20473.6
2
5
100 %
100 %
22740
100 %
16484.9
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
49985.8
100 %
28200.4
2
5
70 %
100 %
24295.2
100 %
22562
2
5
90 %
100 %
27747.4
100 %
18935.5
2
5
100 %
100 %
31129.4
100 %
16596.1
De nuevo hay que destacar la diferencia que existe entre los métodos de búsqueda local
implementados.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mutación
Exito
Media Eval.
2
100 %
100 %
16484.9
2
100 %
100 %
16596.1
2
70 %
100 %
16844.7
2
90 %
100 %
18935.5
Nota 45
Siempre encuentra la misma solución.
114
Resolvemos el Problema10-15-5
Tamaño del espacio de búsqueda: aprox. 290
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
484850
100 %
593998
2
5
70 %
100 %
474252
100 %
288598
2
5
90 %
100 %
182997
100 %
104509
2
5
100 %
100 %
457354
100 %
340361
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
831604
100 %
646195
2
5
70 %
100 %
589418
100 %
338841
2
5
90 %
100 %
472892
100 %
362079
2
5
100 %
100 %
499089
100 %
340070
En este caso podemos observar que no siempre la mejor probabilidad de mutación es
el 100 %, pues aunque siempre se encuentra solución el número de evaluaciones necesarias
varı́a.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
90 %
100 %
104509
2
70 %
100 %
338841
1
90 %
100 %
182997
2
100 %
100 %
340070
Nota 46
Siempre encuentra la misma solución.
115
Resolvemos el Problema10-15-3
Tamaño del espacio de búsqueda: aprox. 290
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
44 %
611542
76 %
338663
2
5
70 %
50 %
652274
94 %
327947
2
5
90 %
68 %
642407
98 %
362663
2
5
100 %
56 %
583024
92 %
264417
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
38 %
646377
72 %
614781
2
5
70 %
48 %
616960
94 %
424924
2
5
90 %
34 %
643584
86 %
401649
2
5
100 %
64 %
573071
92 %
413873
Como en todos los problemas anteriores la búsqueda local 2 es más efectiva.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
90 %
98 %
362663
2
70 %
94 %
424924
2
70 %
94 %
327947
2
100 %
92 %
413873
Nota 47
Admite distintas soluciones.
Existen dos variables del problema que puede tomar distintos valores, el resto de las
variables siempre toman el mismo valor.
116
Resolvemos el Problema15-12-4
Tamaño del espacio de búsqueda: aprox. 272
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
1676.5
100 %
1187.36
2
5
70 %
100 %
1377.96
100 %
1154.76
2
5
90 %
100 %
1484.46
100 %
860.82
2
5
100 %
100 %
1254.8
100 %
1058.64
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
2289.98
100 %
1729.18
2
5
70 %
100 %
1988.92
100 %
1456.82
2
5
90 %
100 %
2110.4
100 %
1395.68
2
5
100 %
100 %
2032.8
100 %
1431.26
La búsqueda local resulta ser un proceso tan potente que permite en muchas ocasiones
encontrar solución aplicandolo unicamente a la población inicial, es decir, no se llega a
usar el algoritmo genético simple.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
90 %
100 %
860.82
2
90 %
100 %
1395.68
2
100 %
100 %
1058.64
2
100 %
100 %
1431.26
Nota 48
Siempre encuentra la misma solución.
La mayorı́a de las variables son la cadena vacia, Λ.
117
Resolvemos el Problema15-7-5
Tamaño del espacio de búsqueda: aprox. 242
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
225.48
100 %
226.56
2
5
70 %
100 %
225.48
100 %
225.26
2
5
90 %
100 %
290.3
100 %
216.64
2
5
100 %
100 %
290.3
100 %
225.64
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
354.9
100 %
374.8
2
5
70 %
100 %
402.16
100 %
268.84
2
5
90 %
100 %
347.18
100 %
322.48
2
5
100 %
100 %
341.42
100 %
255.94
En la mayorı́a de los casos la búsqueda local aplicada a la población inicial es suficiente para encontrar la solución del problema, sólo en algunos casos necesita unas pocas
iteraciones del algoritmo genético para encontrarla. Por eso no existe diferencia entre los
resultados obtenidos para las distintas probabilidades de mutación.
Nota 49
Admite distintas soluciones.
Existen dos variables del problema que puede tomar distintos valores, el resto de las
variables siempre toman el mismo valor.
118
Resolvemos el Problema25-8-3
Tamaño del espacio de búsqueda: aprox. 248
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
2938.72
100 %
2336.18
2
5
70 %
100 %
1748.66
100 %
1525.2
2
5
90 %
100 %
1280.28
100 %
1405.06
2
5
100 %
100 %
1839.48
100 %
1054.16
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
3599.8
100 %
3011.56
2
5
70 %
100 %
2282.54
100 %
1665.56
2
5
90 %
100 %
2487.56
100 %
1630.5
2
5
100 %
100 %
2375.56
100 %
2222.46
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
100 %
100 %
1054.16
2
90 %
100 %
1630.5
1
90 %
100 %
1280.28
2
70 %
100 %
1665.56
Nota 50
Siempre encuentra la misma solución.
119
Resolvemos el Problema25-8-5
Tamaño del espacio de búsqueda: aprox. 248
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
4674.82
100 %
2522.74
2
5
70 %
100 %
2782.76
100 %
1517.26
2
5
90 %
100 %
3174.1
100 %
1407.38
2
5
100 %
100 %
2613.64
100 %
1189.84
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
100 %
3502.56
100 %
1710.58
2
5
70 %
100 %
2591.5
100 %
1244
2
5
90 %
100 %
2788.2
100 %
1145.1
2
5
100 %
100 %
2468.54
100 %
1165.28
Destacar de nuevo la diferencia entre el número de evaluaciones necesarias para encontrar solución usando los distintos métodos de búsqueda local.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
100 %
100 %
1189.84
2
90 %
100 %
1145.1
2
90 %
90 %
1407.38
2
100 %
100 %
1165.28
Nota 51
Siempre encuentra la misma solución.
120
Resolvemos el Problema5-15-3
Tamaño del espacio de búsqueda: aprox. 290
Usando el método 1 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
94 %
535684
88 %
311136
2
5
70 %
96 %
299938
96 %
171128
2
5
90 %
94 %
246925
100 %
192842
2
5
100 %
94 %
317776
98 %
246269
Usando el método 2 para generar la población inicial se obtuvo
Búsq. Local 1
Búsq. Local 2
tp
l
Mutación
Exito
Media Eval.
Exito
Media Eval.
2
5
20 %
62 %
541543
96 %
391371
2
5
70 %
94 %
415857
94 %
284990
2
5
90 %
88 %
341641
94 %
222077
2
5
100 %
92 %
316503
96 %
218227
En este problema, la búsqueda local 2 no sólo mejora el número de evaluaciones sino
que aumenta la probabilidad de éxito, es decir, de encontrar solución.
Los mejores resultados se obtienen para
Usando el método 1
Usando el método 2
Búsq. L.
Mut.
Exito
Media Eval.
Búsq. L.
Mut.
Exito
Media Eval.
2
90 %
100 %
192842
2
100 %
96 %
218227
2
100 %
98 %
246269
2
20 %
96 %
391371
Nota 52
Admite distintas soluciones.
121
Conclusión 3
Resumen de los mejores resultados obtenidos:
Usando el método 1
Problema
Búsq. L.
Mutación
Exito
Media Eval.
10-8-5
2
100 %
100 %
8220.76
10-8-3
2
100 %
100 %
16484.9
10-3-3
2
—
100 %
50.26
10-15-5
2
90 %
100 %
104509
10-15-3
2
90 %
98 %
362663
15-12-4
2
90 %
100 %
860.82
15-7-5
2
—
100 %
216.64
25-8-3
2
100 %
100 %
1054.16
25-8-5
2
100 %
100 %
1189.84
5-15-3
2
90 %
100 %
192842
Usando el método 2
Problema
Búsq. L.
Mutación
Exito
Media Eval.
10-8-5
2
100 %
100 %
9629.52
10-8-3
2
100 %
100 %
16596.1
10-3-3
2
—
100 %
44.06
10-15-5
2
70 %
100 %
338841
10-15-3
2
70 %
94 %
424924
15-12-4
2
90 %
100 %
1395.68
15-7-5
2
—
100 %
255.94
25-8-3
2
90 %
100 %
1630.5
25-8-5
2
90 %
100 %
1145.1
5-15-3
2
100 %
96 %
218227
Estamos en condiciones de afirmar que la búsqueda local 2 es más efectiva que la 1
y que la probabilidad de mutación sigue siendo una etapa del proceso importante en la
resolución de los problemas.
122
Hemos podido obsevar que la búsqueda local resulta decisiva en la primera etapa, cuando se genera la población inicial. Antes de empezar a trabajar con el algoritmo genético,
la población inicial es sometida a un proceso de búsqueda local y, como consecuencia,
esta población experimenta una mejora considerable. Pero, una vez iniciado el algoritmo
genético, el efecto que tiene la búsqueda local sobre las nuevas poblaciones generadas no es
significativo. Por ese motivo, creemos que es importante mantener valores de probabilidad
de mutación entre 70 % y el 100 %.
Para concluir esta sección comparamos algunos de los resultados obtenidos al aplicar
el algoritmo genético simple (AGS) y el algoritmo genético con búsqueda local (AE).
Como ya sabemos que la búsqueda local 2 es mejor que la 1, tomamos como referencia los resultados obtenidos con busqueda local 2 para compararlos con los resultados
obtenidos sin búsqueda local.
Se tienen las siguientes tablas
Usando el método 1
Problema
Mutación
Éxito(AGS)
Éxito(AE)
Media Eval.(AGS)
Media Eval.(AE)
10-15-5
70 %
0%
100 %
0
288598
(290 )
90 %
2%
100 %
801145
104509
100 %
4%
100 %
1.04644e+06
340361
10-8-5
70 %
100 %
100 %
129486
10368.7
(248 )
90 %
98 %
100 %
90263.3
10164.8
100 %
100 %
100 %
86991.9
8220.76
15-12-4
70 %
96 %
100 %
324814
1154.76
(272 )
90 %
100 %
100 %
263719
860.82
100 %
98 %
100 %
295557
1058.64
25-8-5
70 %
100 %
100 %
26307.5
1517.26
(248 )
90 %
100 %
100 %
20602.3
1407.38
100 %
98 %
100 %
35748.3
1189.84
5-15-3
70 %
2%
96 %
91868
171128
(290 )
90 %
2%
100 %
374402
192842
100 %
10 %
98 %
743411
246269
123
Usando el método 2
Problema
Mutación
Éxito(AGS)
Éxito(AE)
Media Eval.(AGS)
Media Eval.(AE)
10-15-5
70 %
2%
100 %
1.12728e+06
338841
(290 )
90 %
2%
100 %
1.0302e+06
362079
100 %
2%
100 %
253817
340070
10-8-5
70 %
100 %
100 %
179024
12433.7
(248 )
90 %
100 %
100 %
93544.5
9702.92
100 %
100 %
100 %
73362.4
9629.52
15-12-4
70 %
98 %
100 %
313743
1456.82
(272 )
90 %
100 %
100 %
204263
1395.68
100 %
96 %
100 %
255491
1431.26
25-8-5
70 %
100 %
100 %
36819.7
1244
(248 )
90 %
100 %
100 %
34846.2
1145.1
100 %
100 %
100 %
29859.6
1165.28
5-15-3
70 %
10 %
94 %
273665
284990
(290 )
90 %
10 %
94 %
511213
222077
100 %
4%
96 %
1.00708e+06
218227
Conclusión 4
La búsqueda local mejora la probabilidad de exito y reduce considerablemente el número
de evaluaciones necesarias para encontrar una solución del problema.
124
Capı́tulo 9
Experimento final
En esta sección vamos a presentar los resultados obtenidos en la resolución de un
grupo de problemas clasificados como de gran dificultad por tener un espacio de búsqueda
grande.
Como ya hemos comprobado el mejor funcionamiento de la búsqueda local 2, en toda
esta sección usaremos esta búsqueda con tamaño de población dos.
Resolvemos el Problema15-25-5
Tamaño del espacio de búsqueda: aprox. 2150
Usando el método 1 para generar la población inicial se obtuvo
Búsq. L.
tp
l
Mutación
Exito
Media Eval.
2
2
5
70 %
92 %
414500
2
2
5
80 %
98 %
384799
2
2
5
90 %
96 %
359897
2
2
5
100 %
86 %
417172
Usando el método 2 para generar la población inicial se obtuvo
Búsq. L.
tp
l
Mutación
Exito
Media Eval.
2
2
5
70 %
98 %
475580
2
2
5
80 %
96 %
424660
2
2
5
90 %
90 %
409549
2
2
5
100 %
90 %
421117
125
Mientras que con el método 1 los mejores resultados se obtienes con probabilidad
de mutación 80 % y 90 %, con el método 2 a medida que disminuye la probabilidad de
mutación mejoran los resultados, esto nos lleva a pensar en intentar resolver el problema
con menor probabilidad de mutación. Aunque según la experiencia no esperamos que haya
mejora, pues hemos establecido la probabilidad de mutación más adecuada entre el 70 %
y el 100 %.
Nota 53
Admite distintas soluciones.
Existen dos variables del problema que puede tomar distintos valores, el resto de las
variables siempre toman el mismo valor.
Resolvemos el Problema25-23-4
Tamaño del espacio de búsqueda: aprox. 2138
Usando el método 1 para generar la población inicial se obtuvo
Búsq. L.
tp
l
Mutación
Exito
Media Eval.
2
2
5
70 %
68 %
375905
2
2
5
80 %
78 %
549016
2
2
5
90 %
78 %
593530
2
2
5
100 %
76 %
601812
Usando el método 2 para generar la población inicial se obtuvo
Búsq. L.
tp
l
Mutación
Exito
Media Eval.
2
2
5
70 %
74 %
530816
2
2
5
80 %
72 %
690505
2
2
5
90 %
70 %
663918
2
2
5
100 %
70 %
519235
Se repite el comportamiento observado en el problema anterior, con el método 1 el
mejor éxito se tiene con probabilidad de mutación igual a 80 %, seguido del 90 %, mientras
que con el método 2 va mejorando el resultado a medida que disminuye la probabilidad
de mutación.
Nota 54
Siempre encuentra la misma solución.
126
Para finalizar, en la siguiente tabla se comparan los resultados obtenidos al resolver
algunos problemas en distintos espacios de búsqueda con el algoritmo genético simple
(AGS) y con el A.E. Búsqueda Local (AE).
Usamos el método 1 para generar la población inicial y mutación del 90 %
Problema
l
Esp. Búsq.
Éxito(AE)
Éxito(AGS)
Eval.(AE)
Eval.(AGS)
p10-8-3
3
232
100 %
98 %
3593.14
243568
p25-8-3
3
232
100 %
100 %
771.66
86942
p10-8-3
4
240
100 %
72 %
7094
464914
p25-8-3
5
248
100 %
74 %
1405.06
393360
p25-8-3
6
256
100 %
42 %
2502.06
644780
p5-15-3
3
260
100 %
50 %
19332.8
290949
p10-15-3
3
260
100 %
62 %
9788.18
458878
p15-12-4
4
260
100 %
100 %
644.94
193523
p10-8-3
7
264
100 %
4%
221302
60725
p25-8-3
8
272
100 %
16 %
4946.56
389959
p10-8-3
10
288
76 %
0%
577926
–
p5-15-3
5
290
100 %
6%
192842
366681
p10-15-3
5
290
98 %
10 %
362663
712000
p10-15-5
5
290
100 %
2%
104509
801145
p25-23-4
4
2115
96 %
0%
493575
–
p25-23-4
5
2138
78 %
0%
593530
–
p15-25-5
5
2150
96 %
0%
359897
–
p5-15-3
10
2165
4%
0%
464710
–
p25-8-3
20
2168
100 %
0%
31189.3
–
Conclusión 5
El algoritmo evolutivo que proponemos para resolver los problemas l SW ES usa los operadores genéticos definidos en el capı́tulo 5 y aplica búqueda local 2 a cada una de las
poblaciones generadas.
127
Apéndice A
El Algoritmo Genético Simple
// //////////////////////////////////////////////////////////// //
// /
// /
/ //
Un Algoritmo Genético para Resolver el problema l_SWES
// /
/ //
/ //
// //////////////////////////////////////////////////////////// //
#include<fstream.h> #include<stdlib.h> #include<vector.h>
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
fstream fsol("solucion.dat",ios::out);
void Pedir_Datos(string &problema, int &TamPob, int &lMaxVsol,
int &NMaxIter, int &ProMut, int &Metodo);
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist);
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist);
128
void Genera_PobIni(vector<vector<vector<int> > > &PobIni,
vector<vector<int> > &lReVsol,
vector<vector<vector<int> > > &AuxPob,
vector<vector<int> > &AuxlRsol,
const int &TamPob, const int &m_var,
const int &lMaxVsol, const int &Metodo);
void reordeno(const int &l, vector<int> &v, int &lReVsol);
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist,
const vector<int> &lReVsol,
const vector<vector<int> > &PobIni,
vector<int> &AuxIzq, vector<int> &AuxDer,
int &fitness, int &haysol);
void sustituyo( const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni);
void Mejor_Sol(int &MejorFit, vector<int> &fitness,
const int &TamPob, const int &m_var,
vector<vector<int> > &AuxPob, const int &lMaxVsol,
vector<vector<vector<int> > > &PobIni,
vector<int> &AuxlRsol, vector<vector<int> > &lReVsol);
void Ruleta(int &Sumfit, const int &TamPob, vector<int> &fitness);
void eligo_padre(int &TamPob, int &Padre, vector<int> &fitness);
void Cruce(const int &m_var, const int &lMaxVsol,
129
vector<vector<int> > &AuxPob,
vector<int> &lReVsolIzq, vector<int> &lReVsolDer,
vector<int> &AuxlRsol, vector<vector<int> > &PobIniIzq,
vector<vector<int> > &PobIniDer);
void Mutacion(const int &m_var, const int &lMaxVsol, int &muto,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol);
int main(void)
{
string problema;
int TamPob, lMaxVsol, NMaxIter, ProMut, Metodo;
int n_ec, m_var;
int haysol, muto;
int MejorFit, Sumfit, PadreIzq, PadreDer;
int i, j, k, iter;
vector<int> l_ec;
vector<vector<int> > Sist;
vector<vector<vector<int> > > PobIni;
vector<vector<int> > lReVsol;
vector<vector<vector<int> > > AuxPob; //vector auxiliar
vector<vector<int> > AuxlRsol; //vector auxiliar
vector<int> fitness;
vector<int> AuxIzq; //Sólo se utiliza en la función evalua
vector<int> AuxDer; //Sólo se utiliaza en la función evalua
randomize();
Pedir_Datos(problema, TamPob, lMaxVsol, NMaxIter, ProMut, Metodo);
Lee_Prob(problema, n_ec, m_var, l_ec, Sist);
130
Simplifica(n_ec, l_ec, Sist);
Genera_PobIni(PobIni, lReVsol, AuxPob,
AuxlRsol, TamPob, m_var, lMaxVsol, Metodo);
fitness.resize(TamPob);
haysol= 0;
for(k= 0; k<TamPob; k++)
Evalua(n_ec, l_ec, Sist, lReVsol[k], PobIni[k],
AuxIzq, AuxDer, fitness[k], haysol);
fsol<<"La poblacion inicial generada es: "<<endl;
for(k= 0; k<TamPob; k++){ for(i= 0; i< m_var; i++){
for(j=0;j<lMaxVsol; j++) fsol<<PobIni[k][i][j]; fsol<<" "; } //fin
fsol<<endl; fsol<<"Con fitness "<<fitness[k]<<endl; } //fin for(k)
for(iter= 0; iter<NMaxIter && haysol == 0; iter++)
{
Mejor_Sol(MejorFit, fitness, TamPob, m_var, AuxPob[0],
lMaxVsol, PobIni, AuxlRsol[0], lReVsol);
Ruleta(Sumfit, TamPob, fitness);
for(k= 1; k< TamPob; k++)
{
// * Selección *
PadreIzq = rand() % Sumfit;
eligo_padre(TamPob, PadreIzq, fitness);
PadreDer = rand() % Sumfit;
eligo_padre(TamPob, PadreDer, fitness);
131
// * Cruce *
Cruce(m_var, lMaxVsol, AuxPob[k], lReVsol[PadreIzq], lReVsol[PadreDer],
AuxlRsol[k], PobIni[PadreIzq], PobIni[PadreDer]);
// * Mutación *
if(ProMut!=0)
{
muto = rand() %100;
if(muto< ProMut)
// * Muto la solusión k_esima *
Mutacion(m_var, lMaxVsol, muto, AuxPob[k], AuxlRsol[k]);
}//fin if(ProMut!=0)
}//fin for(k)
for(k= 0; k< TamPob; k++)
for(i= 0; i< m_var; i++)
{
lReVsol[k][i]= AuxlRsol[k][i];
for(j= 0;j< lMaxVsol; j++) PobIni[k][i][j]= AuxPob[k][i][j];
}//fin for(i)
fitness[0]= MejorFit;
for(k= 1; k<TamPob; k++)
{
Evalua(n_ec, l_ec, Sist, lReVsol[k],PobIni[k],AuxIzq,
AuxDer, fitness[k], haysol);
}//fin for(k)
fsol<<"La poblacion generada en la ultima iteracion realizada
es:"<<endl;
132
for(k= 0; k< TamPob; k++) { for(i= 0; i< m_var; i++) {
for(j= 0;j< lMaxVsol; j++) fsol<<PobIni[k][i][j]; fsol<<" ";}//fin
fsol<<endl; fsol<<"Con fitness "<<fitness[k]<<endl; }//fin for(k)
}//fin for(iter)
if(haysol == 1)
{
fsol<<"El programa ha encontrado solucion con una poblacion de "<<endl;
fsol<<"tama~
no "<<TamPob<<" y longitud máxima de las variables "<<endl;
fsol<<"igual a "<<lMaxVsol<<" ."<<endl;
fsol<<"La probabilidad de mutacion utiliza fue "<<ProMut<<" ."<<endl;
fsol<<"La solucion propuesta tras "<<iter<<" iteraciones es: "<<endl;
k=0;
while(fitness[k] !=0) k= k + 1;
for(i= 0; i< m_var; i++)
{
fsol<<"x"<<i<<" = ";
if(lReVsol[k][i] == 0) fsol<<"la palabra vacia."<<endl;
else
{
for(j= 0;j< lReVsol[k][i]; j++) fsol<<PobIni[k][i][j]<<" ";
fsol<<endl;
}//fin else
}//fin for(i)
}//fin if(haysol==1)
if(haysol == 0)
{
fsol<<"El programa NO ha encontrado solucion."<<endl;
fsol<<"Los datos propuestos fueron: "<<endl;
fsol<<"Tama~
no de poblacion igual a "<<TamPob<<" ."<<endl;
133
fsol<<"Longitud máxima de las variables igual a "<<lMaxVsol<<" ."<<endl;
fsol<<"La probabilidad de mutacion utiliza fue "<<ProMut<<" ."<<endl;
}//fin if(haysol==0)
fsol<<"La poblacion generada en la ultima iteracion realizada es: "<<endl;
for(k= 0; k< TamPob; k++)
{
for(i= 0; i< m_var; i++)
{
for(j= 0;j< lMaxVsol; j++) fsol<<PobIni[k][i][j];
fsol<<" ";
}//fin for(i)
fsol<<endl;
fsol<<"Con fitness "<<fitness[k]<<endl;
}//fin for(k)
}//fin programa
void Pedir_Datos(string &problema, int &TamPob, int &lMaxVsol,
int &NMaxIter, int &ProMut, int &Metodo)
{
cout<<"Indique el nombre del fichero que contiene el problema: "<<endl;
cin>> problema;
do{
cout<<"Escriba el tamanio (>0) de la poblacion inicial:"<<endl;
cin>> TamPob;
}while(TamPob<=0);
do{
cout<<"Escriba la longitud maxima(>0) de las candidatas a solucion:"<<endl;
cin>> lMaxVsol;
}while(lMaxVsol<=0);
134
do{
cout<<"Numero maximo de iteraciones que desea realizar : "<<endl;
cin>>NMaxIter;
}while(NMaxIter<0);
do{
cout<<"Cual es la probabilidad de mutacion que desea utilizar?"<<endl;
cout<<"Indique esta probabilidad en porcentaje de 100"<<endl;
cin>> ProMut;
cout<<"Recuerde que se trata de un valor entero entre cero y 100."<<endl;
cout<<endl<<endl;
}while(ProMut>100 || ProMut<0);
cout<<"Eliga el metodo que desea utilizar para generar la poblacion"<<endl;
cout<<"inicial."<<endl<<endl;
do{
cout<<"Pulse 1 si quiere fijar la longitud real de la variable y "<<endl;
cout<<"luego generar la variable con simbolos del alfabeto {0,1}."<<endl;
cout<<endl;
cout<<"Pulse 2 si quiere generar la variable en {0,1,2} y luego "<<endl;
cout<<"calcular la longitud real de la variable."<<endl<<endl;
cout<<"Pulse 3 si desea recibir mas informacion sobre los metodos"<<endl;
cout<<"usados en la generacion de la poblacion inicial."<<endl<<endl;
cin>> Metodo;
if(Metodo == 3)
{
cout<<"El metodo-1 elige la longitud real de la variable que va a"<<endl;
cout<<"generar entre cero (que corresponde a la palabra vacia) y "<<endl;
cout<<"la longitud maxima propuesta. Una vez fijada la longitud, "<<endl;
cout<<"genera la variable como una cadena de ceros y unos. "<<endl<<endl;
cout<<"El metodo-2 genera la variable como una cadena de simbolos"<<endl;
cout<<"pertenecientes al conjunto {0,1,2} de longitud la longitud"<<endl;
cout<<"maxima propuesta. Despues reordena esta cadena, colocando"<<endl;
cout<<"todos los doses al final de la misma (pues representan el "<<endl;
135
cout<<"simbolo vacio). Finalmente, calcula el numero de ceros y "<<endl;
cout<<"unos que aparecen en la cadena y que corresponde a la "<<endl;
cout<<"longitud real de la variable generada."<<endl<<endl;
cout<<"Que metodo desea utilizar?"<<endl;
cin>> Metodo;
}//fin if
}while(Metodo<1 || Metodo>2);
}//fin Pedir_Datos
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist)
{
ifstream fprob(problema.c_str(),ios::in);
fprob>>n_ec;
fprob>>m_var;
l_ec.resize(2*n_ec);
Sist.resize(2*n_ec);
for(int i= 0; i< 2*n_ec; i++){
fprob>>l_ec[i]; Sist[i].resize(l_ec[i]);
for(int j= 0; j< l_ec[i]; j++) fprob>>Sist[i][j];
}//fin for(i)
}//fin Lee_Prob
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist)
{
int coincidencia, columnas, minimo;
for(int i= 0; i< 2*n_ec - 1; i+= 2)
{
coincidencia= 0; columnas= 0; minimo= min (l_ec[i],l_ec[i+1]);
while(Sist[i][columnas]== Sist[i+1][columnas] && columnas< minimo)
{
coincidencia= coincidencia +1; columnas= columnas +1;
136
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i]-coincidencia;
for(int j= 0; j< l_ec[i]; j++)
Sist[i][j]= Sist[i][j+coincidencia];
Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia;
for(int j= 0; j< l_ec[i+1]; j++)
Sist[i+1][j]= Sist[i+1][j+coincidencia];
Sist[i+1].resize(l_ec[i+1]);
}//fin if
coincidencia= 0; columnas= 1; minimo= min (l_ec[i],l_ec[i+1]);
while(columnas<= minimo &&
Sist[i][l_ec[i]-columnas] == Sist[i+1][l_ec[i+1]-columnas])
{
coincidencia= coincidencia +1;
columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i] -coincidencia; Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia; Sist[i+1].resize(l_ec[i+1]);
}//fin if
}//fin for(i)
}//fin Simplifica
void Genera_PobIni(vector<vector<vector<int> > > &PobIni,
vector<vector<int> > &lReVsol,
vector<vector<vector<int> > > &AuxPob,
vector<vector<int> > &AuxlRsol, const int &TamPob,
const int &m_var, const int &lMaxVsol, const int &Metodo)
137
{
int longitud;
PobIni.resize(TamPob);
lReVsol.resize(TamPob);
AuxPob.resize(TamPob); //vector auxiliar
AuxlRsol.resize(TamPob); //vector auxiliar
for(int k= 0; k< TamPob; k++)
{
PobIni[k].resize(m_var);
lReVsol[k].resize(m_var);
AuxPob[k].resize(m_var);
//vector auxiliar
AuxlRsol[k].resize(m_var); //vector auxiliar
for(int i= 0; i< m_var; i++)
{
PobIni[k][i].resize(lMaxVsol);
if(Metodo == 1)
{
longitud= lMaxVsol +1;
lReVsol[k][i]= rand() % longitud;
for(int j= 0; j< lReVsol[k][i]; j++) PobIni[k][i][j]= rand() % 2;
for(int j= lReVsol[k][i]; j< lMaxVsol; j++) PobIni[k][i][j]= 2;
}//fin if(Metodo==1)
if(Metodo == 2)
{
lReVsol[k][i]= lMaxVsol;
for(int j= 0; j< lMaxVsol; j++) PobIni[k][i][j] = rand() % 3;
reordeno(lMaxVsol, PobIni[k][i],lReVsol[k][i]);
}//fin if(Metodo==2)
}//fin for(i)
}//fin for(k)
}//fin Genera_PobIni
138
void reordeno(const int &l, vector<int> &v,int &lReVsol)
{
// Reordeno la variable y calculo la longitud real de la misma
int columna;
for(int j= 0; j< l-1; j++)
if(v[j] == 2)
{
columna= j+1;
while(v[columna] == 2 && columna< l-1) columna = columna +1;
if(v[columna]!= 2)
{
v[j]= v[columna];
v[columna]= 2;
}//fin if
}//if(v[j] == 2)
for(int j= 0; j< l; j++)
if(v[j] == 2) lReVsol= lReVsol -1;
}//fin reordeno
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist, const vector<int> &lReVsol,
const vector<vector<int> > &PobIni, vector<int> &AuxIzq,
vector<int> &AuxDer, int &fitness, int &haysol)
{
int lAuxIzq, lAuxDer;
int coincidencia, minimo;
coincidencia = 0;
for(int i= 0; i<n_ec; i++)
{
lAuxIzq= l_ec[2*i];
lAuxDer= l_ec[2*i+1];
AuxIzq.resize(lAuxIzq);
139
AuxDer.resize(lAuxDer);
sustituyo(Sist[2*i], lAuxIzq, AuxIzq, lReVsol, PobIni);
sustituyo(Sist[2*i+1], lAuxDer, AuxDer, lReVsol, PobIni);
minimo = min(lAuxIzq,lAuxDer);
for(int j= 0;j< minimo; j++)
if(AuxIzq[j] != AuxDer[j]) coincidencia= coincidencia+1;
coincidencia= coincidencia + abs(lAuxIzq-lAuxDer);
}//fin for(i)
fitness= coincidencia;
if(fitness == 0) haysol= 1;
}//fin Evaluacion
void sustituyo(const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni)
{
int auxiliar, variable;
int j, l;
auxiliar = 0;
for(j= 0;j< lAux; j++)
if(Sist[j+auxiliar]==0 || Sist[j+auxiliar]==1) Aux[j] = Sist[j+auxiliar];
else
{
variable = Sist[j+auxiliar] - 3;
if(lVsol[variable]==0)
{
lAux=lAux-1;
Aux.resize(lAux);
auxiliar=auxiliar+1;
j=j-1;
}
else
140
{
lAux=lAux+lVsol[variable]-1;
Aux.resize(lAux);
for(l= 0; l< lVsol[variable]; l++) Aux[j+l] = PobIni[variable][l];
auxiliar = auxiliar - lVsol[variable] + 1;
j=j+lVsol[variable]-1;
}
}//fin else
}//fin sustituyo
void Mejor_Sol(int &MejorFit, vector<int> &fitness,
const int &TamPob, const int &m_var, vector<vector<int> > &AuxPob,
const int &lMaxVsol, vector<vector<vector<int> > > &PobIni,
vector<int> &AuxlRsol, vector<vector<int> > &lReVsol)
{
int minimo, variable;
variable = 0;
MejorFit = fitness[0];
for(int k= 1; k< TamPob; k++)
{
if(fitness[k]< MejorFit)
{
MejorFit= fitness[k];
variable= k;
}//fin if(fitness[k]< MejorFit)
if(MejorFit == fitness[k])
{
minimo= rand() % 2;
if(minimo == 0)
{
MejorFit= fitness[k];
variable= k;
141
}//fin if(minimo == 0)
}//fin if(MejorFit == fitness[k])
}//fin for(k)
for(int i= 0; i< m_var; i++)
{
AuxPob[i].resize(lMaxVsol);
AuxlRsol[i]= lReVsol[variable][i];
for(int j= 0; j< lMaxVsol; j++) AuxPob[i][j]= PobIni[variable][i][j];
}//fin for(i)
}//fin Mejor_Sol
void Ruleta(int &Sumfit, const int &TamPob, vector<int> &fitness)
{
Sumfit = 0;
for(int k= 0; k< TamPob; k++) Sumfit= Sumfit + fitness[k];
for(int k= 0; k< TamPob; k++) fitness[k]= Sumfit - fitness[k];
Sumfit = 0;
for(int k= 0; k< TamPob; k++) Sumfit= Sumfit + fitness[k];
}//fin Ruleta
void eligo_padre(int &TamPob, int &Padre, vector<int> &fitness)
{
int limInf, limSup, coinciden;
coinciden= 0;
limInf = 0;
limSup = fitness[0];
for(int i= 0; i<TamPob && coinciden == 0; i++)
{
if(limInf <= Padre && Padre < limSup)
{
Padre = i;
coinciden = 1;
142
}
else
{
limInf = limInf + fitness[i];
limSup = limSup + fitness[i+1];
}
}
}//fin eligo_padre
void Cruce(const int &m_var, const int &lMaxVsol,
vector<vector<int> > &AuxPob,
vector<int> &lReVsolIzq, vector<int> &lReVsolDer,
vector<int> &AuxlRsol, vector<vector<int> > &PobIniIzq,
vector<vector<int> > &PobIniDer)
{
int minimo, columna, longitud;
for(int i= 0; i< m_var; i++)
{
AuxPob[i].resize(lMaxVsol);
minimo= min(lReVsolIzq[i],lReVsolDer[i]);
AuxlRsol[i]= minimo;
for(int j= 0; j< minimo; j++)
{
columna= rand() % 2;
if(columna == 0) AuxPob[i][j]= PobIniIzq[i][j];
else AuxPob[i][j]= PobIniDer[i][j];
}//fin for(j)
longitud= lMaxVsol - minimo;
columna= longitud +1;
columna= rand() % columna;
if(lReVsolIzq[i]< lReVsolDer[i])
{
143
for(int j= 0; j< columna; j++)
{
AuxPob[i][minimo+j]= PobIniDer[i][minimo+j];
if(AuxPob[i][minimo+j]!= 2) AuxlRsol[i]= AuxlRsol[i] +1;
}//fin for(j)
for(int j= columna; j< longitud; j++) AuxPob[i][minimo+j]= 2;
}//fin if
else
{
for(int j= 0; j< columna; j++)
{
AuxPob[i][minimo+j]= PobIniIzq[i][minimo+j];
if(AuxPob[i][minimo+j]!= 2) AuxlRsol[i] = AuxlRsol[i] +1;
}//fin for(j)
for(int j= columna; j< longitud; j++) AuxPob[i][minimo+j] = 2;
}//fin else
}//fin for(i)
}//fin cruce
void Mutacion(const int &m_var, const int &lMaxVsol, int &muto,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol)
{
for(int i= 0; i< m_var; i++){
for(int j= 0; j< lMaxVsol; j++)
{
muto= rand() % lMaxVsol;
if(muto == 0) AuxPob[i][j]= rand() % 3;
}//fin for (j)
AuxlRsol[i]= lMaxVsol;
reordeno(lMaxVsol, AuxPob[i],AuxlRsol[i]);
}//fin for(i)
}//fin Mutacion
144
Apéndice B
El Algoritmo Genero Problema
// //////////////////////////////////////////////////////////// //
// /
// /
/ //
Un Algoritmo que Genera Sistemas de ‘‘Word Ecuations’’
// /
/ //
/ //
// //////////////////////////////////////////////////////////// //
#include<fstream.h> #include<stdlib.h> #include<vector.h>
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
void Pedir_Datos(string &sistema, string &soluciones,
int &n_ec, int &lmax_ec,
int &m_var, int &lmax_var);
void Genero_Cadenas(const int &SumaLong, const int &lmax_ec,
vector<int> &Ecizq, vector<int> &Ecder,
int &l_ecizq, int &l_ecder);
void ruleta(const int &SumaLong, const int &lmax_ec, int &l_ec);
145
void Def_variable(const int &n_ec, const int &m_var,
const int &lmax_var, vector<int> &l_ec,
vector<vector<int> > &Ec, vector<int> &l_var,
vector<vector<int> > &Var, int &contador);
void insertar(const int &k,int &posicion, int &a, vector<int> &v);
void sustituir(const int &k, int &posicion, int &a,
const int &b,vector<int> &v);
void recorro_ec(int &contador, const int &k, int &l_ec,
const int &lmax_var, const int &l_var,
const vector<int> &Var, vector<int> &Ec);
void mejora(int &l_ecizq,
vector<int> &Ecizq,
const int &SumaLong, int &l_ecder, vector<int> &Ecder,
const int &lmax_var, const vector<int> &l_var,
const vector<vector<int> > Var,
const int &lmax_ec, const int &m_var);
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Ec);
int main(void)
{
string sistema;
string soluciones;
int i, j, k, l, m;
int probabilidad;
int n_ec, lmax_ec, m_var, lmax_var;
int indice, posicion, auxiliar, contador, coincidencia, coinciden;
146
int SumaLong;
vector<vector<int> > Ec;
vector<vector<int> > Var;
vector<int> l_ec;
vector<int> l_var;
randomize();
Pedir_Datos(sistema, soluciones, n_ec, lmax_ec, m_var, lmax_var);
fstream fprob(sistema.c_str(),ios::out);
fstream fsol(soluciones.c_str(),ios::out);
fsol<<"n_ec= "<<n_ec<<" lmax_ec= "<<lmax_ec<<" m_var= "<<m_var;
fsol<<" lmax_var= "<<lmax_var<<endl;
Ec.resize(2*n_ec);
Var.resize(m_var);
l_ec.resize(2*n_ec);
l_var.resize(m_var);
SumaLong= (lmax_ec +1)*lmax_ec / 2;
for(i= 0; i< 2*n_ec -1; i+= 2)
Genero_Cadenas(SumaLong, lmax_ec, Ec[i],
Ec[i+1], l_ec[i], l_ec[i+1]);
Def_variable(n_ec, m_var, lmax_var, l_ec, Ec, l_var, Var, contador);
for(k= 0; k< m_var && contador> 0; k++)
for(i= 0;i< 2*n_ec; i++)
147
recorro_ec(contador, k, l_ec[i], lmax_var, l_var[k],
Var[k], Ec[i]);
if(contador <= 0)
{
fprob<<"El programa ha fracasado."<<endl;
fprob<<"No hemos podido generar un sistema de"<<endl;
fprob<<n_ec<<" ecuaciones con "<<m_var<<" variables."<<endl;
indice = k;
for(l= indice; l< m_var; l++)
{
l_var[l]= 0;
Var[l].resize(l_var[l]);
}//fin for(l)
}//fin if
for(i= 0; i< 2*n_ec -1; i+= 2)
mejora(l_ec[i], Ec[i], SumaLong, l_ec[i+1], Ec[i+1], lmax_var,
l_var, Var, lmax_ec, m_var);
Simplifica(n_ec, l_ec, Ec);
if(contador >= 0)
{
fprob<<n_ec<<endl;
fprob<<m_var<<endl;
for(i= 0; i< 2*n_ec; i++)
{
fprob<<l_ec[i]<<"
";
for(j= 0; j<l_ec[i]; j++) fprob<< Ec[i][j]<<" ";
fprob<<endl;
}//fin for(i)
fsol<<"las soluciones son :"<<endl;
148
for(i= 0; i< m_var; i++)
{
if(l_var[i] == 0) fsol<<"2";
else
for(j= 0; j< l_var[i]; j++) fsol<<Var[i][j]<<" ";
fsol<<endl;
}//fin for(i)
}//fin if(contador >= 0)
}//fin programa
void Pedir_Datos(string &sistema, string &soluciones, int &n_ec,
int &lmax_ec, int &m_var, int &lmax_var)
{
cout<<"Indique el fichero donde se guardara el sistema generado:"<<endl;
cin>> sistema;
cout<<"Indique el fichero donde se guardaran las soluciones: "<<endl;
cin>> soluciones;
do{
cout<<"Indique el numero de ecuaciones(>0) que desea construir:"<<endl;
cin>> n_ec;
cout<<endl;
}while ( n_ec <= 0 );
cout<<"Definimos la longitud de una ecuacion como: "<<endl;
cout<<"’numero de simbolos que aparecen en cada lado de la ecuacion’."<<endl;
cout<<endl;
do{
cout<<"Indique la longitud maxima(>0) de las ecuaciones del sistema:"<<endl;
cin>> lmax_ec;
cout<<endl;
}while ( lmax_ec <= 0 );
cout<<"El sistema constara de al menos una variable."<<endl;
do{
149
cout<<"Indique el numero de variables(>0): "<<endl;
cin>> m_var;
cout<<endl;
}while ( m_var <= 0 );
do{
cout<<"Indique la longitud maxima(>0) de las variables: "<<endl;
cin>> lmax_var;
cout<<endl;
}while ( lmax_var <= 0 );
}//fin void Pedir_Datos
void Genero_Cadenas(const int &SumaLong, const int &lmax_ec,
vector<int> &Ecizq, vector<int> &Ecder,
int &l_ecizq, int &l_ecder)
{
ruleta(SumaLong,lmax_ec,l_ecizq);
l_ecder= l_ecizq;
Ecizq.resize(l_ecizq);
Ecder.resize(l_ecder);
for(int j= 0; j< l_ecizq; j++)
{
Ecizq[j]= rand() % 2;
Ecder[j]= Ecizq[j];
}//fin for(j)
}//fin void Genero_Cadenas
void ruleta(const int &SumaLong, const int &lmax_ec, int &l_ec)
{
int aux, sum, limInf, limSup;
aux = rand() % SumaLong;
sum = 1;
limInf = 0;
150
limSup = sum;
l_ec = 0;
for(int j= 0; j< lmax_ec && l_ec == 0; j++)
if(limInf<= aux && aux< limSup) l_ec = j +1;
else
{
sum= sum +1;
limInf= limSup;
limSup= limSup +sum;
}//fin else
}//fin void ruleta
void Def_variable(const int &n_ec, const int &m_var,
const int &lmax_var, vector<int> &l_ec,
vector<vector<int> > &Ec, vector<int> &l_var,
vector<vector<int> > &Var, int &contador)
{
int auxiliar, indice, posicion;
contador = 0;
for(int i= 0; i< 2*n_ec; i++) contador = contador + l_ec[i];
for(int k= 0; k< m_var && contador >0; k++)
{
do{
indice = 2 * n_ec;
indice = rand() % indice;
posicion = rand() % l_ec[indice];
}while(Ec[indice][posicion]!=0 && Ec[indice][posicion]!=1);
auxiliar = 0;
for(int j= posicion; j< l_ec[indice] &&
(Ec[indice][j]==0 || Ec[indice][j]==1); j++)
auxiliar = auxiliar + 1;
auxiliar = min(lmax_var,auxiliar) +1;
151
l_var[k] = rand() % auxiliar;
Var[k].resize(l_var[k]);
if(l_var[k]==0) insertar( k, posicion, l_ec[indice], Ec[indice]);
else
{
auxiliar = 0;
for(int j= posicion; j< posicion +l_var[k]; j++)
{
Var[k][auxiliar]= Ec[indice][j];
auxiliar= auxiliar +1;
}//fin for(j)
sustituir(k, posicion, l_ec[indice], l_var[k], Ec[indice]);
}//else
contador = contador -l_var[k];
}//fin for(k)
}//fin void Def_variable
void insertar(const int &k,int &posicion, int &a, vector<int> &v)
{
a = a+1;
v.resize(a);
for(int l = a-1; l> posicion; l--) v[l] = v[l-1];
v[posicion] = k+3;
}//fin insertar
void sustituir(const int &k,int &posicion,int &a,const int &b,
vector<int> &v)
{
v[posicion]= k+3;
a = a -b +1;
for(int l= posicion; l< a-1; l++) v[l+1]=v[l+b];
v.resize(a);
152
}// fin sustituir
void recorro_ec(int &contador, const int &k, int &l_ec,
const int &lmax_var, const int &l_var,
const vector<int> &Var, vector<int> &Ec)
{
int coincidencia, probabilidad, auxiliar, posicion;
coincidencia = 1;
for(int j= 0; j< l_ec && coincidencia == 1; j++)
if(l_var==0)
{
if((Ec[j] == 0 || Ec[j] == 1) && (Ec[j-1] != k+3))
{
probabilidad = 4*lmax_var;
probabilidad = rand() % probabilidad;
if(probabilidad == 0)
{
insertar( k, j, l_ec, Ec);
j=j+1;
}//fin if
}//fin if
}
else
{
auxiliar=l_ec-j;
if(l_var > auxiliar) coincidencia = 0;
else
{
auxiliar = 0;
coincidencia = 1;
posicion = j;
for(int l= posicion;l< posicion +l_var && coincidencia == 1; l++)
153
if(Ec[l] != Var[auxiliar]) coincidencia = 0;
else auxiliar = auxiliar + 1;
}//fin else
if(coincidencia == 1)
{
// * Cambio con probabilidad l_var[k]/lmax_var *
probabilidad = rand() % lmax_var;
probabilidad = probabilidad + 1;
if(probabilidad <= l_var)
{
sustituir( k, posicion, l_ec, l_var, Ec);
contador = contador - l_var;
}//fin if(probabilidad <= l_var)
}//fin if(coincidencia == 1)
}//fin else
}//fin recorro_ec
void mejora(int &l_ecizq,
vector<int> &Ecizq,
const int &SumaLong, int &l_ecder, vector<int> &Ecder,
const int &lmax_var, const vector<int> &l_var,
const vector<vector<int> > Var,
const int &lmax_ec, const int &m_var)
{
int coinciden, contador, cont_izq, cont_der;
do{
coinciden= 0;
cont_izq = 0;
for(int j= 0; j< l_ecizq; j++)
if(Ecizq[j]!=0 && Ecizq[j]!=1) cont_izq = cont_izq +1;
if(cont_izq == 0)
for(int k= 0; k< m_var && contador> 0; k++)
recorro_ec(cont_izq, k, l_ecizq, lmax_var, l_var[k],Var[k], Ecizq);
154
cont_der = 0;
for(int j= 0; j< l_ecder; j++)
if(Ecder[j]!=0 && Ecder[j]!=1) cont_der = cont_der +1;
if(cont_der == 0)
for(int k= 0; k< m_var && contador> 0; k++)
recorro_ec(cont_der, k, l_ecder, lmax_var, l_var[k],Var[k], Ecder);
if(l_ecizq == l_ecder)
{
coinciden= 1;
for(int j= 0; j< l_ecizq && coinciden == 1; j++)
if(Ecizq[j]!=Ecder[j]) coinciden=0;
if(coinciden == 1) // * defino una nueva ecuacion *
{
Genero_Cadenas(SumaLong,lmax_ec,Ecizq,Ecder,l_ecizq,l_ecder);
contador= 2*l_ecizq;
for(int k= 0; k< m_var && contador> 0; k++)
{
recorro_ec(contador, k, l_ecizq, lmax_var, l_var[k],
Var[k], Ecizq);
recorro_ec(contador, k, l_ecder, lmax_var, l_var[k],
Var[k], Ecder);
}//fin for(k)
}//fin if(coinciden == 1)
}//fin if(l_ecizq == l_ecder)
}while(coinciden == 1 || cont_izq == 0 || cont_der == 0);
}//fin mejora
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Ec)
{
int coincidencia, columnas, minimo;
155
for(int i= 0; i< 2*n_ec - 1; i+= 2)
{
coincidencia= 0; columnas= 0; minimo= min (l_ec[i],l_ec[i+1]);
while(Ec[i][columnas]== Ec[i+1][columnas] && columnas< minimo)
{
coincidencia= coincidencia +1; columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i]-coincidencia;
for(int j= 0; j< l_ec[i]; j++)
Ec[i][j]= Ec[i][j+coincidencia];
Ec[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia;
for(int j= 0; j< l_ec[i+1]; j++)
Ec[i+1][j]= Ec[i+1][j+coincidencia];
Ec[i+1].resize(l_ec[i+1]);
}//fin if
coincidencia= 0; columnas= 1; minimo= min (l_ec[i],l_ec[i+1]);
while(columnas<= minimo &&
Ec[i][l_ec[i]-columnas] == Ec[i+1][l_ec[i+1]-columnas])
{
coincidencia= coincidencia +1;
columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i] -coincidencia; Ec[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia; Ec[i+1].resize(l_ec[i+1]);
}//fin if
}//fin for(i)
}//fin Simplifica
156
Apéndice C
Los problemas propuestos
En el capı́tulo de experimentos hacemos referencia a una cadena de problemas que
hemos seleccionado para estudiar el comportamiento del algoritmo genético diseñado.
Estos son algunos de los problemas propuestos:
C.1.
El problema 10-15-3
Sistema de 10 “word equations” con 15 variables de longitudes comprendidas entre 0
y 3, siendo al menos una de ellas una cadena de longitud 3.
x1 x13 x4 x7 = x1 x2 x11 x6
x13 x4 x4 x1 x1 x1 x11 = x1 x4 1x6 x1 x13 x4
x1 x6 x5 x1 = 0x1 x1 x10
x1 1 = x11
x11 x8 10x6 x9 x12 x13 = 0x2 1x4 x1 0x1 x15 x13
x1 x4 x1 0x5 0x2 x1 x6 = x11 x6 1x4 x6 x1 x12 x13
1x1 x4 x1 1x4 0 = x2 x8 1x1 x3 x4 x12 0
1x2 x13 x6 x1 10 = x4 0x1 x8 0x2 0x4 x13
0x4 x3 10x2 x6 = x8 x11 x8 x3 1x1 x12 0x1
x5 x6 x1 x1 x13 = x2 x5 x14 x7 0
157
C.2.
El problema 10-15-5
Sistema de 10 “word equations” con 15 variables de longitudes comprendidas entre 0
y 5, siendo al menos una de ellas una cadena de longitud 5.
x3 x6 x5 x17 01 = x12 1x15 x16 1x12 00x11 1x9 x14 x1
x10 x5 0x9 0 = x10 x5 x9 x17 0x12
1x12 x3 x4 x8 x9 0 = x17 1x12 0x15 x5 x16 0x4 1x9 x17 0
x9 x11 1 = x9 1
0 = x16 0
x6 x3 x12 x10 x10 = 1x16 10x9 x15 x3
x5 x11 01x5 0x7 0 = x8 x5 x3 x11 x17 0
1x9 0x9 0x9 x4 x13 = x10 x9 00x11 x12 0x14 00
x12 = x4 x9
10x13 1x3 0x10 x7 0 = 1x9 x8 101x6
C.3.
El problema 10-3-3
Sistema de 10 “word equations” con 3 variables de longitudes comprendidas entre 0 y
3, siendo al menos una de ellas una cadena de longitud 3.
x4 00100x3 01x3 = 1000100x3 01x3
x4 x4 0101x4 x5 0 = x4 10010x3 1010
00x3 = 00110
0x5 x4 01x4 0x3 0 = 0x4 x3 0x3 0x3 0
11x3 00x4 = 11x3 0010
1001 = x4 01
10001x3 x4 1x3 0 = x4 001x3 x5 x3 0
x4 x4 01000010 = x4 x4 010000x4
010x4 000000 = 0x4 10000000
1100010x4 x4 1 = 11000x5 0x5
La solución propuesta es:
x3 = 110
x4 = 10
158
x5 = 101
C.4.
El problema 10-5-1
Sistema de 10 “word equations” con 5 variables de longitudes comprendidas entre 0 y
1, siendo al menos una de ellas una cadena de longitud 1.
x3 x6 1x3 x6 1x3 1 = x4 x6 1x6 1x5
x3 1x6 1x4 0x4 0 = x3 1x6 1x6 0x3 0
x3 11x4 x6 1x3 01 = 5x5 x3 1x3 x6 0x3 1
x4 x6 0x4 10x3 00 = x7 x6 1x6 00x6 0
x3 101x6 0 = x5 x4 0x1 0
x3 0x6 11x7 0 = x3 x6 0x3 1100
x4 0x3 0x6 0 = x7 x3 x4 0x3 0
x4 x6 100x3 1 = x5 x3 00x4 1
x7 x6 0x4 x6 0x3 0 = x3 x4 000x3 0
x7 x3 1x3 x6 10x3 0 = x7 x3 x6 1x3 x4 x6 10x3 0
La solución propuesta es:
C.5.
x3 = Λ
x4 = Λ
x5 = 1
x6 = Λ
x7 = 0
El problema 10-5-2
Sistema de 10 “word equations” con 5 variables de longitudes comprendidas entre 0 y
2, siendo al menos una de ellas una cadena de longitud 2.
x4 1x4 = x6 1
x4 0x4 010x4 1x4 1x4 = x5 x4 0x4 1011
0x5 0 = x5 0x4 0x6
1x6 = x4 1
x4 x6 111 = 111x4
x7 1x4 = x5 1
x6 11x5 = 110
x4 = Λ
x6 0x5 x5 0x4 x6 00 = 0x7 x7 x7 x5 x5
x3 x7 = x6 100
159
La solución propuesta es:
C.6.
x3 = 10
x4 = Λ
x5 = 0
x6 = Λ
x7 = 0
El problema 10-8-3
Sistema de 10 “word equations” con 8 variables de longitudes comprendidas entre 0 y
3, siendo al menos una de ellas una cadena de longitud 3.
1x8 0x7 = 1x9 01
0x3 = 0x4 x8 1
x5 011 = x5 0x7 x5 x7
x9 0x3 000 = 0x3 000
0x4 0x4 0x9 0x5 0 = x9 0x5 x10 x8 x10 00
00100x9 0 = 00x10 00
x6 0 = x4 x3 0
x4 x10 000 = x3 000x5 0
x10 x8 0x9 00x5 0 = x4 00x9 0x9 00
0x3 x5 0x5 1 = 0x8 x7 x4 01
C.7.
El problema 10-8-5
Sistema de 10 “word equations” con 8 variables de longitudes comprendidas entre 0 y
5, siendo al menos una de ellas una cadena de longitud 5.
0011x3 111 = 0x3 0x8 x8 1
x4 x9 x3 1 = x4 0x4 11
01x7 x5 0111 = x5 x3 x10 x3 00011x3 x1
0x9 1x5 001 = x3 00x8 x3 x4 000x4 1
x8 1x5 1x6 11 = x8 1x3 01x6 x8
0x5 1x5 x5 x5 = x5 01x5 00
000x9 x3 0 = x5 0x5 0x4 10
0x3 x4 x10 x5 = x3 x9 x5 x5 010
1x9 1x7 10 = 101x10 x9 0
x5 1x5 0 = 0x3 10x5
160
C.8.
El problema 12-6-4
Sistema de 12 “word equations” con 6 variables de longitudes comprendidas entre 0 y
4, siendo al menos una de ellas una cadena de longitud 4.
x5 01x8 = x5 0x7 10
111 = 1x7 11
x4 1x8 101x8 1x8 = 1010x4 x7 10x4 1x8
x7 11x5 00 = 11x5 00
x8 11x3 10 = 0x4 11x3 x4 10
101x8 0110x4 11 = 1010011011
x8 0x4 1 = 0x8 1
1x6 x7 01x4 100 = 1x6 x4 x7 0x6
x7 x8 11x7 0x3 1 = 0110x3 1
x4 x6 1 = x6 1
0x4 x7 10x4 x7 1001 = 01x4 01001
000x3 = 0x8 0x3
C.9.
El problema 15-12-4
Sistema de 15 “word equations” con 12 variables de longitudes comprendidas entre 0
y 4, siendo al menos una de ellas una cadena de longitud 4.
11x5 x13 x3 x9 x12 x3 x10 x13 = 1x6 11x5 0x9 x12 1x9 0
0x7 0000 = 00x3 x9 000
1x13 0x3 x12 0x12 x13 x5 11 = x10 1x5 x6 x12 x8 011x9 0x12 x3 x5 x12
x4 x4 0 = x5 0x6 100x5 100
x6 11x4 11x3 x4 x3 00x12 = x6 1x9 x12 x4 1x12 0x12 0x5 x7 x8
x6 x10 1x13 0x4 x7 0x8 = x6 x10 x12 x3 x10 x14 10x3 x14 0x4 x5 0x9 0x3 0x5 x7 1
x9 10x12 x5 x13 x4 = x7 x13 1x12 0x4
x6 x5 0x4 x14 0x3 x12 = x3 x8 001
0x4 x7 1x9 1x6 0x5 0 = x14 00x6 10x3 1x1 41x9 00
x5 0x3 01x3 0 = 0x3 x4
x4 1x6 1x3 x14 01x6 x7 1x10 x12 = x4 1x7 1x14 0x12 x12 1
161
x5 0x11 00 = 011x7 x14 1x6 0x5 00
x10 011 = 0x12 x5 1
1x9 1x13 0x14 11 = 1x10 x141x3 x13 x3 0x14 1x9 1
x12 x12 x4 x5 0x3 x14 1x7 x12 x4 = x12 10x12 00x9 1x3 1x4
C.10.
El problema 15-25-5
Sistema de 15 “word equations” con 25 variables de longitudes comprendidas entre 0
y 5, siendo al menos una de ellas una cadena de longitud 5.
x17 01000100x24 1x18 = x3 x20 11x16 0x20 1x3 00x20 0100111
x8 10x26 01x20 000x10 = x3 x10 01x18 01x12 x19 001x20 10x23 0
1x4 x4 1x25 1x14 = x11 x3 11x18 1x19 1x12 111x10 0x19
x8 x12 1x10 = 0x20 1
x14 x16 0x25 11x19 011x3 = x19 x20 1x3 0x10 011011
x11 11x10 x19 1x10 0x15 x5 = x4 x4 00x12 x16 x20 01x20 1x25 110
x5 1x20 1 = x25 1x4 011x12 x25 x10
x8 x9 = x25 01x18 x25 0x18 1
x15 00001x18 0x10 00x3 x12 11x21 = 0x12 0x10 1000x19 0x18 1x13 11x12 x20 1x16 1
x11 x11 x16 1x16 10 = 1x22
x12 1x16 x19 0x20 01x10 0x20 1x18 101x12 1x20 0x19 1x3 = x14 010x25 110110x16 1
010x18 00x19 1x16 0x11 x6 = x3 x20 0x16 x20 1x27 0x12 1x10 01x18 010x10 x12 1
1x3 x19 1010011x12 x25 0x16 100x10 = x4 x3 x12 0x3 x10 10x18 0x16 x20 1101x19 00x25
x20 01x7 x12 1 = x6 1x17
x19 10x25 010x25 10x8 = 100x20 10100
162
C.11.
El problema 15-7-5
Sistema de 15 “word equations” con 7 variables de longitudes comprendidas entre 0 y
5, siendo al menos una de ellas una cadena de longitud 5.
10x9 00x4 0000 = 10x7 x4 000000
x8 x3 1 = 10x3 x9
x4 0x7 = 001
x7 x5 0000x4 x9 = x7 x5 000001
00010 = 000x9 x4
11x6 0x4 x4 0 = x9 1x6 0x4 0x4
x8 10x8 0x5 = 1010x7 00x5
x9 = 1
x4 00x8 x4 11 = 0001x6
0x7 x4 1x4 = 0x9 0x7 0
0x7 11x4 x7 = 01x9 101
x7 0x4 x9 0x8 0 = x9 00x7 01x4 0
x5 x8 0001 = x5 100001
x6 0 = 001x7 x4
0x5 x5 1 = 0x5 x5 x7
163
C.12.
El problema 25-23-4
Sistema de 25 “word equations” con 23 variables de longitudes comprendidas entre 0
y 4, siendo al menos una de ellas una cadena de longitud 4.
x9 x13 x7 x14 x9 x3 0x7 x5 x13 x19 x25 = x9 x7 x6 x23 0x9 x5 x25 x5 x9 x4 1x9
x22 x7 x7 x3 x13 x3 x9 x6 x9 x13 x4 x22 = x22 x4 x4 x16 x21 x3 x9 x13 x4 x18 x6 0
0x7 0x14 0x21 x4 0 = x24 010x15 x10 x25 x7 x13 x5 x22
x5 x10 10x4 x7 x22 x12 x3 = x13 01x4 x20 x22 x9
x22 1x24 x25 x3 x16 x3 x9 = x9 x14 0x3 x25 x3 x3
1x4 x3 = x4 x4 x3
x21 x9 x4 x14 x3 x7 x4 x3 x4 x7 x9 = x10 x23 1x18 x12 x3 x4 x19 1x3 x6 x22
x6 0x9 = 1x15 x22 x9
x18 x22 x18 x13 = x16 x10 x25 1
1x10 x18 x3 0x6 x24 0x10 x19 x21 x3 = x5 1x3 x3 x4 x4 x9 x13 x22 0
1x9 1 = x23 1x17
x7 1x22 x14 x25 1x16 x15 x3 x15 = x8 x7 x18 x10 x15 x3 x4 0x9 x4
x7 x24 00x5 x4 x22 x6 x9 x8 x14 x16 = x7 x10 0x22 x13 0x8 x7 x4 x10 0x5 x13 x9 0
x3 x3 x6 x4 0x14 x5 x15 = x3 x11 x4 0x6
x6 x7 x4 = x4 x4 x4 x21
x6 x9 x9 x18 x4 x7 x6 x4 1x4 x5 x14 x4 x25 = x14 x4 x3 0x5 1x5 x7 x4 x5 x7 x5 x21 x13 x4 x4 x21 0
x15 x4 x4 = x5 x10 x14 x5 x7 x13
x5 1x5 x9 x21 = x15 x25 x7
x21 = x15
x21 x3 x6 x25 x7 = x7 x18 x9 x13 x10 x7 x17
x7 x4 x9 x4 10x19 x15 x16 x15 x4 x18 = x13 x4 0x4 x13 0x4 00x7 x5 x13 0
x7 x5 0x14 x4 x6 = x24 x5 x24 1x22 x6 x13 x4
0x7 x9 x7 x18 x4 0x6 x5 x7 x5 x7 x5 x7 x6 = 0x7 x9 x21 x25 1x18 x15 x5 x6 x12 x4 x14
x4 x3 x5 x13 x10 x12 x12 x3 x3 0x7 = x7 x9 x10 0x7 x4 x4 x6 x16 x25 x9 x22 x14
x21 x22 x15 = x4 x9 x13
164
C.13.
El problema 25-8-3
Sistema de 25 “word equations” con 8 variables de longitudes comprendidas entre 0 y
3, siendo al menos una de ellas una cadena de longitud 3.
x7 0 = x6 x8 x5 00
1x3 x3 x10 x7 x4 x3 x9 x9 = 1x8 x3 1x3 x3 x4 0x3 x3
x9 = 0
11x4 x8 x3 x6 1x8 x5 0 = x6 x10 x10 x3 x10 x10 x3 x3 x10 0x6 0
x3 x5 x9 1x3 x3 0 = x3 x8 1x7 x8
x10 1x10 x3 x10 x10 x3 x7 x5 x8 x3 x10 x8 = x10 11x4 x9 0x3 x3 x8 1x3
1x4 01x8 x9 x3 x3 = 1x4 01x3 x8 x3 x9
1x6 1x3 x3 x10 x8 = 1x10 x7 1x3
x5 1x8 = x6 1x3
x6 x8 x6 0x4 x4 x10 x10 x4 1 = 0x3 x3 1x6 1x4 x10 1x5 x4 x10
x7 x6 10x5 1x3 1x6 x8 x3 0x3 x9 1x9 = x9 x9 x10 x9 101x3 x3 x5 x7 0x10 x9
x9 x10 x9 x3 1x8 = x3 1x3 x8 1x3
11x4 0x7 x3 0x10 0 = x10 1x4 0x3 0x7 x10 x3
11x3 x8 x10 = 11x8 x3 1
1x3 01x3 = x10 x8 x3 x10 x5 0
0x8 0x10 x4 x10 x7 x10 x9 1 = 0x9 x3 1x4 x10 x7 1x3 1
x8 x3 11x3 11x10 x10 = x8 x3 x10 x5 x10 x4 1x5 x6 1
x1 0x8 x7 = x10 x3 x7
x10 x10 x9 0x3 = 1x5 x6 x10 0x7
x9 = 0
x4 1x7 1 = x3 x10 11x7 1
x10 x8 x3 x5 0x8 x3 x3 x4 = x10 x7 0x3 x9 x9 x3 x10 x10
x7 1x4 1 = x3 x3 1x4 x10
x8 x7 0x5 0 = 0x5 x6 x9 x3 x6 x7
101x5 x7 10x9 x3 = 1x3 1x7 1x5 0x5 0x8
165
C.14.
El problema 25-8-5
Sistema de 25 “word equations” con 8 variables de longitudes comprendidas entre 0 y
5, siendo al menos una de ellas una cadena de longitud 5.
1x3 x5 0 = x6 x9 0x10
x4 01000x4 000x4 01110 = 0x10 00x9 11x4 0
1x8 = x6 0
11x8 1x3 0 = 1x6 01x3 0
1x9 0x4 100 = 1000x4 1x4 0x10
x8 110x3 11 = x8 x4 110x3 11
x7 111x6 = x7 1111x4 110
00x10 x4 001 = 00100001
0101x4 x6 01x4 1x4 1 = 0101x6 0111
x10 00 = 10000
001x4 0 = 0010
0x6 0x6 1000 = 0x6 01x7 00
1x4 10x10 x10 111 = x7 x4 0x10 111
x4 1 = 1
x9 000 = 000x4 1x4 000
1011110x5 1 = 101x6 x5 1
110x4 101001 = x7 1001
11011111x6 = 110x4 11111111x4 0
x4 1x7 00000 = x6 10x4 00000
111111x4 10 = 1111x6
x10 x4 001x3 011 = x4 10x4 x9 x3 011
00001x3 1x4 11 = 00001x3 111
x4 000 = 000
x4 0x10 00x9 x9 = 0x10 000x4 001x9
0001x4 00 = 000x10
166
C.15.
El problema 5-15-3
Sistema de 5 “word equations” con 15 variables de longitudes comprendidas entre 0 y
3, siendo al menos una de ellas una cadena de longitud 3.
x1 210110x15 0x16 0 = 001011001x8
x6 1110x9 1000100 = x5 1x3 1x5 1x11 00x9 0100x11
x4 x4 0x9 1x13 = x9 x14 1x9 01x7
x3 01001000x11 1001x6 = x4 01x9 0010001x11 0010
x10 x10 x11 0x9 0100 = x4 x17 x10 001x11 00x9
C.16.
El problema 5-3-2
Sistema de 5 “word equations” con 3 variables de longitudes comprendidas entre 0 y
2, siendo al menos una de ellas una cadena de longitud 2.
10 = x3 x4
x5 1 = 0x3 1x3
x3 011001 = 011001x3
x3 0 = x5
x5 = x3 0
La solución propuesta es:
x3 = Λ
x4 = 10
x5 = 0
167
Apéndice D
A.E. Sólo Mutación
// //////////////////////////////////////////////////////////// //
// /
/ //
// / Un Algoritmo Evolutivo para Resolver el problema l_SWES
/ //
// /
/ //
// /
(SÓLO MUTACIÓN)
/ //
// /
/ //
// //////////////////////////////////////////////////////////// //
#include<fstream.h> #include<stdlib.h> #include<vector>
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
fstream fsol;
void Pedir_Datos(string &problema, int &lMaxVsol,
int &NMaxIter, int &Metodo);
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist);
168
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist);
void Genera_PobIni(vector<vector<int> > &PobIni,
vector<int> &lReVsol,
vector<vector<int> > &AuxPob, const int &m_var,
const int &lMaxVsol, const int &Metodo);
void reordeno(const int &l, vector<int> &v, int &lReVsol);
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist, const vector<int> &lReVsol,
const vector<vector<int> > &PobIni, vector<int> &AuxIzq,
vector<int> &AuxDer, int &fitness, int &haysol);
void sustituyo(const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni);
void Mutacion(const int &m_var, const int &lMaxVsol,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol);
int main()
{
string problema;
int lMaxVsol, NMaxIter, Metodo, Npruebas;
int n_ec, m_var;
int fitness, AuxFit, haysol;
int i, j, k, iter, npru;
vector<int> l_ec;
169
vector<vector<int> > Sist;
vector<vector<int> > PobIni;
vector<int> lReVsol;
vector<vector<int> > AuxPob; //vector auxiliar
vector<int>
AuxlRsol; //vector auxiliar
vector<int> AuxIzq; //Sólo se utiliza en la función evalua
vector<int> AuxDer; //Sólo se utiliaza en la función evalua
randomize();
Pedir_Datos(problema, lMaxVsol, NMaxIter,
Metodo);
Lee_Prob(problema, n_ec, m_var, l_ec, Sist);
PobIni.resize(m_var);
lReVsol.resize(m_var);
AuxPob.resize(m_var);
//vector auxiliar
AuxlRsol.resize(m_var); //vector auxiliar
Simplifica(n_ec, l_ec, Sist);
Genera_PobIni(PobIni, lReVsol, AuxPob, m_var, lMaxVsol, Metodo);
haysol= 0;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer, fitness, haysol);
fsol<<"La poblacion inicial generada es: "<<endl;
for(i= 0; i<m_var; i++) { for(j= 0;j< lMaxVsol; j++)
fsol<<PobIni[i][j]; fsol<<" long="<<lReVsol[i]<<"
fsol<<"
"; fsol<<"Con fitness "<<fitness<<endl;
170
"; }//fin
for(i= 0; i< m_var; i++)
{AuxlRsol[i]= lReVsol[i];
for(j= 0;j< lMaxVsol; j++) AuxPob[i][j]= PobIni[i][j];
}//fin for(i)
for(iter= 0; iter< NMaxIter && haysol == 0; iter++)
{
Mutacion(m_var, lMaxVsol, AuxPob, AuxlRsol);
Evalua(n_ec, l_ec, Sist, AuxlRsol, AuxPob, AuxIzq, AuxDer, AuxFit,haysol);
if(AuxFit < fitness)
{
for(i= 0; i< m_var; i++)
{lReVsol[i] = AuxlRsol[i];
for(j= 0;j< lMaxVsol; j++) PobIni[i][j] = AuxPob[i][j];
}//fin for(i)
fitness = AuxFit;
}
else
{
for(i= 0; i< m_var; i++)
{AuxlRsol[i] = lReVsol[i];
for(j= 0;j< lMaxVsol; j++) AuxPob[i][j] = PobIni[i][j];
}//fin for(i)
AuxFit = fitness;
}
}//fin for(iter)
if(haysol == 1)
{
fsol<<"El programa ha encontrado solucion con longitud máxima "<<endl;
fsol<<"de las variables igual a "<<lMaxVsol<<" ."<<endl;
171
fsol<<"Para generar la poblacion inicial se uso el metodo_"<<Metodo<<endl;
fsol<<"La solucion propuesta tras "<<iter<<" iteraciones es: "<<endl;
for(i= 0; i< m_var; i++)
{
fsol<<"x"<<i<<" = ";
if(lReVsol[i] == 0) fsol<<"la palabra vacia."<<endl;
else
{for(j= 0;j< lReVsol[i]; j++) fsol<<PobIni[i][j]<<" ";
fsol<<endl;}//fin else
}//fin for(i)
}//fin if(haysol==1)
if(haysol == 0)
{
fsol<<"El programa NO ha encontrado solucion."<<endl;
fsol<<"Los datos propuestos fueron: "<<endl;
fsol<<"Longitud máxima de las variables igual a "<<lMaxVsol<<" ."<<endl;
fsol<<"Para generar la población inicial se uso el metodo_"<<Metodo<<endl;
fsol<<"La candidata a solución generada tras "<<iter<<" iteraciones es:"<<endl;
for(i= 0; i< m_var; i++)
{for(j= 0;j< lMaxVsol; j++) fsol<<PobIni[i][j];
fsol<<" ";}//fin for(i)
fsol<<"
";
fsol<<"Con fitness "<<fitness<<endl;
}//fin if(haysol==0)
}//fin programa
void Pedir_Datos(string &problema, int &lMaxVsol, int &NMaxIter,
int &Metodo)
{
cout<<"Indique el nombre del fichero que contiene el problema: "<<endl;
cin>> problema;
172
do{
cout<<"Escriba la longitud maxima(>0) de las candidatas a solucion:"<<endl;
cin>> lMaxVsol;
}while(lMaxVsol<=0);
do{
cout<<"Numero maximo de iteraciones que desea realizar : "<<endl;
cin>>NMaxIter;
}while(NMaxIter<0);
cout<<"Eliga el metodo que desea utilizar para generar la poblacion"<<endl;
cout<<"inicial."<<endl<<endl;
do{
cout<<"Pulse 1 si quiere fijar la longitud real de la variable y "<<endl;
cout<<"luego generar la variable con simbolos del alfabeto {0,1}."<<endl;
cout<<endl;
cout<<"Pulse 2 si quiere generar la variable en {0,1,2} y luego "<<endl;
cout<<"calcular la longitud real de la variable."<<endl<<endl;
cout<<"Pulse 3 si desea recibir mas informacion sobre los metodos"<<endl;
cout<<"usados en la generacion de la poblacion inicial."<<endl<<endl;
cin>> Metodo;
if(Metodo == 3)
{
cout<<"El metodo-1 elige la longitud real de la variable que va a"<<endl;
cout<<"generar entre cero (que corresponde a la palabra vacia) y "<<endl;
cout<<"la longitud maxima propuesta. Una vez fijada la longitud, "<<endl;
cout<<"genera la variable como una cadena de ceros y unos. "<<endl<<endl;
cout<<"El metodo-2 genera la variable como una cadena de simbolos"<<endl;
cout<<"pertenecientes al conjunto {0,1,2} de longitud la longitud"<<endl;
cout<<"maxima propuesta. Despues reordena esta cadena, colocando"<<endl;
cout<<"todos los doses al final de la misma (pues representan el "<<endl;
cout<<"simbolo vacio). Finalmente, calcula el numero de ceros y "<<endl;
cout<<"unos que aparecen en la cadena y que corresponde a la "<<endl;
cout<<"longitud real de la variable generada."<<endl<<endl;
173
cout<<"Que metodo desea utilizar?"<<endl;
cin>> Metodo;
}//fin if
}while(Metodo<1 || Metodo>2);
}//fin Pedir_Datos
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist)
{
ifstream fprob(problema.c_str(),ios::in);
fprob>>n_ec;
fprob>>m_var;
l_ec.resize(2*n_ec);
Sist.resize(2*n_ec);
for(int i= 0; i< 2*n_ec; i++){
fprob>>l_ec[i]; Sist[i].resize(l_ec[i]);
for(int j= 0; j< l_ec[i]; j++) fprob>>Sist[i][j];
}//fin for(i)
}//fin Lee_Prob
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist)
{
int coincidencia, columnas, minimo;
for(int i= 0; i< 2*n_ec - 1; i+= 2)
{
coincidencia= 0; columnas= 0; minimo= min (l_ec[i],l_ec[i+1]);
while(Sist[i][columnas]== Sist[i+1][columnas] && columnas< minimo)
{coincidencia= coincidencia +1; columnas= columnas +1; }//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i]-coincidencia;
for(int j= 0; j< l_ec[i]; j++)
174
Sist[i][j]= Sist[i][j+coincidencia];
Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia;
for(int j= 0; j< l_ec[i+1]; j++)
Sist[i+1][j]= Sist[i+1][j+coincidencia];
Sist[i+1].resize(l_ec[i+1]);
}//fin if
coincidencia= 0; columnas= 1; minimo= min (l_ec[i],l_ec[i+1]);
while(columnas<= minimo &&
Sist[i][l_ec[i]-columnas] == Sist[i+1][l_ec[i+1]-columnas])
{coincidencia= coincidencia +1;
columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{l_ec[i]= l_ec[i] -coincidencia; Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia; Sist[i+1].resize(l_ec[i+1]);
}//fin if
}//fin for(i)
}//fin Simplifica
void Genera_PobIni(vector<vector<int> > &PobIni,
vector<int> &lReVsol, vector<vector<int> > &AuxPob,
const int &m_var, const int &lMaxVsol, const int &Metodo)
{
int longitud;
for(int i= 0; i< m_var; i++)
{
PobIni[i].resize(lMaxVsol);
AuxPob[i].resize(lMaxVsol);
if(Metodo == 1)
{longitud= lMaxVsol +1;
lReVsol[i]= rand() % longitud;
175
for(int j= 0; j< lReVsol[i]; j++) PobIni[i][j]= rand() % 2;
for(int j= lReVsol[i]; j< lMaxVsol; j++) PobIni[i][j]= 2;
}//fin if(Metodo==1)
if(Metodo == 2)
{lReVsol[i]= lMaxVsol;
for(int j= 0; j< lMaxVsol; j++) PobIni[i][j] = rand() % 3;
reordeno(lMaxVsol, PobIni[i], lReVsol[i]);
}//fin if(Metodo==2)
}//fin for(i)
}//fin Genera_PobIni
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist, const vector<int> &lReVsol,
const vector<vector<int> > &PobIni, vector<int> &AuxIzq,
vector<int> &AuxDer, int &fitness, int &haysol)
{
int lAuxIzq, lAuxDer;
int coincidencia, minimo;
coincidencia = 0;
for(int i= 0; i< n_ec; i++)
{lAuxIzq= l_ec[2*i];
lAuxDer= l_ec[2*i+1];
AuxIzq.resize(lAuxIzq);
AuxDer.resize(lAuxDer);
sustituyo(Sist[2*i], lAuxIzq, AuxIzq, lReVsol, PobIni);
sustituyo(Sist[2*i+1], lAuxDer, AuxDer, lReVsol, PobIni);
minimo = min(lAuxIzq,lAuxDer);
for(int j= 0;j< minimo; j++)
if(AuxIzq[j] != AuxDer[j]) coincidencia= coincidencia+1;
coincidencia= coincidencia + abs(lAuxIzq-lAuxDer);
}//fin for(i)
fitness= coincidencia;
176
if(fitness == 0) haysol= 1;
}//fin Evaluacion
void sustituyo(const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni)
{
int auxiliar, variable;
int j, l;
auxiliar = 0;
for(j= 0;j< lAux; j++)
if(Sist[j+auxiliar]==0 || Sist[j+auxiliar]==1) Aux[j] = Sist[j+auxiliar];
else
{
variable = Sist[j+auxiliar] - 3;
if(lVsol[variable]==0)
{lAux=lAux-1;
Aux.resize(lAux);
auxiliar=auxiliar+1;
j=j-1;}
else
{lAux=lAux+lVsol[variable]-1;
Aux.resize(lAux);
for(l= 0; l< lVsol[variable]; l++) Aux[j+l] = PobIni[variable][l];
auxiliar = auxiliar - lVsol[variable] + 1;
j=j+lVsol[variable]-1;}
}//fin else
}//fin sustituyo
void Mutacion(const int &m_var, const int &lMaxVsol,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol)
{
177
int muto;
for(int i= 0; i< m_var; i++)
{
for(int j= 0; j< lMaxVsol; j++)
{muto= rand() % lMaxVsol;
if(muto == 0) AuxPob[i][j]= rand() % 3;}//fin for (j)
AuxlRsol[i]= lMaxVsol;
reordeno(lMaxVsol, AuxPob[i],AuxlRsol[i]);
}//fin for(i)
}//fin Mutación
void reordeno(const int &l, vector<int> &v,int &lReVsol)
{
// Reordeno la variable y calculo la longitud real de la misma
int columna;
for(int j= 0; j< l-1; j++)
if(v[j] == 2)
{
columna= j+1;
while(v[columna] == 2 && columna< l-1) columna = columna +1;
if(v[columna]!= 2)
{v[j]= v[columna];
v[columna]= 2; }//fin if
}//if(v[j] == 2)
for(int j= 0; j< l; j++)
if(v[j] == 2) lReVsol= lReVsol -1;
}//fin reordeno
178
Apéndice E
A.E. Búsqueda Local
// //////////////////////////////////////////////////////////// //
// /
/ //
// / Un Algoritmo Evolutivo para Resolver el problema l_SWES
/ //
// /
/ //
// /
(BÚSQUEDA LOCAL)
// /
/ //
/ //
// //////////////////////////////////////////////////////////// //
#include<fstream.h> #include<stdlib.h> #include<vector.h>
#include<stdio.h> #include<math.h> #include<string.h>
using namespace std;
fstream fsol("solucion.dat",ios::out);
void Pedir_Datos(string &problema, int &TamPob, int &lMaxVsol,
int &NMaxIter, int &ProMut, int &Metodo, int &Busqueda);
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist);
179
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist);
void Genera_PobIni(vector<vector<vector<int> > > &PobIni,
vector<vector<int> > &lReVsol,
vector<vector<vector<int> > > &AuxPob,
vector<vector<int> > &AuxlRsol,
const int &TamPob, const int &m_var,
const int &lMaxVsol, const int &Metodo);
void reordeno(const int &l, vector<int> &v, int &lReVsol);
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist, const vector<int> &lReVsol,
const vector<vector<int> > &PobIni, vector<int> &AuxIzq,
vector<int> &AuxDer, int &fitness, int &haysol, int &prueba);
void sustituyo( const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni);
void Mejor_Sol(int &MejorFit, vector<int> &fitness,
const int &TamPob, const int &m_var,
vector<vector<int> > &AuxPob, const int &lMaxVsol,
vector<vector<vector<int> > > &PobIni,
vector<int> &AuxlRsol, vector<vector<int> > &lReVsol);
void Ruleta(int &Sumfit, const int &TamPob, vector<int> &fitness);
void eligo_padre(int &TamPob, int &Padre, vector<int> &fitness);
void Cruce(const int &m_var, const int &lMaxVsol,
180
vector<vector<int> > &AuxPob,
vector<int> &lReVsolIzq, vector<int> &lReVsolDer,
vector<int> &AuxlRsol, vector<vector<int> > &PobIniIzq,
vector<vector<int> > &PobIniDer);
void Mutacion(const int &m_var, const int &lMaxVsol, int &muto,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol);
void Busqueda_loc_A(const int &m_var, vector<int> &lReVsol,
const int &lMaxVsol, const int &n_ec,
const vector<int> &l_ec,
const vector<vector<int> > &Sist, vector<int> &AuxIzq,
vector<int> &AuxDer, vector<vector<int> > &PobIni,
int &fitness, int &haysol, int &cont_uno, int &cont_dos,
int &prueba);
void Busqueda_loc_B(const int &m_var, vector<int> &lReVsol,
const int &lMaxVsol, const int &n_ec,
const vector<int> &l_ec,
const vector<vector<int> > &Sist, vector<int> &AuxIzq,
vector<int> &AuxDer, vector<vector<int> > &PobIni,
int &fitness, int &haysol, int &cont_uno,int &prueba);
int main(void)
{
string problema;
int TamPob, lMaxVsol, NMaxIter, ProMut, Metodo, Busqueda;
int n_ec, m_var;
int haysol, muto;
int MejorFit, Sumfit, PadreIzq, PadreDer;
int i, j, k, iter;
int cont_uno, cont_dos;
181
int prueba; prueba=0;
vector<int> l_ec;
vector<vector<int> > Sist;
vector<vector<vector<int> > > PobIni;
vector<vector<int> > lReVsol;
vector<vector<vector<int> > > AuxPob; //vector auxiliar
vector<vector<int> > AuxlRsol; //vector auxiliar
vector<int> fitness;
vector<int> AuxIzq; //Sólo se utiliza en la función evalua
vector<int> AuxDer; //Sólo se utiliaza en la función evalua
cont_uno = 0; cont_dos = 0;
randomize();
Pedir_Datos(problema, TamPob, lMaxVsol, NMaxIter, ProMut, Metodo, Busqueda);
fsol<<problema<<" "<<TamPob<<" "<<lMaxVsol<<" "<<NMaxIter<<"
"<<ProMut<<" "; fsol<<Metodo<<" "<<Busqueda<<endl;
Lee_Prob(problema, n_ec, m_var, l_ec, Sist);
Simplifica(n_ec, l_ec, Sist);
Genera_PobIni(PobIni,lReVsol,AuxPob,AuxlRsol,TamPob,m_var,lMaxVsol,Metodo);
fitness.resize(TamPob);
haysol= 0;
for(k= 0; k<TamPob; k++)
Evalua(n_ec, l_ec, Sist, lReVsol[k], PobIni[k],
AuxIzq, AuxDer, fitness[k], haysol,prueba);
182
fsol<<"La poblacion inicial generada es: "<<endl; for(k= 0;
k<TamPob; k++) { for(i= 0; i< m_var; i++) { for(j= 0;j< lMaxVsol;
j++) fsol<<PobIni[k][i][j]; fsol<<" "; }//fin for(i) fsol<<"
";
fsol<<"Con fitness "<<fitness[k]<<endl; }//fin for(k)
if(Busqueda != 0)
for(k= 0; k<TamPob; k++)
if(Busqueda == 1)
Busqueda_loc_A(m_var, lReVsol[k], lMaxVsol, n_ec, l_ec, Sist,
AuxIzq, AuxDer, PobIni[k], fitness[k],
haysol, cont_uno, cont_dos, prueba);
else
Busqueda_loc_B(m_var, lReVsol[k], lMaxVsol, n_ec, l_ec, Sist,
AuxIzq, AuxDer, PobIni[k], fitness[k],
haysol, cont_uno,prueba);
fsol<<"La poblacion inicial tras la busqueda local es: "<<endl;
for(k= 0; k< TamPob; k++) { for(i= 0; i< m_var; i++) { for(j= 0;j<
lMaxVsol; j++) fsol<<PobIni[k][i][j]; fsol<<" "; }//fin for(i)
fsol<<"
"; fsol<<"Con fitness "<<fitness[k]<<endl; }//fin for(k)
for(iter= 0; iter<NMaxIter && haysol == 0; iter++)
{
Mejor_Sol(MejorFit, fitness, TamPob, m_var, AuxPob[0], lMaxVsol,
PobIni, AuxlRsol[0], lReVsol);
Ruleta(Sumfit, TamPob, fitness);
for(k= 1; k< TamPob; k++)
{
// * Selección *
183
PadreIzq = rand() % Sumfit;
eligo_padre(TamPob, PadreIzq, fitness);
PadreDer = rand() % Sumfit;
eligo_padre(TamPob, PadreDer, fitness);
// * Cruce *
Cruce(m_var, lMaxVsol, AuxPob[k], lReVsol[PadreIzq], lReVsol[PadreDer],
AuxlRsol[k], PobIni[PadreIzq], PobIni[PadreDer]);
// * Mutación *
if(ProMut!=0)
{
muto = rand() %100;
if(muto< ProMut)
// * Muto la solusión k_esima *
Mutacion(m_var, lMaxVsol, muto, AuxPob[k], AuxlRsol[k]);
}//fin if(ProMut!=0)
}//fin for(k)
for(k= 0; k< TamPob; k++)
for(i= 0; i< m_var; i++)
{
lReVsol[k][i]= AuxlRsol[k][i];
for(j= 0;j< lMaxVsol; j++) PobIni[k][i][j]= AuxPob[k][i][j];
}//fin for(i)
fitness[0]= MejorFit;
for(k= 1; k<TamPob; k++)
Evalua(n_ec, l_ec, Sist, lReVsol[k],PobIni[k],AuxIzq, AuxDer,
fitness[k] , haysol, prueba);
if(Busqueda != 0)
for(k= 0; k<TamPob; k++)
if(Busqueda == 1)
184
Busqueda_loc_A(m_var, lReVsol[k], lMaxVsol, n_ec, l_ec, Sist,
AuxIzq, AuxDer, PobIni[k], fitness[k],
haysol, cont_uno, cont_dos, prueba);
else
Busqueda_loc_B(m_var, lReVsol[k], lMaxVsol, n_ec, l_ec, Sist,
AuxIzq, AuxDer, PobIni[k], fitness[k],
haysol, cont_uno,prueba);
}//fin for(iter)
if(haysol == 1)
{
fsol<<"El programa ha encontrado solucion con una poblacion de "<<endl;
fsol<<"tama~
no "<<TamPob<<" y longitud máxima de las variables "<<endl;
fsol<<"igual a "<<lMaxVsol<<" ."<<endl;
fsol<<"La probabilidad de mutacion utiliza fue "<<ProMut<<" ."<<endl;
fsol<<"Para generar la poblacion inicial se uso el metodo_"<<Metodo<<endl;
fsol<<"La solucion propuesta tras "<<iter<<" iteraciones es: "<<endl;
k=0;
while(fitness[k] !=0) k= k + 1;
for(i= 0; i< m_var; i++)
{
fsol<<"x"<<i<<" = ";
if(lReVsol[k][i] == 0) fsol<<"la palabra vacia."<<endl;
else
{
for(j= 0;j< lReVsol[k][i]; j++) fsol<<PobIni[k][i][j]<<" ";
fsol<<endl;
}//fin else
}//fin for(i)
}//fin if(haysol==1)
if(haysol == 0)
185
{
fsol<<"El programa NO ha encontrado solucion."<<endl;
fsol<<"Los datos propuestos fueron: "<<endl;
fsol<<"Tama~
no de poblacion igual a "<<TamPob<<" ."<<endl;
fsol<<"Longitud máxima de las variables igual a "<<lMaxVsol<<" ."<<endl;
fsol<<"La probabilidad de mutacion utilizada fue "<<ProMut<<" ."<<endl;
fsol<<"Para generar la población inicial se uso el metodo_"<<Metodo<<endl;
}//fin if(haysol==0)
fsol<<"La poblacion generada tras "<<iter<<" iteraciones es: "<<endl;
for(k= 0; k< TamPob; k++)
{
for(i= 0; i< m_var; i++)
{
for(j= 0;j< lMaxVsol; j++) fsol<<PobIni[k][i][j];
fsol<<" ";
}//fin for(i)
fsol<<"
";
fsol<<"Con fitness "<<fitness[k]<<endl;
}//fin for(k)
if(Busqueda != 0)
{
float media;
media = iter +1;
media = cont_uno/media;
fsol<<"contador_uno = "<<cont_uno<<"
en media = "<<media<<endl;
if(Busqueda == 1)
{
media = iter +1;
media = cont_dos/media;
fsol<<"contador_dos = "<<cont_dos<<"
}//fin if
186
en media = "<<media<<endl;
fsol<<"Busqueda= "<<Busqueda<<endl;
}//fin if(Busqueda != 0)
fsol<<"El numero de evaluaciones es "<<prueba<<endl;
}//fin programa
void Pedir_Datos(string &problema, int &TamPob, int &lMaxVsol,
int &NMaxIter, int &ProMut, int &Metodo, int &Busqueda)
{
cout<<"Indique el nombre del fichero que contiene el problema: "<<endl;
cin>> problema;
do{
cout<<"Escriba el tamanio (>0) de la poblacion inicial:"<<endl;
cin>> TamPob;
}while(TamPob<=0);
do{
cout<<"Escriba la longitud maxima(>0) de las candidatas a solucion:"<<endl;
cin>> lMaxVsol;
}while(lMaxVsol<=0);
do{
cout<<"Numero maximo de iteraciones que desea realizar : "<<endl;
cin>>NMaxIter;
}while(NMaxIter<0);
do{
cout<<"Cual es la probabilidad de mutacion que desea utilizar?"<<endl;
cout<<"Indique esta probabilidad en porcentaje de 100"<<endl;
cin>> ProMut;
cout<<"Recuerde que se trata de un valor entero entre cero y 100."<<endl;
cout<<endl<<endl;
}while(ProMut>100 || ProMut<0);
cout<<"Eliga el metodo que desea utilizar para generar la poblacion"<<endl;
cout<<"inicial."<<endl<<endl;
do{
187
cout<<"Pulse 1 si quiere fijar la longitud real de la variable y "<<endl;
cout<<"luego generar la variable con simbolos del alfabeto {0,1}."<<endl;
cout<<endl;
cout<<"Pulse 2 si quiere generar la variable en {0,1,2} y luego "<<endl;
cout<<"calcular la longitud real de la variable."<<endl<<endl;
cout<<"Pulse 3 si desea recibir mas informacion sobre los metodos"<<endl;
cout<<"usados en la generacion de la poblacion inicial."<<endl<<endl;
cin>> Metodo;
if(Metodo == 3)
{
cout<<"El metodo-1 elige la longitud real de la variable que va a"<<endl;
cout<<"generar entre cero (que corresponde a la palabra vacia) y "<<endl;
cout<<"la longitud maxima propuesta. Una vez fijada la longitud, "<<endl;
cout<<"genera la variable como una cadena de ceros y unos. "<<endl<<endl;
cout<<"El metodo-2 genera la variable como una cadena de simbolos"<<endl;
cout<<"pertenecientes al conjunto {0,1,2} de longitud la longitud"<<endl;
cout<<"maxima propuesta. Despues reordena esta cadena, colocando"<<endl;
cout<<"todos los doses al final de la misma (pues representan el "<<endl;
cout<<"simbolo vacio). Finalmente, calcula el numero de ceros y "<<endl;
cout<<"unos que aparecen en la cadena y que corresponde a la "<<endl;
cout<<"longitud real de la variable generada."<<endl<<endl;
cout<<"Que metodo desea utilizar?"<<endl;
cin>> Metodo;
}//fin if
}while(Metodo<1 || Metodo>2);
do{
cout<<"Si no desea aplicar busqueda local pulse 0, "<<endl;
cout<<"en otro caso pulse 1 o 2 dependiendo del tipo de busqueda"<<endl;
cout<<"que desea utilizar."<<endl;
cout<<"Si desea recibir informacion acerca de los metodos usados"<<endl;
cout<<"pulse 3."<<endl;
cin>> Busqueda;
188
if(Busqueda == 3)
{
cout<<"El tipo de busqueda_1 aplica la busqueda local clasica a "<<endl;
cout<<"cada solucion hasta que no mejora y luego cambia la "<<endl;
cout<<"longitud de las variables en una unidad. Si hay mejora"<<endl;
cout<<"repite el proceso."<<endl;
cout<<"Mientras que la busqueda_2 aplica a cada variable de una "<<endl;
cout<<"solucion la busqueda local y el cambio de longitud. Si al"<<endl;
cout<<"final hay mejora se repite el proceso."<<endl;
cout<<"Que tipo de busqueda local desea aplicar?"<<endl;
cin>> Busqueda;
}//fin if
}while(Busqueda<0 || Busqueda>2);
}//fin Pedir_Datos
void Lee_Prob(const string &problema, int &n_ec, int &m_var,
vector<int> &l_ec, vector<vector<int> > &Sist)
{
ifstream fprob(problema.c_str(),ios::in);
fprob>>n_ec;
fprob>>m_var;
l_ec.resize(2*n_ec);
Sist.resize(2*n_ec);
for(int i= 0; i< 2*n_ec; i++){
fprob>>l_ec[i]; Sist[i].resize(l_ec[i]);
for(int j= 0; j< l_ec[i]; j++) fprob>>Sist[i][j];
}//fin for(i)
}//fin Lee_Prob
void Simplifica(const int &n_ec, vector<int> &l_ec,
vector<vector<int> > &Sist)
{
int coincidencia, columnas, minimo;
189
for(int i= 0; i< 2*n_ec - 1; i+= 2)
{
coincidencia= 0; columnas= 0; minimo= min (l_ec[i],l_ec[i+1]);
while(Sist[i][columnas]== Sist[i+1][columnas] && columnas< minimo)
{
coincidencia= coincidencia +1; columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i]-coincidencia;
for(int j= 0; j< l_ec[i]; j++)
Sist[i][j]= Sist[i][j+coincidencia];
Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia;
for(int j= 0; j< l_ec[i+1]; j++)
Sist[i+1][j]= Sist[i+1][j+coincidencia];
Sist[i+1].resize(l_ec[i+1]);
}//fin if
coincidencia= 0; columnas= 1; minimo= min (l_ec[i],l_ec[i+1]);
while(columnas<= minimo &&
Sist[i][l_ec[i]-columnas] == Sist[i+1][l_ec[i+1]-columnas])
{
coincidencia= coincidencia +1;
columnas= columnas +1;
}//fin while
if(coincidencia!=0)
{
l_ec[i]= l_ec[i] -coincidencia; Sist[i].resize(l_ec[i]);
l_ec[i+1]= l_ec[i+1]-coincidencia; Sist[i+1].resize(l_ec[i+1]);
}//fin if
}//fin for(i)
}//fin Simplifica
190
void Genera_PobIni(vector<vector<vector<int> > > &PobIni,
vector<vector<int> > &lReVsol,
vector<vector<vector<int> > > &AuxPob,
vector<vector<int> > &AuxlRsol, const int &TamPob,
const int &m_var, const int &lMaxVsol, const int &Metodo)
{
int longitud;
PobIni.resize(TamPob);
lReVsol.resize(TamPob);
AuxPob.resize(TamPob); //vector auxiliar
AuxlRsol.resize(TamPob); //vector auxiliar
for(int k= 0; k< TamPob; k++)
{
PobIni[k].resize(m_var);
lReVsol[k].resize(m_var);
AuxPob[k].resize(m_var);
//vector auxiliar
AuxlRsol[k].resize(m_var); //vector auxiliar
for(int i= 0; i< m_var; i++)
{
PobIni[k][i].resize(lMaxVsol);
if(Metodo == 1)
{
longitud= lMaxVsol +1;
lReVsol[k][i]= rand() % longitud;
for(int j= 0; j< lReVsol[k][i]; j++) PobIni[k][i][j]= rand() % 2;
for(int j= lReVsol[k][i]; j< lMaxVsol; j++) PobIni[k][i][j]= 2;
}//fin if(Metodo==1)
if(Metodo == 2)
{
lReVsol[k][i]= lMaxVsol;
for(int j= 0; j< lMaxVsol; j++) PobIni[k][i][j] = rand() % 3;
191
reordeno(lMaxVsol, PobIni[k][i],lReVsol[k][i]);
}//fin if(Metodo==2)
}//fin for(i)
}//fin for(k)
}//fin Genera_PobIni
void reordeno(const int &l, vector<int> &v,int &lReVsol)
{
// Reordeno la variable y calculo la longitud real de la misma
int columna;
for(int j= 0; j< l-1; j++)
if(v[j] == 2)
{
columna= j+1;
while(v[columna] == 2 && columna< l-1) columna = columna +1;
if(v[columna]!= 2)
{
v[j]= v[columna];
v[columna]= 2;
}//fin if
}//if(v[j] == 2)
for(int j= 0; j< l; j++)
if(v[j] == 2) lReVsol= lReVsol -1;
}//fin reordeno
void Evalua(const int &n_ec, const vector<int> &l_ec,
const vector<vector<int> > &Sist, const vector<int> &lReVsol,
const vector<vector<int> > &PobIni, vector<int> &AuxIzq,
vector<int> &AuxDer, int &fitness, int &haysol,int &prueba)
{
int lAuxIzq, lAuxDer;
int coincidencia, minimo;
192
prueba=prueba+1;
coincidencia = 0;
for(int i= 0; i<n_ec; i++)
{
lAuxIzq= l_ec[2*i];
lAuxDer= l_ec[2*i+1];
AuxIzq.resize(lAuxIzq);
AuxDer.resize(lAuxDer);
sustituyo(Sist[2*i], lAuxIzq, AuxIzq, lReVsol, PobIni);
sustituyo(Sist[2*i+1], lAuxDer, AuxDer, lReVsol, PobIni);
minimo = min(lAuxIzq,lAuxDer);
for(int j= 0;j< minimo; j++)
if(AuxIzq[j] != AuxDer[j]) coincidencia= coincidencia+1;
coincidencia= coincidencia + abs(lAuxIzq-lAuxDer);
}//fin for(i)
fitness= coincidencia;
if(fitness == 0) haysol= 1;
}//fin Evaluacion
void sustituyo(const vector<int> &Sist, int &lAux,
vector<int> &Aux, const vector<int> &lVsol,
const vector<vector<int> > &PobIni)
{
int auxiliar, variable;
int j, l;
auxiliar = 0;
for(j= 0;j< lAux; j++)
if(Sist[j+auxiliar]==0 || Sist[j+auxiliar]==1) Aux[j] = Sist[j+auxiliar];
else
{
variable = Sist[j+auxiliar] - 3;
if(lVsol[variable]==0)
193
{
lAux=lAux-1;
Aux.resize(lAux);
auxiliar=auxiliar+1;
j=j-1;
}
else
{
lAux=lAux+lVsol[variable]-1;
Aux.resize(lAux);
for(l= 0; l< lVsol[variable]; l++) Aux[j+l] = PobIni[variable][l];
auxiliar = auxiliar - lVsol[variable] + 1;
j=j+lVsol[variable]-1;
}
}//fin else
}//fin sustituyo
void Mejor_Sol(int &MejorFit, vector<int> &fitness,
const int &TamPob, const int &m_var,
vector<vector<int> > &AuxPob, const int &lMaxVsol,
vector<vector<vector<int> > > &PobIni,
vector<int> &AuxlRsol, vector<vector<int> > &lReVsol)
{
int minimo, variable;
variable = 0;
MejorFit = fitness[0];
for(int k= 1; k< TamPob; k++)
{
if(fitness[k]< MejorFit)
{
MejorFit= fitness[k];
variable= k;
194
}//fin if(fitness[k]< MejorFit)
if(MejorFit == fitness[k])
{
minimo= rand() % 2;
if(minimo == 0)
{
MejorFit= fitness[k];
variable= k;
}//fin if(minimo == 0)
}//fin if(MejorFit == fitness[k])
}//fin for(k)
for(int i= 0; i< m_var; i++)
{
AuxPob[i].resize(lMaxVsol);
AuxlRsol[i]= lReVsol[variable][i];
for(int j= 0; j< lMaxVsol; j++) AuxPob[i][j]= PobIni[variable][i][j];
}//fin for(i)
}//fin Mejor_Sol
void Ruleta(int &Sumfit, const int &TamPob, vector<int> &fitness)
{
Sumfit = 0;
for(int k= 0; k< TamPob; k++) Sumfit= Sumfit + fitness[k];
for(int k= 0; k< TamPob; k++) fitness[k]= Sumfit - fitness[k];
Sumfit = 0;
for(int k= 0; k< TamPob; k++) Sumfit= Sumfit + fitness[k];
}//fin Ruleta
void eligo_padre(int &TamPob, int &Padre, vector<int> &fitness)
{
int limInf, limSup, coinciden;
coinciden= 0;
195
limInf = 0;
limSup = fitness[0];
for(int i= 0; i<TamPob && coinciden == 0; i++)
{
if(limInf <= Padre && Padre < limSup)
{
Padre = i;
coinciden = 1;
}
else
{
limInf = limInf + fitness[i];
limSup = limSup + fitness[i+1];
}
}
}//fin eligo_padre
void Cruce(const int &m_var, const int &lMaxVsol,
vector<vector<int> > &AuxPob,
vector<int> &lReVsolIzq, vector<int> &lReVsolDer,
vector<int> &AuxlRsol, vector<vector<int> > &PobIniIzq,
vector<vector<int> > &PobIniDer)
{
int minimo, columna, longitud;
for(int i= 0; i< m_var; i++)
{
AuxPob[i].resize(lMaxVsol);
minimo= min(lReVsolIzq[i],lReVsolDer[i]);
AuxlRsol[i]= minimo;
for(int j= 0; j< minimo; j++)
{
columna= rand() % 2;
196
if(columna == 0) AuxPob[i][j]= PobIniIzq[i][j];
else AuxPob[i][j]= PobIniDer[i][j];
}//fin for(j)
longitud= lMaxVsol - minimo;
columna= longitud +1;
columna= rand() % columna;
if(lReVsolIzq[i]< lReVsolDer[i])
{
for(int j= 0; j< columna; j++)
{
AuxPob[i][minimo+j]= PobIniDer[i][minimo+j];
if(AuxPob[i][minimo+j]!= 2) AuxlRsol[i]= AuxlRsol[i] +1;
}//fin for(j)
for(int j= columna; j< longitud; j++) AuxPob[i][minimo+j]= 2;
}//fin if
else
{
for(int j= 0; j< columna; j++)
{
AuxPob[i][minimo+j]= PobIniIzq[i][minimo+j];
if(AuxPob[i][minimo+j]!= 2) AuxlRsol[i] = AuxlRsol[i] +1;
}//fin for(j)
for(int j= columna; j< longitud; j++) AuxPob[i][minimo+j] = 2;
}//fin else
}//fin for(i)
}//fin cruce
void Mutacion(const int &m_var, const int &lMaxVsol, int &muto,
vector<vector<int> > &AuxPob, vector<int> &AuxlRsol)
{
for(int i= 0; i< m_var; i++){
for(int j= 0; j< lMaxVsol; j++)
197
{
muto= rand() % lMaxVsol;
if(muto == 0) AuxPob[i][j]= rand() % 3;
}//fin for (j)
AuxlRsol[i]= lMaxVsol;
reordeno(lMaxVsol, AuxPob[i],AuxlRsol[i]);
}//fin for(i)
}//fin Mutacion
void Busqueda_loc_A(const int &m_var, vector<int> &lReVsol,
const int &lMaxVsol, const int &n_ec,
const vector<int> &l_ec, const vector<vector<int> > &Sist,
vector<int> &AuxIzq, vector<int> &AuxDer,
vector<vector<int> > &PobIni, int &fitness, int &haysol,
int &cont_uno, int &cont_dos, int &prueba)
{
int mejoro, repito, MejorFit, columna, valor, vizq, vder;
do{
cont_uno = cont_uno +1;
repito = 0;
do{
cont_dos = cont_dos +1;
mejoro = 0;
for(int i= 0; i< m_var && fitness != 0; i++)
{
for(int j= 0; j< lReVsol[i]; j++)
{
if(PobIni[i][j] == 1) PobIni[i][j] = 0;
else PobIni[i][j] = 1;
MejorFit = fitness;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer,
fitness, haysol, prueba);
198
if(fitness >= MejorFit)
{
fitness = MejorFit;
if(PobIni[i][j] == 1) PobIni[i][j] = 0;
else PobIni[i][j] = 1;
}
else mejoro = 1;
}//fin for(j)
}//fin for(i)
}while(mejoro == 1 && fitness != 0);
for(int i= 0; i< m_var && fitness != 0; i++)
{
//Incremento la variable i_ésima
columna = lReVsol[i];
if(columna < lMaxVsol)
{
MejorFit = fitness;
PobIni[i][columna]= 0;
lReVsol[i] = columna +1;
Evalua(n_ec,l_ec,Sist,lReVsol,PobIni,AuxIzq,AuxDer,fitness,haysol,
prueba);
if(MejorFit <= fitness)
{
fitness = MejorFit;
PobIni[i][columna] = 2;
lReVsol[i] = columna;
}
else repito = 1;
vder = PobIni[i][columna];
MejorFit = fitness;
PobIni[i][columna]= 1;
199
lReVsol[i] = columna +1;
Evalua(n_ec,l_ec,Sist,lReVsol,PobIni,AuxIzq,AuxDer,fitness,
haysol,prueba);
if(MejorFit <= fitness)
{
fitness = MejorFit;
PobIni[i][columna] = vder;
if(vder == 2) lReVsol[i] = columna;
}
else repito = 1;
}//fin if(columna < lMaxVsol)
//decremento la variable i_ésima
if(columna > 0 && fitness != 0)
{
valor = PobIni[i][columna -1];
if(columna < lMaxVsol) vder = PobIni[i][columna];
PobIni[i][columna -1]= 2;
if(columna < lMaxVsol) PobIni[i][columna]= 2;
MejorFit = fitness;
lReVsol[i] = columna -1;
Evalua(n_ec,l_ec,Sist,lReVsol,PobIni,AuxIzq,AuxDer,fitness,haysol,
prueba);
if(MejorFit <= fitness)
{
fitness = MejorFit;
PobIni[i][columna -1]= valor;
if(columna < lMaxVsol)
{
PobIni[i][columna]= vder;
if(vder == 2) lReVsol[i] = columna;
else lReVsol[i] = columna +1;
200
}
else lReVsol[i] = columna;
}//fin if
else repito = 1;
}//fin if if(lReVsol[i] > 0 && columna > 1)
}//fin for(i)
}while(repito == 1 && fitness != 0);
}//fin Busqueda_loc_A
void Busqueda_loc_B(const int &m_var, vector<int> &lReVsol,
const int &lMaxVsol, const int &n_ec,
const vector<int> &l_ec, const vector<vector<int> > &Sist,
vector<int> &AuxIzq, vector<int> &AuxDer,
vector<vector<int> > &PobIni, int &fitness, int &haysol,
int &cont_uno, int &prueba)
{
int repito, MejorFit, columna, valor, vizq, vder;
do{
repito = 0;
cont_uno = cont_uno +1;
for(int i= 0; i< m_var && fitness != 0; i++)
{
for(int j= 0; j< lReVsol[i]; j++)
{
if(PobIni[i][j] == 1) PobIni[i][j] = 0;
else PobIni[i][j] = 1;
MejorFit = fitness;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer,
fitness, haysol,prueba);
if(fitness >= MejorFit)
{
fitness = MejorFit;
201
if(PobIni[i][j] == 1) PobIni[i][j] = 0;
else PobIni[i][j] = 1;
}
else repito = 1;
}//fin for(j)
//Incremento la variable i_ésima
columna = lReVsol[i];
if(columna < lMaxVsol)
{
MejorFit = fitness;
PobIni[i][columna]= 0;
lReVsol[i] = columna +1;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer,
fitness, haysol,prueba);
if(MejorFit <= fitness)
{
fitness = MejorFit;
PobIni[i][columna] = 2;
lReVsol[i] = columna;
}
else repito = 1;
vder = PobIni[i][columna];
MejorFit = fitness;
PobIni[i][columna]= 1;
lReVsol[i] = columna +1;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer,
fitness, haysol,prueba);
if(MejorFit <= fitness) {
fitness = MejorFit;
PobIni[i][columna] = vder;
if(vder == 2) lReVsol[i] = columna;
202
}
else repito = 1;
}//fin if(columna < lMaxVsol)
//decremento la variable i_ésima
if(columna > 0 && fitness != 0)
{
valor = PobIni[i][columna -1];
if(columna < lMaxVsol) vder = PobIni[i][columna];
PobIni[i][columna -1]= 2;
if(columna < lMaxVsol) PobIni[i][columna]= 2;
MejorFit = fitness;
lReVsol[i] = columna -1;
Evalua(n_ec, l_ec, Sist, lReVsol, PobIni, AuxIzq, AuxDer,
fitness, haysol,prueba);
if(MejorFit <= fitness)
{
fitness = MejorFit;
PobIni[i][columna -1]= valor;
if(columna < lMaxVsol)
{
PobIni[i][columna]= vder;
if(vder == 2) lReVsol[i] = columna;
else lReVsol[i] = columna +1;
}
else lReVsol[i] = columna;
}//fin if
else repito = 1;
}//fin if if(lReVsol[i] > 0 && columna > 1)
}//fin for(i)
}while(repito == 1 && fitness != 0);
}//fin Busqueda_loc_B
203
Bibliografı́a
[1] Abdulrab, H.: Résolution d’equations sur les mots: étude et implementation LIPs
de l’algoritme de Makanin.
Ph. D. dissertation, Univ. Rouen, Rouen. 1987
[2] Alonso, C.L., Drubi, F., and Montaña, J.L.: An Evolutionary Algorithm for Solving Word Equation Systems.
[3] Bäck, T., Eiben, A., and Vink, M.: A superior evolutionary algorithm for 3-SAT.
In Saravanan, N., Waagen, D., and Eiben, A., editors,
Proceedings of the Seventh Annual Conference on Evolutionary Programming. Lecture Notes
in Computer Science, Volumen 1477, pages 125 − 136, Springer, Berlin, Germany. 1998
[4] Eiben, A., and van der Hauw, J.: Solving 3-SAT with adaptative genetic algorithms.
In Proceedings of the Fourfth IEEE Conference on Evolutionary Computation, pages 81 − 86,
IEEE Press, Piscataway, New Jersey. 1997
[5] Goldberg, D. E.: Genetic Algorithms in Search, Optimization & Machine Learning.
Addison-Wesley Publishing Company, Inc. 1989
[6] Gottlieb, J., and Voss, N.: Improving the performance of evolutionary algorithms
for the satisfiability problem by refening functions.
In Eiben, A. et al., editors,
Proceedings of the Fifth International Conference on Parallel Problem Solving from Nature.
Lecture Notes in Computer Science, Volumen 1498, pages 755 − 764, Springer, Berlin, Germany. 1998
204
[7] Gottlieb, J., and Voss, N.: Representations, fitness functions and genetic operators for the satisfiability problem.
In Hao, J.-K. et al., editors,
Proceedings of Artificial Evolution. Lecture Notes in Computer Science, Volumen 1363, pages
55 − 68, Springer, Berlin, Germany. 1998
[8] Gottlieb, J., Marchiori, E., and Rossi, C.: Evolutionary Algorithms for the Satisfiability Problem.
Massachusetts Institute of Technology. 2002
[9] Gutiérrez, C.: Solving Equations in Strings: On Makanin’s Algorithm.
Wesleyan University, Middletown, CT 06459, U.S.A.
[10] Hao, J.-K.: A clausal genetic representation and its evolutionary procedures for
satisfiatibility problems.
In Pearson, D., Steele, N., and Albrecht, R., editors,
Proceedings of the International Conference on Artificial Neural Nets and Genetic Algorithms,
pages 289 − 292, Springer, Vienna, Austria. 1995
[11] Hmelevski, J. I.: Equations in free semigroups.
Trudy Mat. Inst. Steklov. 107, 1971
[12] Holland, J. H.: Adaptation in natural and artificial systems.
Ann Arbor: The University of Michigan Press. 1975
[13] Jaffar, J.: Minimal and Complete Word Unification.
Journal ACM, Vol. 37, No 1, January 1990, pages47 − 85
[14] Koscielski, A., and Pacholski, L.: Complexity of Makanin’s Algorithm.
Journal of the ACM, Vol. 43, July 1996, pages670 − 684
[15] Lentin, J.: Equations in free monoids, in automata languages and programming.
In Nivat, M., editors,
North Holland, pages67 − 85. 1972
[16] Makanin, G.S.: The problem of solvability of equations in a free semigroup, Math.
USSR Sbornik 32(1997) (2), 129 − 198
205
[17] Marchiori, E. and Rossi, C.: A flipping genetic algorithm for hard 3-SAT problems.
In Banzhaf, W. et al., editors,
Proceedings of Genetic and Evolutionary Computation Conference, pages 393 − 400, Morgan
Kaufmann, San Francisco, California. 1999
[18] McAllester, D., selman, B., and Kautz, H.: Evidence for invariants in local
search.
In Proceedings of the National Conference on Artificial Intelligent, pages 321 − 326, AAAI
Press, Menlo Park, California. 1997
[19] Pécuchet, J. P.: Equations avec constantes et algoritme de Makanin. These de
doctorat, Laboratoire dı́nformatique, Rouen, 1981
[20] Plotkin, G. D.: Building in equational theories, Mach. pages 73 − 90, 1972
[21] Robson, J. M., and Diekert, V.: On quadratic word equations.
[22] Rossi, C., Marchiori, E., and Kok, J.: An adaptive evolutionary algorithm for the
satisfiability problem.
In Carroll, J. et al., editors,
Proceedings of ACM Symposium on Applied Computing, pages 463 − 469, ACM, New York.
2000
[23] Selman, B., Kautz, H., and Cohen, B.: Noise strategies for improving local search.
In Proceedings of the Twelfth National Conference on Artificial Intelligent, pages 337 − 343,
AAAI Press, Menlo Park, California. 1994
[24] Schulz, K. U.: Word Unification and Transformation of Generalized Equatons.
Journal of Automated Reasoning 11, pages 149 − 184, 1993
[25] Siekmann, J.: A modification of Robinson’s unification procedure, M.Sc. Thesis.
1972
206
Descargar