Backtracking

Anuncio
PARTE II: ALGORÍTMICA
Tema 6. Backtracking.
6.1. Método general.
6.2. Análisis de tiempos de ejecución.
6.3. Ejemplos de aplicación.
6.3.1. Problema de la mochila 0/1.
6.3.2. Problema de las 8 reinas.
6.3.3. Resolución de juegos.
A.E.D. II
Tema 6. Backtracking.
1
6.1. Método general.
• El backtracking (método de retroceso ó vuelta atrás) es una técnica
general de resolución de problemas, aplicable tanto a problemas de
optimización, juegos y otros tipos de problemas.
• El backtracking realiza una búsqueda exhaustiva y sistemática en el
espacio de soluciones. Por ello, suele resultar ineficiente.
• La solución de un problema de backtracking se puede expresar como
una tupla (x1, x2, ..., xn), satisfaciendo unas restricciones P(x1, x2, ..., xn) y
tal vez optimizando una cierta función objetivo.
• En cada momento, en algoritmo se encontrará en un 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.
• Se sigue hasta que la solución parcial sea una solución completa del
problema, o hasta que no queden más posibilidades.
• 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.
A.E.D. II
Tema 6. Backtracking.
2
6.1. Método general.
• Ejemplo. Dado un conjunto de números enteros {13, 11, 7}, encontrar si
existe algún subconjunto cuya suma sea exactamente 20.
• 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).
Árbol de
soluciones 1
1
0
1
2
0
9
1
3
0
4
1
5
0
7
0
6
10
0
7
1
8
11
18
1
k=1
(13)
k=2
(11)
k=3
(7)
13
0
11
1
12
0
14
1
15
13
20
24
31
Sumas totales
• 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.
A.E.D. II
Tema 6. Backtracking.
3
6.1. Método general.
• Posibilidad 2) En cada nivel i decidir qué elemento se añade (1, 2 ó 3).
Representación de la solución (s1, ..., sm), donde mn y si  {1, 2, 3}.
Árbol de
soluciones 2
1
13 2
2
3
24 3
3
20 5
0 1
3
2
11 6
k=1
7 8
3
k=2
18 7
k=3
31 4
• Cada nodo es una posible solución. Será válida si la suma es 20.
• El recorrido es también en profundidad, aunque la forma del árbol (lógico)
generado es diferente.
• Igual que antes, podemos llevar la cuenta de la suma actual, e ignorar los
descendientes cuando el valor sea mayor que 20.
• 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.
A.E.D. II
Tema 6. Backtracking.
4
6.1. Método general.
• Esquema general (sin recursividad). Suponiendo que existe al menos
una solución y que queremos obtener una cualquiera.
Backtracking (var s: array [1.. Max_nivel] of tipo)
nivel:= 1;
fin:= false;
repeat
s[nivel]:= Generar (nivel, s);
if Solución (nivel, s) then
fin:= true;
else if Criterio (nivel, s) then
nivel:= nivel + 1;
else while not MasHermanos (nivel, s) do
Retroceder (nivel, s);
until fin=true;
• Variables:
– s: Almacena la solución (un array, desde 1 hasta el nº máximo de niveles).
– nivel: Indica el nivel actual en el que se encuentra el algoritmo.
– fin: Valdrá true cuando hayamos encontrado alguna solución.
A.E.D. II
Tema 6. Backtracking.
5
• Funciones:
6.1. Método general.
– Generar (nivel, s): Genera el siguiente hermano (o el primero) para el nivel
actual. Devuelve el siguiente valor a añadir a la solución parcial actual, según
cuál sea esta solución parcial y el nivel considerado.
– Solución (nivel, s): Comprueba si la solución almacenada desde s[1], ...,
s[nivel], es una solución válida para el problema.
– 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.
– MasHermanos (nivel, s): Devuelve verdad 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.
• ¿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.
A.E.D. II
Tema 6. Backtracking.
6
6.2. Análisis de tiempos de ejecución.
• El tiempo de ejecución depende del número de nodos generados y del
tiempo requerido para cada nodo.
• Por lo general, el tiempo en 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 xi.
• Si el número de posibles valores para cada xi es mi, entonces se generan:
m1
m1·m2
...
m1·m2· ... ·mn
nodos en el nivel 1
nodos en el nivel 2
....
nodos en el nivel 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 nivel 1
tenemos n posibilidades, en el nivel 2 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.
A.E.D. II
Tema 6. Backtracking.
7
6.3. Ejemplos de aplicación.
6.3.1. Problema de la mochila 0/1.
n
n
• Problema: maximizar
x
i
 vi
