Você está na página 1de 207

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 experi-
mentales 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 modifi-
caciones al algoritmo genético simple. Los resultados experimentales nos permitirán com-
probar 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 natu-


rales.

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 res-
tricciones 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 iden-
tidad.
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 re-
sultan 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 acumula-
da 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 ob-
jetivos 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 pre-
tende 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 indivi-
duos 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 dife-
renciadas: 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 repre-
sentació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

|{z } 1000
0010 |{z } 0100
|{z} 0010
|{z } 1000
|{z }
C1 C2 C3 C4 C5

representa una candidata a solución sobre las aristas ordenadas.

11
C2 C4
0 0 1
0 1 C3 1 1 C5 0
C1 C3 C5 ...
1 0 0
C4 C1
0 0 0

C5 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.

C2 Con esta representación de las


M variables nos vemos obligados a
w
1 C3 controlar las cadenas generadas
para garantizar que se visitan
C1 )
todas las ciudades una sóla vez
]
C4 cada una. Después, comprobamos
cuál es la que hace mı́nima la
C5 distancia recorrida.

Observamos que las posibles soluciones son de la forma

C2 C2 C2
7 M k
w
C3 1 C3 C3
/
C1 i C1 C1 6
] q C4
C4 C4
 
?
C5 C5 C5

12
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
Con esta representación sólo
] ~3
tenemos que preocuparnos de
9 encontrar la permutación que
1
haga mı́nima la distancia necesaria
4
~ 3 para recorrer todas las ciudades.
5

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 comu-
nicadas 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 gene-
ració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
f itness(i)
P(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 alfa-
beto 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

P
Individuo Pob. Inicial Valor de x f (x) = x2 P (i) = fi / fj

I1 01010 10 100 0.19


I2 01001 18 324 0.6
I3 11100 7 49 0.09
I4 00010 8 64 0.12

Se deduce que el individuo I2 es el que más probabilidad tiene de contribuir en la gene-


ración siguiente, por eso, la sección circular que le corresponde en la ruleta es la más grande.
qqqqq
qqqqqqqqqqqqqq qqqqqqqqqqqqqq
qqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqq
qqqq qqqqq qqqqqq qqqq Nota:
qqq qqqqq qqqqq qqqq
qq qq qqqqq 19 % qqqq qqq
qqq qq 12 % qqqqq qqqqq La selección es con reemplazamiento,
q q qqq qqq
qq qq 9 % qqqqq qqqq
qqqq qqqq q q es decir, un mismo individuo puede
qqq qq
qqq qqqq qqq qqqq
qqq qq qq qq ser copiado varias veces. Por lo tanto,
qqq qqqq 60 % qqqq qqqq
qqq qqqqq qqqq qq
qqqq qqqqq qqqqq qqqqqqq los individuos mejor adaptados tienen
qqqqq qqqqqqqqq qqqqqqq
qqqqqqq qqqqqqqqqqqqqqqqqqqq qqqqqqqq
qqqqqqqqqq q
qqqqqqqqqqqqqqqqqqqqqqqqq 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 con-
cretas 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
- 0 ···
ω2 = 00101111

Al comparar los sı́mbolos que ocupan la segunda posición, y teniendo en cuenta la proba-
bilidad de cruce, ha resultado que el sı́mbolo de ω1 se copia a la nueva cadena.

ω1 = 01010111
- 0 1 ···
ω2 = 00101111

Repitiendo el proceso hasta la última posición de ambas cadenas podrı́a obtenerse

ω1 = 01010111
- 0 1 1 0 1 1 1 1
ω2 = 00101111

19
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 en-
cuentre 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 dis-
tintos 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 gene-
ració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 sim-
ple, 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
exp( x2 ) cos(5x)
x −→ (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
exp( x̃−2 ) cos(5(x̃−2)) exp( x̃2 −1) cos(5x̃−10)
x̃ −→ x̃ − 2 −→ 2
((x̃−2)+3)2
+1= (x̃+1)2
+1

Suponemos que la solución viene dada con tres cifras decimales. Luego,

0, 000 ≤ x̃ ≤ 4, 000

23
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

P
Individuo P0 x̃ f˜(x̃) 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 comple-
tar 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

P
Individuo P1 x̃ f˜(x̃) 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 opera-
dores 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
P
Individuo P2 x̃ f˜(x̃) 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̃) P (Ii ) = f˜i / fj

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

Repetimos los pasos para generar la población P4 .

P
Individuo P4 x̃ f˜(x̃) 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 .

P
Individuo P5 x̃ f˜(x̃) 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 algo-
ritmo 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 mu-
tació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 ca-
racterı́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

f (H) δ(H)
m(H, k + 1) ≥ m(H, k) · · [1 − pc · − o(H) · pm ]
f¯ L−1

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 ge-


neració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

f¯ + cf¯
m(H, k + 1) = m(H, k) · = (1 + c) · m(H, k)

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

δ(H)
ps = 1 −
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

δ(H)
ps ≥ 1 − pc ·
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 ·
f L−1

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 )
f L−1

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
f L−1

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
δ(H)
1−
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 par-
tiendo de una población inicial evoluciona, dando lugar a nuevas poblaciones, con la es-
peranza 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 cons-
tituye 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 impor-
tantes 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 pro-
blema 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 respectiva-
mente 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 in-
satisfactible) 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óri-


