Está en la página 1de 51

Robert Espinoza Domínguez

Algoritmos de retroceso
Retroceso o Backtracking

 El backtracking (o método de retroceso o vuelta


atrás) es una técnica general de resolución de
problemas, aplicable en problemas de
optimización, juegos y otros tipos.

 El backtracking realiza una búsqueda exhaustiva


y sistemática en el espacio de soluciones. Por ello,
suele resultar muy ineficiente.
Retroceso o Backtracking

 Se puede entender como “opuesto” a los


algoritmos voraces o de avance rápido.

 Voraz:añadir elementos a la solución y no


deshacer ninguna decisión tomada.

 Backtracking: añadir y quitar todos los


elementos. Probar todas las combinaciones.
Características

 Una solución se puede expresar como una tupla:


(x1, x2, ..., xn), satisfaciendo unas restricciones
P(x1, x2, ..., xn) y tal vez optimizando cierta función
objetivo.
 En cada momento, el algoritmo se encontrará en
cierto nivel k, con una solución parcial (x1, ..., xk).
 Si se puede añadir un nuevo elemento a la solución
xk+1, se genera y se avanza al nivel k+1.
 Si no, se prueban otros valores de xk.
 Si no existe ningún valor posible por probar,
entonces se retrocede al nivel anterior k-1.
Características

 Se sigue hasta que la solución parcial sea una


solución completa del problema, o hasta que no
queden más posibilidades por probar.
 El resultado es equivalente a hacer un recorrido
en profundidad en el árbol de soluciones. Sin
embargo este árbol es implícito, no se almacena
en ningún lugar.
Características

Inicio

x1=0 x1=1

x1=0 x1=0 x1=1 x1=1


x2=0 x2=1 x2=0 x2=1

x1=0 x1=0 x1=0 x1=0 x1=1 x1=1 x1=1 x1=1


x2=0 x2=0 x2=1 x2=1 x2=0 x2=0 x2=1 x2=1
x3=0 x3=1 x3=0 x3=1 x3=0 x3=1 x3=0 x3=1
Características

Inicio

x1=0 x1=1

x1=0 x1=0 x1=1 x1=1


x2=0 x2=1 x2=0 x2=1

x1=0 x1=0 x1=0 x1=0 x1=1 x1=1 x1=1 x1=1


x2=0 x2=0 x2=1 x2=1 x2=0 x2=0 x2=1 x2=1
x3=0 x3=1 x3=0 x3=1 x3=0 x3=1 x3=0 x3=1
Características

Representación simplificada del árbol.


1
x1 0 1

2 9
x2 0 1 0 1

3 6 10 13

x3 0 1 0 1 0 1 0 1

4 5 7 8 11 12 14 15
Ejemplo

Dado un conjunto de números enteros


{13, 11, 7}, encontrar si existe algún
subconjunto cuya suma sea
exactamente 20.
Ejemplo – Posibilidad 1
En cada nivel i decidir si el elemento i está o no en la
solución. Representación de la solución: (x1, x2, x3),
donde xi = (0, 1).
1
Árbol de 0 1 k=1 (13)
soluciones 1

2 9
0 1 0 1 k=2 (11)

3 6 10 13

0 1 0 1 0 1 0 1 k=3 (7)

4 5 7 8 11 12 14 15
Sumas
0 7 11 18 13 20 24 31 totales
Ejemplo – Posibilidad 1

 Cada nodo representa un paso del algoritmo, una


solución parcial en cada momento dado.
 El árbol indica un orden de ejecución (recorrido en
profundidad) pero no se almacena en ningún lugar.
 Una solución es un nodo hoja con valor de suma 20.
 Posible mejora: En cada nodo llevamos el valor de la
suma hasta ese punto. Si el valor es mayor que 20,
retroceder al nivel anterior
Ejemplo – Posibilidad 2
En cada nivel i decidir qué elemento se añade (1, 2 ó 3) en
la solución. Representación de la solución: (s1, …, sm),
donde m ≤ n y si  {1, 2, 3}.

Árbol de 0 1
soluciones 2 1 3 k=1
2
13 2 11 6 7 8

2 3 3 k=2
24 18
3 20 5 7
3
k=3
31 4
Ejemplo – Posibilidad 2

 Cada nodo es una posible solución. Será válida si la


suma es 20.
 El recorrido es también en profundidad.
 Necesitamos funciones para generar los nodos, para