sujeto a
i 1
x
i
 wi  M
con xi= 0, 1
i 1
siendo vi = beneficio del objeto i; wi = peso de i; M capacidad máxima.
• 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).
• Diseño de la solución con backtracking:
– Representación de la solución: Una solución será de la forma (x1, x2, ..., xn),
con xi= 0, 1. Se generará un árbol binario de soluciones.
– En cada nivel i, probamos la posibilidad de incluir o no el objeto i.
0
1
1
2
0
3
0
1
4
5
x1
9
1
6
1
0
7
8
0
10
0
1
11 12
A.E.D. II
Tema 6. Backtracking.
1
13
1
0
14 15
x2
x3
8
6.3.1. Problema de la mochila 0/1.
• Para calcular el peso y el beneficio en cada nodo podemos usar variables
locales w_act, v_act que guardarán el peso y el beneficio acumulado.
• El array de soluciones será s: array [1..n] of integer.
– s[i] = 1, 0. Se añade o no se añade el objeto i.
– s[i] = -1. No se ha considerado el objeto i (es el valor de inicialización).
• Funciones:
– Generar (nivel, s). Probar los valores 0 y 1.
Si s[nivel]=-1 entonces Devolver 0;
Sino w_act:= w_act + w[nivel];
v_act:= v_act + v[nivel];
Devolver 1;
– Solución (nivel, s). Indica los nodos hoja que cumplen la restricción de peso.
Devolver (nivel=n) and (w_act  M);
– Criterio (nivel, s). Indicará si se cumple la restricción de peso.
Devolver (w _act  M);
– MasHermanos (nivel, s).
Devolver s[nivel]  1;
– Retroceder (nivel, s).
w_act:= w_act - s[nivel]*w[nivel];
v_act:= v_act - s[nivel]*v[nivel];
s[nivel]:= -1;
A.E.D. II
9
nivel:= nivel - 1;
Tema 6. Backtracking.
6.3.1. Problema de la mochila 0/1.
• Diferencias con el esquema general:
•
– Al ser un problema de optimización no acabamos hasta haber recorrido todos
los nodos. Acabar cuando nivel = 0 (volvemos al nodo raíz).
– En cada momento llevamos la mejor solución hasta un nodo. Si encontramos
una solución nueva, comprobar si es mejor que la solución actual.
– Variable v_max, con el valor de la mejor solución hasta este nodo y s_max
con los objetos que la componen.
If Solución (nivel, s) then
if v_act > v_max then begin
v_max:= v_act;
s_max:= s;
end;
1
x1
Ejemplo. n = 4; M = 7;
0
1
v = (2, 3, 4, 5)
w = (1, 2, 3, 4)
2
0
3
....
....
17
1
10
....
....
....
A.E.D. II
Tema 6. Backtracking.
1
25
1
0
.... 26
29
1
....
28 ....
v= 10
x2
0
18
x3
x4
....
10
6.3.1. Problema de la mochila 0/1.
• Orden de complejidad del algoritmo: Número de nodos generado= 2n+1-1.
El algoritmo es de O(2n).
• Problema: En el ejemplo, se generan todos los nodos posibles. La
función Criterio es siempre cierta (excepto para algunos nodos hoja).
• Solución: Intentar eliminar algunos nodos del árbol de soluciones, con
una función Criterio más restrictiva.
– Para cada nodo, hacer una estimación del máximo beneficio que se podría
obtener a partir del mismo.
– Si es menor que el mayor beneficio de una solución anterior (v_max)
entonces rechazar ese nodo y sus descendientes.
• ¿Cómo podemos calcular una cota superior del beneficio que se puede
obtener a partir de un nodo, es decir a partir de (x1, ..., xk)? La estimación
debe poder realizarse de forma rápida.
• La estimación del beneficio para el nivel y nodo actual será:
v_estimado:= v_act + Estimacion (k + 1, M - w_act);
• Estimacion (k, Q): Estimar una cota superior para el problema de la
mochila 0/1, usando los objetos k..n, con capacidad máxima Q.
A.E.D. II
Tema 6. Backtracking.
11
6.3.1. Problema de la mochila 0/1.
• Idea: el resultado del problema de la mochila (no 0/1) es una cota
superior válida para el problema de la mochila 0/1.
• Demostración: Si una solución para el problema de la mochila 0/1 tiene
valor vTOTAL, entonces esta es una solución válida para la mochila no 0/1.
Por lo tanto, la solución óptima para el problema de la mochila no 0/1
será vTOTAL o mayor.
• Estimacion (k, Q): Aplicar el algoritmo voraz para el problema de la
mochila, con los elementos de k..n. Si los beneficios son enteros, nos
podemos quedar con la parte entera del resultado anterior.
•
Ejemplo. n = 4; M = 7; v = (2, 3, 4, 5)
w = (1, 2, 3, 4)
A.E.D. II
Tema 6. Backtracking.
12 12
6.3.1. Problema de la mochila 0/1.
• Función Criterio (nivel, s, v_act, w_act, v_max).
Si (w _act > M) entonces
Devolver False;
Sino
v_estimado:= v_act + MochilaVoraz (nivel+1, M - w_act);
Devolver v_estimado > v_max;
• Modificación en el algoritmo de backtracking.
...
while not MasHermanos (nivel, s) or
not Criterio (nivel, s, v_act, w_act, v_max) do
Retroceder (nivel, s);
...
• Se eliminan nodos a costa de aumentar el tiempo de ejecución de la
función Criterio. ¿Cuál será el tiempo de ejecución total?
• Suponemos todos los objetos ordenados por vi/wi.
• Tiempo de la función Criterio en el nivel i (en el peor caso) = 1 + Tiempo
de la función MochilaVoraz = 1 + n - i.
• Idea intuitiva. Tiempo en el peor caso (suponiendo todos los nodos):
Número de nodos O(2n) * Tiempo de cada nodo (función criterio) O(n).
• ¿Tiempo: O(n·2n)?
A.E.D. II
13
Tema 6. Backtracking.
6.3.1. Problema de la mochila 0/1.
n
t(n ) 