ca, pueden encontrar soluciones para los problemas SAT, pero no garantizan una
respuesta definitiva a todos los problemas, ya que no pueden determinar con seguri-
dad 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 re-
solver 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 can-
didata 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 re-
presenta 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 fun-
ciones 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 opti-
mizació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 direc-


tamente 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  
m
X n
Y
g(x) =  hij (x)
i=1 j=1

siendo m el número de 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)
hij (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 corres-
ponden 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 pro-


blema 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 inconsis-
tentes 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 proble-


ma.

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 mu-
cho 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 algo-
ritmos 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áusu-
las 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 o-
perador 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 reemplaza-
miento 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 
2  X 
 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 propor-
cional 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 reco-
rrer 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ódu-


los 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 pro-
babilidad µ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 meca-
nismo 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 evolu-


tivos, 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 desa-
rrollado 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 ···
⇒ ω1 6= ω2
ω2 - a b b a b b b ···
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 deter-
minar. 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, algorit-
mos 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], Jaf-
far [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 computa-
cional.

4.2. Conceptos Básicos

Sean A = { a1 , . . . , ar } un alfabeto y V = { v1 , v2 , . . .} un conjunto infinito de varia-


bles 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
X
Una solución σ de un sistema se dice mı́nimal si la suma |σ(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. El Algoritmo de Makanin

4.3.1. 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 co-
rrespondientes 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 b
a y a y

y a y a
b x 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 x

52
podemos reemplazar y = xa en el otro suceso de y, para obtener

b a b a
x x

=⇒ =⇒ xa = ax
a a
b 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 reemplaza-
miento.

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 x1 x1

bx1 yx1 = yax2 z =⇒ x1 = x2 =⇒


y x2 x2
a 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 z

1 2 3 4 5 6 7

Además, resulta conveniente tener exactamente dos copias de cada variable en la repre-
sentación. En este caso, tenemos que añadir una copia de la variable z. Ası́, la repre-
sentación final del sistema viene dada por

b y
x1 x1
x2

z
y x2
a z

1 2 3 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 lon-
gitud.

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 x

1 2 3 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, respectiva-


mente, 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 NP-
completo, 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 X
zi = zi j = 1,. . . ,n-1
ej ≤i<ej+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 z

1 2 3 4 5 6 7

4.3.3. 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 sucesiva-
mente, 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 x

1 2 3 4 5 6 7 8

60
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 cr rc bm

superfluo transporte
(col(b1 ) < lc ) (lc ≤ Izq(bs) < cr ) fijo fijo

61
Ejemplo 30
1. En este diagrama

x b
a y

y a
b x

1 2 3 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 x

1 2 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(Ex ) = tr(e1 , · · · , en ) = (etr tr


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 7 4tr 8

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 gener-
alizada 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 ve-
ces) 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 para i = 0, . . . , m − 1

yv , zv 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 si X̃j = v
vj =
 z 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 ecua-
ciones 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 con-
catenació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 α3

73
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 indivi-
duos 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 deno-
taremos 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 reorde-
nación se tiene que α1 = 10BB.
Para i = 2 construimos aleatoriamente la cadena α2 = BB1B, después de la reorde-
nació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 reorde-
nación se tiene que α1 = 11BB.
Para i = 2 construimos aleatoriamente la cadena α2 = 0B01, después de la reorde-
nació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 reorde-
nación se tiene que α1 = 0000.
Para i = 2 construimos aleatoriamente la cadena α2 = BBBB, después de la reor-
denació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 sub-
cadenas α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,
α1 = 11B - x1 = 11
I1 = 11B01B -
α2 = 01B - x2 = 01

resulta que
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,
α1 = 1BB - x1 = 1
I2 = 1BB01B -
α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
f (I) = (max{lk , rk } − ck )
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,
2
X 2
X
f (I1 ) = fk (I1 ) = (max{lk , rk } − ck ) = 0 + 3 = 3
k=1 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,
2
X 2
X
f (I2 ) = fk (I2 ) = (max{lk , rk } − ck ) = 0 + 2 = 2
k=1 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
f (j) − f (i)
j=1
P(i) = 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,
4
X
sum = 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
P
Individuo fitness f^
itness 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

I1 = α11 · · · αm
1

I2 = α12 · · · αm
2

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

αi1 = 011101 BB
| {z } |{z}
βi1 β̄i1
αi2 |{z } BBBB
= 1101 | {z }
β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 prob-


abilidad 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


- I10 = 01B11B
α21 = B11 - α21 = 11B

α12 = 011 - α12 = 011


- I20 = 0111BB
α22 = B1B - α22 = 1BB

α13 = 101 - α13 = 101


- I30 = 1010BB
α23 = BB0 - α23 = 0BB

84
α14 = BB1 - α14 = 1BB
- I40 = 1BB10B
α24 = 1B0 - α24 = 10B

Obtenemos la pobalción inicial P0 dada por los individuos:

I10 = 01B11B
I20 = 0111BB
I30 = 1010BB
I40 = 1BB10B

Evaluamos la población inicial:


I10
x2 10x1 = 1x1 0x1 - 111001 6= 101001
I10
- f1 = 3
x2 x1 = 10x2 - 1101 6= 1011
I20
x2 10x1 = 1x1 0x1 - 110011 6= 10110011
I20
- f2 = 8
x2 x1 = 10x2 - 1011 6= 101
I30
x2 10x1 = 1x1 0x1 - 010101 6= 11010101
I30
- f3 = 7
x2 x1 = 10x2 - 0101 6= 100
I40
x2 10x1 = 1x1 0x1 - 10101 6= 1101
I40
- f4 = 5
x2 x1 = 10x2 - 101 6= 1010

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 com-
pletar 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
- α1 = 00B
α11 = 01B

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
- α2 = 01B
α21 = 11B

Luego, I21 = 00B01B.

Mutación

Este individuo es sometido al proceso de mutación, resultando


00B −→ B0B −→ 0BB
- I21 = 0BB01B
01B −→ 01B −→ 01B

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
- α1 = 011
α12 = 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
- α2 = 1BB
α22 = 1BB

Luego, I21 = 0111BB.

Mutación

Este individuo es sometido al proceso de mutación, resultando


011 −→ B01 −→ 01B
- I21 = 01B01B
0BB −→ 0B1 −→ 01B

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 pro-
blemas 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 sis-
tema 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 coinci-
da 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. In-
tentaremos 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 evalua-
ciones, 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 oca-


siones 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 encon-
tramos 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 propues-
tos 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 acotare-
mos 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 prob-
lema, 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 al-
goritmo 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 indi-
viduo, entonces αi es sustituida por la nueva cadena.

110
Cuando terminamos de modificar las longitudes de todas las cadenas βi en bus-
ca 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 indi-
viduo 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 cuan-
do 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 ejecu-
ció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 sufi-


ciente 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 encon-


trar 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, cuan-
do 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 refe-
rencia 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 o-
peradores 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 x5 = 101

158
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:

x3 = Λ x4 = Λ
x5 = 1 x6 = Λ x7 = 0

C.5. 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: x3 = 10 x4 = Λ

x5 = 0 x6 = Λ x7 = 0

C.6. 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]<<" "; }//fin
fsol<<" "; fsol<<"Con fitness "<<fitness<<endl;

170
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<<" en media = "<<media<<endl;
}//fin if

186
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 Sol-
ving 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 algo-
rithms.

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 Lear-
ning.

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, Ger-
many. 1998

204
[7] Gottlieb, J., and Voss, N.: Representations, fitness functions and genetic oper-
ators 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 Sa-
tisfiability 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 pro-
blems.

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

Você também pode gostar