descartar nodos y para saber si un nodo es solución
 ¿Cómo será la eficiencia del algoritmo? Depende del
número de nodos.
Árboles de retroceso (backtracking)

 El árbol es simplemente una forma de


representar la ejecución del algoritmo.
 Es implícito, no almacenado (no
necesariamente).
 El recorrido es en profundidad, normalmente de
izquierda a derecha.
 La primera decisión para aplicar backtracking:
¿cómo es la forma del árbol?
 Preguntas relacionadas: ¿Qué significa cada
valor de la tupla solución (x1, ..., xn)? ¿Cómo es
la representación de la solución al problema?
5.1. Árboles de retroceso (backtracking)

 Tipos comunes de árboles de backtracking:

 Árboles binarios.

 Árboles n-arios.

 Árboles permutacionales.

 Árboles combinatorios.
Árboles binarios
s= (x1, x2, ..., xn), con xi  {0, 1}

x1 1
0 1

2 5
x2 0 1 0 1

3 4 6 7

• Tipo de problemas: elegir ciertos elementos de entre un


conjunto, sin importar el orden de los elementos.
• Problema de la mochila 0/1.
• Encontrar un subconjunto de {12, 23, 1, 8, 33, 7, 22} que
sume exactamente 50.
Árboles n-arios

s= (x1, x2, ..., xn), con xi  {1,..,k}


1
x1 1 3
2

2 6 10
x2 1 3 1 3 1 3
2 2 2

3 4 5 7 8 9 11 12 13

• Tipo de problemas: varias opciones para cada xi.


– Problema del cambio de monedas.
– Problema de las n reinas.
Árboles permutacionales
s = (x1, x2, ..., xn), con xi  {1,..,n} y xi ≠ xj
1
1 3 x1
2
2 6 11
2 3 1 2 x2
1 3

3 5 7 9 12 14
3 2 3 1 2 1 x3

4 6 8 10 13 15

• Tipo de problemas: los xi no se pueden repetir.


– Generar todas las permutaciones de (1, ..., n).
– Asignar n trabajos a n personas, asignación uno-a-uno.
Árboles combinatorios

s= (x1, x2, ..., xm), con m≤n, xi  {1,..,n} y xi < xi+1

1
1 3 x1
2
2 6 8
2 3
3 x2
3 5 7
3
x3
4

• Tipo de problemas: los mismos que con árboles binarios


– Binario: (0, 1, 0, 1, 0, 0, 1)  Combinatorio: (2, 4, 7)
Método general.

Cuestiones a resolver antes de programar:


 ¿Qué tipo de árbol es adecuado para el problema?
 ¿Cómo es la representación de la solución?
 ¿Cómo es la tupla solución? ¿Qué indica cada xi y qué valores
puede tomar?
 ¿Cómo generar un recorrido según ese árbol?
 Generar un nuevo nivel.
 Generar los hermanos de un nivel.
 Retroceder en el árbol.
 ¿Qué ramas se pueden descartar por no conducir a
soluciones del problema?
 Poda por restricciones del problema.
 Poda según el criterio de la función objetivo.
Método general

Método Clase.Backtracking (var s: TuplaSolución)


nivel 1
s  sINICIAL
fin  falso
Hacer
Generar (nivel, s)
Si Solución (nivel, s) entonces
fin  verdadero
Sino Si Criterio (nivel, s) entonces
nivel  nivel + 1
Sino Mientras NO MasHermanos (nivel, s) hacer
Retroceder (nivel, s)
fMientras
fSi
fSi
Mientras fin = falso
fMetodo
Método general

 Variables:
 s: Almacena la solución parcial hasta cierto punto.
 sINICIAL: Valor de inicialización.
 nivel: Indica el nivel actual en el que se encuentra el algoritmo.
 fin: Valdrá verdadero cuando hayamos encontrado alguna
solución.
 Funciones:
 Generar (nivel, s): Genera el siguiente hermano (o el primero)
para el nivel actual.
 Solución (nivel, s): Comprueba si la tupla (s[1], ..., s[nivel]) es
una solución válida para el problema.
Método general

 Criterio (nivel, s): Comprueba si a partir de (s[1], ..., s[nivel])


se puede alcanzar una solución válida. En otro caso se
rechazarán todos los descendientes (poda).
 MasHermanos (nivel, s): Devuelve verdadero si hay más