i 1
n
2  ( n  i  1)  ( n  1)  2 
i
i
i 1
n

i  2  2 ·2
i
n 1
 2n  4
i 1
• Conclusiones:
– El cálculo “intuitivo” del tiempo no es correcto (no es válido O(n·2n)).
– El orden de complejidad no varía, O(2n), aunque el tiempo es 2 veces mayor.
– Si se podan más de la mitad de los nodos podemos esperar una mejora.
Posible modificación 1
• Para cada nodo, generar primero el valor 1 y luego el valor 0 (en lugar de
primero 0 y luego 1).
• Ejemplo anterior.
A.E.D. II
Tema 6. Backtracking.
14
6.3.1. Problema de la mochila 0/1.
• Si la solución óptima es de la forma s = (1, 1, 1, X, X, 0, 0, 0) entonces se
alcanza antes la solución generando primero 1 (y luego 0).
• Si es de la forma s = (0, 0, 0, X, X, 1, 1, 1) será mejor empezar por 0.
• Idea: es de esperar que la solución de la mochila 0/1 sea “parecida” a la
de la mochila no 0/1. Si ordenamos los objetos por vi/wi entonces
tendremos una solución del primer tipo.
Posible modificación 2
• Utilizar una representación de la solución como un conjunto de los
elementos incluidos. S = (s1, s2, ..., sm) donde mn y si  {1, 2, ..., n}.
Árbol
combinatorio
• ¿Cómo generar un
árbol de este tipo?
• ¿Cuál es el número
de nodos máximo?
A.E.D. II
Tema 6. Backtracking.
15
6.3.2. Problema de las n reinas.
• Problema: Dado un tablero de ajedrez de tamaño nxn, encontrar una
forma (o todas) de colocar n reinas, sin que ninguna de ellas pueda
comerse a otra.
• Solución 1: Probar todas las posiciones posibles y para cada una
comprobar si es válida. Para tamaño 8: 4.4261165.368 posibilidades.
• Solución 2: Colocar cada reina en cada fila. Una solución será un array
de 1..8. Para cada reina se probarán cada una de las 8 columnas. Habrá
que probar: 88 = 161777.216 posibilidades.
• Solución 3: No colocar dos reinas en una misma columna. La solución
será una permutación de los números (1, 2, ..., 8): 8! = 40.320 posibilidad.
• Error: No se comprueba si la situación es correcta hasta el final.
A.E.D. II
Tema 6. Backtracking.
16
6.3.2. Problema de las n reinas.
Solución con backtracking.
• Características: Puede existir solución o no. No es un problema de
optimización. Suponemos que buscamos todas las soluciones.
• Representación.
– Array solución: s: array [1..8] of 0..8.
– s[i] = j. La reina de la fila i está en la columna j.
• Recorrido con backtracking: En cada nivel i, probar las formas de
colocar la reina de la fila i, desde la columna 1 hasta la 8.
• La posición será válida (podemos avanzar de nivel) si la reina i no está
en la misma columna o diagonal que alguna de las reinas anteriores.
• Funciones:
– Generar (nivel, s). Probar primero la posición 1, luego la 2, ..., hasta la 8.
– MasHermanos (nivel, s). Cierto si s[nivel] es menor que 8.
– Criterio (nivel, s). Comprobar si la reina de la posición s[nivel] no se come a
las anteriores (1, 2, ..., nivel-1).
– Solución (nivel, s). Cierto si el nodo es una hoja (nivel = n) y se cumple
Criterio.
– Retroceder (nivel, s). Quitar la reina de la posición nivel. s[nivel]:= 0 (valor
de inicialización).
A.E.D. II
17
Tema 6. Backtracking.
6.3.2. Problema de las n reinas.
NReinasBacktracking (var s: array [1.. n] of integer)
nivel:= 1;
s[1]:= 0;
repeat
s[nivel]:= s[nivel] + 1;
while (s[nivel]  n) and not Criterio (nivel, s) do
s[nivel]:= s[nivel] + 1;
if (nivel=n) and (s[nivel]  n) then
MostrarSolucion (s);
else if (s[nivel]  n) then
nivel:= nivel + 1;
s[nivel]:= 0;
else
nivel:= nivel -1;
until nivel = 0;
Criterio (nivel: 0..n; s: array [1.. n] of integer) : boolean
for i:= 1 to nivel-1 do
if (s[nivel]=s[i]) or |s[i] - s[nivel]| = |i - nivel| then
Devolver false;
Devolver true;
A.E.D. II
Tema 6. Backtracking.
18
6.3.2. Problema de las n reinas.
• Ejemplo. N= 4.
1
X
X
6
2
X
X
3
X
X
X
4
X
7
X
8
X
5
X
X
X
X
X
9
X
X
X
A.E.D. II
Tema 6. Backtracking.
19
6.3.2. Problema de las n reinas.
• Evaluación de la eficiencia. La evaluación es compleja, ya que el
tiempo de ejecución en cada nodo no es constante y el número de nodos
generado es difícil de predecir.
– El tiempo de ejecución de la función Criterio depende del nivel. Para nivel i,
el número de comprobaciones es (i-1). Está en O(i).
– Número de nodos, en el peor caso:
•
•
•
•
•
Nivel 0: 1
Nivel 1: n/2
Nivel 2: (n/2)(n-1)
....
Nivel n: n!/2
0·n/2
Comprobaciones
1·(n/2)(n-1)
“
(n-1)·n!/2
“
• La cota superior está muy alejada del número real de nodos generados.
• Ejemplo. Para n = 4. Número de nodos en el peor caso = 33. Número de
nodos generados realmente = 9.
• Además, en cada nodo (excepto en las hojas) se comprueban 4 posibles
descendientes. Este es el número de veces que se ejecutan las
funciones Criterio y Genera (s[nivel]:= s[nivel] + 1).
• Solución: Estimación de la eficiencia por probabilidad. Hacemos un
cálculo aproximado del número de nodos esperado.
A.E.D. II
Tema 6. Backtracking.
20
6.3.2. Problema de las n reinas.
• Estimación de la eficiencia por probabilidad.
– Generamos varias permutaciones de (1, 2, .., n), de forma aleatoria.
– Para cada una calcular el nivel al que llegaría (aplicando la función Criterio), y
el número de nodos máximo para ese nivel.
– Hacer una media del número de nodos.
6.3.3. Resolución de juegos.
Características.
• Consideraremos juegos con dos jugadores, A y B, que mueven
alternativamente (primero A y luego B).
• En cada movimiento un jugador puede elegir entre varias posibilidades.
• El resultado del juego puede ser que gana A, gana B o hay empate.
• Supondremos juegos en los que no influye el azar.
• Ejemplo. El juego de las tres en raya.
A
B
A
B
X
X
X
O
X
X
O
X
O
O
A.E.D. II
Tema 6. Backtracking.
21
6.3.3. Resolución de juegos.
• El problema puede ser representado mediante árboles de juegos:
– Cada nodo representa una situación del juego.
– El nodo raíz será el comienzo de una partida. Los descendientes de un nodo
dado son los movimientos posibles de un jugador.
– El nivel 1 del árbol (y los impares) son los movimientos del jugador A.
– El nivel 2 del árbol (y los pares) son los movimientos del jugador B.
– Un nodo hoja es una situación donde acaba el juego.
• Ejemplo. Parte del árbol de juego de las tres en raya.
X
X
X
O
X
O
X
Movimientos de A
X
X
O
X
O
X
X
X
X
X
O
O
O
O
X
X
X
O
O
X
O
X
O
O
X
O
O
O
X
Movimientos de A
Movimientos de B
O
X
O
O
O
1
X
X
X
O
X
X
X
X
O
X
X
O
O
O
O
O
O
X
-1
X
O
X
X
X
X
X
O
X
X
X
O
O
X
O
X
X
O
O
X
O
O
X
O
O
X
O
0
1
A.E.D. II
Tema 6. Backtracking.
0
22
6.3.3. Resolución de juegos.
• Cada nodo hoja está etiquetado con un número, que valdrá:
1 Si el juego finaliza con victoria de A.
-1 Si acaba con victoria de B.
0 Si se produce un empate.
• El objetivo (para A) es encontrar un camino en el árbol que le lleve hasta
un nodo hoja con valor 1.
• ¿Qué pasa si a partir de la situación inicial no se llega a un nodo hoja
con valor 1?
– En los movimientos de B, el jugador B intentará llegar a hojas con valor -1 (ó
en caso de no existir, de valor 0).
– En los movimientos de A, el jugador A intentará llegar a hojas con valor 1 (ó
en caso de no existir, de valor 0).
• Los valores de las hojas se propagan al padre de la siguiente forma:
– En los movimientos de A, el valor del nodo padre será el máximo de los
valores de los nodos hijos.
– En los movimientos de B, el valor del nodo padre será el mínimo de los
valores de los nodos hijos.
– Se repite hasta llegar al nodo raíz (situación de partida).
A.E.D. II
Tema 6. Backtracking.
23
6.3.3. Resolución de juegos.
• Ejemplo. Árbol de juego, con la estrategia ganadora.
X
X
X
O
Movimientos de A
O
X
X
Movimientos de A
O
X
O
X
0
O
X
O
X
X
X
O
O
X
O
X
O
O
X
O
O
1
X
X
X
Movimientos de B
O
0
X
X
X
X
O
O
O
X
O
-1
O
O
1
X
O
X
X
X
X
O
X
X
O
O
O
O
O
1
O
O
X
X
X
X
X
O
X
X
X
O
O
X
O
X
X
O
O
X
O
O
X
O
O
X
O
1
0
X
-1
X
0
X
0
• En general, tendremos una función de utilidad.
• Función de utilidad: para cada nodo hoja devuelve un valor numérico,
indicando cómo de buena es esa situación para el jugador A.
A.E.D. II
Tema 6. Backtracking.
24
6.3.3. Resolución de juegos.
• Si el árbol del juego es muy grande o infinito (por ejemplo, en el ajedrez)
entonces la función de utilidad debe poder aplicarse sobre situaciones no
terminales.
• En estos casos, se generará el árbol hasta un nivel determinado y se
propagarán los valores de las hojas utilizando la estrategia minimax.
• Estrategia minimax:
– En los niveles que son movimientos de A, se maximiza el valor de los hijos.
– En los niveles que son movimientos de B, se minimiza el valor de los hijos.
• El objetivo del problema es elegir el siguiente movimiento a realizar por el
jugador A.
• Solución: escoger el movimiento indicado por el hijo de la raíz con mayor
valor.
Implementación de la estrategia minimax con backtracking
• Usando backtracking, realizaremos un recorrido exhaustivo en
profundidad del árbol del juego.
• Puesto que hay propagación de valores de los nodos hijos a los padres,
usaremos una versión recursiva.
A.E.D. II
Tema 6. Backtracking.
25
6.3.3. Resolución de juegos.
Implementación de la estrategia minimax con backtracking
• Algoritmo.
BuscaMinimax (B: tipo_tablero; modo: (MAX, MIN)) : real;
if B es una hoja then
Devolver Utilidad (B);
else begin
if modo = MAX then
valor_act:= -;
else
valor_act:= ;
for cada hijo C del tablero B do
if modo = MAX then
valor_act:= max (valor_act, BuscaMinimax(C, MIN));
else
valor_act:= min (valor_act, BuscaMinimax(C, MAX));
Devolver valor_act;
end;
A.E.D. II
Tema 6. Backtracking.
26
Descargar