hermanos del nodo actual que todavía no han sido
generados.
 Retroceder (nivel, s): Retrocede un nivel en el árbol de
soluciones. Disminuye en 1 el valor de nivel, y posiblemente
tendrá que actualizar la solución actual, quitando los
elementos retrocedidos.
 Además, suele ser común utilizar variables temporales con
el valor actual (beneficio, peso, etc.) de la tupla solución.
Método general

 ¿Cómo serían estas funciones en los ejemplos anteriores?


 Otros posibles casos de problemas:
1. No está garantizado que exista una solución, puede existir
alguna o no.
2. Queremos obtener todas las soluciones, no sólo una.
3. El problema es de optimización. De todas las soluciones
posibles queremos aquella que maximice (o minimice) una
función objetivo.
Análisis de tiempos de ejecución

 Depende del número de nodos generados y del tiempo


requerido para cada nodo.
 Por lo general el tiempo de cada nodo es constante.
 Suponiendo que una solución sea de la forma:
(x1, x2, …, xn), en el peor caso se generarán todas las
posibles combinaciones para cada x1.
 Si el número de posible de valores para cada xi es mi,
entonces se generan:
m1 nodos en el nivel 1
m1 • m2 nodos en el nivel 2

m1 • m2 • … • mn nodos en el nivel 3
Análisis de tiempos de ejecución

 Ejemplo. Para el problema de la suma de


subconjuntos mi = 2. El número de nodos generados
es:
t(n) = 2 + 22 + 23 + … + 2n = 2n-1 - 2
 Ejemplo. Calcular todas las permutaciones de (1, 2,
…, n). En el primer nivel tenemos n posibilidades, en el
segundo nivel (n-1), …, en el nivel n una posibilidad.
t(n) = n + n•(n-1) + n•(n-1)•(n-2) + … + n!  O(n!)
 En general tendremos tiempos con órdenes de
complejidad factoriales o exponenciales.
Conclusiones

 Backtracking: Recorrido exhaustivo y sistemático en un


árbol de soluciones.
 Pasos para aplicarlo:
 Decidir la forma del árbol.
 Establecer el esquema del algoritmo.
 Diseñar las funciones genéricas del esquema.
 Relativamente fácil diseñar algoritmos que encuentren
soluciones óptimas pero...
 Los algoritmos de backtracking son muy ineficientes.
 Mejoras: mejorar los mecanismos de poda, incluir otros
tipos de recorridos (no solo en profundidad)
 Técnica de Ramificación y Poda.
Ejemplo. Subconjuntos de suma dada
Subconjuntos de suma dada

Encontrar un subconjunto del


Conjunto T= {t1, t2, ..., tn} que
sume exactamente P.
Subconjuntos de suma dada

 Variables:
 Representación de la solución con un árbol binario.
 s: array [1..n] de {-1, 0, 1}
 s[i] = 0  el número i-ésimo no se utiliza
 s[i] = 1  el número i-ésimo sí se utiliza
 s[i] = -1  valor de inicialización (número i-ésimo
no estudiado)
 sINICIAL: (-1, -1, ..., -1)
 fin: Valdrá verdadero cuando se haya encontrado
solución.
 tact: Suma acumulada hasta ahora (inicialmente 0).
Subconjuntos de suma dada

Funciones:
 Generar (nivel, s)
s[nivel]  s[nivel] + 1
Si s[nivel] =1 entonces
tact  tact + t[nivel]
Fin Si
 Solución (nivel, s)
devolver (nivel = n) Y (tact = P)
 Criterio (nivel, s)
devolver (nivel<n) Y (tact≤P)
 MasHermanos (nivel, s)
devolver s[nivel] < 1
Subconjuntos de suma dada

 Retroceder (nivel, s)
Si s[nivel] =1 entonces
tact tact - t[nivel]
Fin Si
s[nivel]:= -1
nivel:= nivel – 1
Subconjuntos de suma dada
Método Clase.Backtracking (var s: TuplaSolución)
nivel  1
s  sINICIAL
fin  falso
Hacer
Generar (nivel, s)
si Solución (nivel, s) entonces
fin  verdadero
sino
Si Criterio (nivel, s) entonces
nivel  nivel + 1
sino
mientras NO MasHermanos (nivel, s) hacer
Retroceder (nivel, s)
fin Mientras
Fin Si
Fin si
Mientras fin = falso
Fin Método
Subconjuntos de suma dada

Variaciones del esquema general:

1) ¿Y si no es seguro que exista una solución?

2) ¿Y si queremos almacenar todas las soluciones


(no sólo una)?

3) ¿Y si el problema es de optimización (maximizar o


minimizar)?
Subconjuntos de suma dada - Caso 1
 Puede que no exista ninguna solución.
Método Clase.Backtracking (var s: TuplaSolución)
nivel 1
s sINICIAL
fin  falso
Hacer Para poder generar todo el
árbol de backtracking
Generar (nivel, s)
si Solución (nivel, s) entonces
fin verdadero
sino
si Criterio (nivel, s) entonces
nivel  nivel + 1
sino
mientras NOT MasHermanos (nivel, s) Y (nivel > 0)
Retroceder (nivel, s)
finsi
Mientras fin Fin= falso Y (nivel > 0)
= falso
Subconjuntos de suma dada - Caso 2
 Queremos almacenar todas las soluciones.
Método Clase.Backtracking (var s: TuplaSolución)
nivel  1 • En algunos problemas los nodos
s  sINICIAL intermedios pueden ser soluciones
fin  falso • O bien, retroceder después de encontrar
una solución
Hacer
Generar (nivel, s)
si Solución (nivel, s) entonces
fin  verdadero
Almacenar (nivel, s)
si Criterio
sino (nivel,(nivel,
si Criterio s) entonces
s) entonces
nivel  nivel + 1
sino
mientras NOT MasHermanos (nivel, s) Y (nivel > 0)
hacer Retroceder (nivel, s)
finsi
Mientras finnivel >0
= falso
Subconjuntos de suma dada - Caso 3
 Problema de optimización (maximización).
Método Clase.Backtracking (var s: TuplaSolución)
nivel 1
voa: valor óptimo actual
s sINICIAL soa: solución óptima actual
voafalso
fin -; soa  Ø
Hacer
Generar (nivel, s)
y Valor(s) > voa entonces
si Solución (nivel, s) entonces
fin verdadero
voa Valor(s); soa  s
si Criterio
sino (nivel,(nivel,
si Criterio s) entonces
s) entonces
nivel  nivel + 1
sino
mientras NOT MasHermanos (nivel, s) Y (nivel>0)
hacer Retroceder (nivel, s)
finsi
Mientras finNivel >0
= falso
Problema de la mochila 0/1
Problema de la mochila 0/1

 Los objetos no se pueden partir (se cogen enteros o


nada).
 Datos del problema:
 n: número de objetos disponibles.
 M: capacidad de la mochila.

 p = (p1, p2, ..., pn) pesos de los objetos.

 b = (b1, b2, ..., bn) beneficios de los objetos.

 Formulación matemática:
Maximizar  xi bi sujeto a la restricción  xi pi ≤ M, y xi{0,1}
i=1..n i=1..n
Problema de la mochila 0/1

 Características del problema


 Es un problema de optimización (maximización)
 Sólo nos interesa una solución, la óptima
 Existirá al menos una solución (no incluir ningún objeto)
Problema de la mochila 0/1

 Ejemplo: n = 4; M = 7
b = (2, 3, 4, 5)
p = (1, 2, 3, 4) 4 kg

3 kg

7 Kg. 2 kg

PVP 5 PVP 4 PVP 3 1 kg PVP 2

 ¿Qué solución devuelve el algoritmo voraz para el problema


de la mochila?
 ¿Qué solución devuelve el algoritmo voraz adaptado al caso
0/1 (o se coge un objeto entero o no)?
 ¿Cuál es la solución óptima?
Problema de la mochila 0/1

 Ejemplo: n = 2; M = 100
b = (2, 190)
p = (1, 100)
100 kg

100 Kg. 1 kg
PVP 2 PVP 190

 ¿Qué solución devuelve el algoritmo voraz para el


problema de la mochila?
 ¿Qué solución devuelve el algoritmo voraz adaptado al
caso 0/1 (o se coge un objeto entero o no)?
 ¿Cuál es la solución óptima?
Problema de la mochila 0/1.

Aplicación de backtracking (proceso metódico):


1) Determinar cómo es la forma del árbol de
backtracking ↔ cómo es la representación de la
solución.
2) Elegir el esquema de algoritmo adecuado,
adaptándolo en caso necesario.
3) Diseñar las funciones genéricas para la aplicación
concreta: según la forma del árbol y las
características del problema.
4) Posibles mejoras: usar variables locales con “valores
acumulados”, hacer más podas del árbol, etc.
Problema de la mochila 0/1
1. Representación de la solución.
 Con un árbol binario: s= (x1, x2, ..., xn), con xi  {0,1}

 xi = 0  No se coge el objeto i
 xi = 1  Sí se coge el objeto i
 xi = -1  Objeto i no estudiado
1 x1
 En cada nivel i se prueba 0 1
la posibilidad de incluir
2 9
o no el objeto i 0 x2
1 0 1
 Las soluciones
están en nivel n 3 6 10 13
x3
0 0 1 1
1 1 0 0

4 5 7 8 11 12 14 15
Problema de la mochila 0/1
1. Representación de la solución.
 También es posible usar un árbol combinatorio:
s= (x1, x2, ..., xm), con m ≤ n, xi  {1,..,n} y xi < xi+1
 xi  Número de objeto escogido
 m  Número total de objetos escogidos
 Las soluciones están en cualquier nivel

1
1 3 x1
2
2 6 8
2 3
3 x2
3 5 7
3
x3
4
Problema de la mochila 0/1

2. Elegir el esquema de algoritmo: caso optimización.


Método Clase.Backtracking (var s: array [1..n] de entero)
nivel 1; s sINICIAL pact: Peso actual
voa -; soa Ø bact: Beneficio actual
pact 0; bact 0
Hacer
Generar (nivel, s)
si Solución (nivel, s) AND (bact > voa) entonces
voa bact; soa s
si Criterio (nivel, s) entonces
nivel nivel + 1
sino
mientras NOT MasHermanos (nivel, s) AND (nivel>0)
hacer Retroceder (nivel, s)
finsi
Mientras nivel > 0
Fin Método
Problema de la mochila 0/1

3) Funciones genéricas del esquema.


 Generar (nivel, s)  Probar primero 0 y luego 1
s[nivel] s[nivel] + 1 si s[nivel] = 1 entonces
pact pact + p[nivel]*s[nivel] pact  pact + p[nivel]
bact bact + b[nivel]*s[nivel] bact  bact + b[nivel]
 Solución (nivel, s) finsi
devolver (nivel =n) Y (pact ≤ M)
 Criterio (nivel, s)
devolver (nivel < n) Y (pact ≤ M)
 MasHermanos (nivel, s)
devolver s[nivel] < 1
 Retroceder (nivel, s)
pact  pact – p[nivel]*s[nivel]
bact bact – b[nivel]*s[nivel]
s[nivel]  -1
nivel  nivel – 1
Problema de la mochila 0/1

 Ejemplo: n = 4; M = 7; 1 x1
0 1
b = (2, 3, 4, 5)
2 17
p = (1, 2, 3, 4) x2
0 1 0 1

3 10 18 25
x3
0 0 1 1
1 1 0 0

4 7 11 14 19 22 26 29
0 1 x4
1 1
0 1 0 1 0 1 0 1 0 1 0 0

5 6 8 9 12 13 15 16 20 21 23 24 27 28 30 31

pact: 0 4 7 9 8 7 10
bact: 0 5 9 12 11 10 14
Problema de la mochila 0/1

 El algoritmo resuelve el problema, encontrando


la solución óptima pero es muy ineficiente.
 ¿Cuánto es el orden de complejidad?
 Número de nodos generados = 2n+1 – 1
 El algoritmo es del orden O(2n)

 Problema adicional: en el ejemplo, se


generan todos los nodos posibles, no hay
ninguna poda. La función Criterio es siempre
cierta (excepto para algunos nodos hoja).
Problema de la mochila 0/1

 Solución: Intentar eliminar algunos nodos del


árbol de soluciones con una función Criterio
más restrictiva.
 Incluir una poda según el criterio de optimización.
 Poda según el criterio de peso: si el peso actual
es mayor que M podar el nodo.
 Poda según el criterio de optimización: si el
beneficio actual no puede mejorar el voa podar el
nodo.
Tarea

Diseñe el algoritmo e implemente en Java lo siguiente:


 Asignación de tareas uno a uno

 Asignar una tarea a una persona. Una persona sólo puede


realizar una tarea.
 El problema de las n reinas
 Distribuir n reinas en un tablero de n x n de tal manera no se
maten entre ellas
 El problema de la mochila (0/1)

También podría gustarte