Professional Documents
Culture Documents
En esta lección se aplican las técnicas y los recursos expuestos en las lecciones anteriores
para implementar algunos algoritmos de búsqueda con retroceso (retroceso, profundización
progresiva, IDA*).
“Rectificar es de sabios.”
(Anónimo)
SOLUCION:
a) El algoritmo de búsqueda con retroceso se puede describir así:
Comenzar con el estado inicial; si hemos alcanzado un estado final, devolver el camino desde el
estado inicial; en otro caso, si hemos alcanzado un estado para el que no hay más posibles
sucesores, retroceder al estado anterior; en otro caso, avanzar, generando un nuevo sucesor del
estado actual y pasando a él.
La estrategia de búsqueda con retroceso considera el nodo más profundo generado en cada
momento y selecciona un sucesor por donde continuar la búsqueda. Sólo en caso de que la
búsqueda a partir de dicho sucesor fracase se considera otro sucesor por donde continuar. De este
modo, la búsqueda conserva en todo momento un único camino o secuencia de estados, aunque
para cada estado es necesario también llevar la cuenta de cuáles de sus sucesores fueron ya
generados.
Supondremos que la clase que implementa los estados cumple con la interfaz definida para la
búsqueda a ciegas en R9.1.
Existen múltiples formas de implementar la búsqueda con retroceso. Examinaremos a continuación
una implementación sencilla inspirada en el procedimiento recursivo backtrack de (Nilsson,
1980). La búsqueda parte de un estado e y avanza probando ordenadamente cada uno de sus
sucesores. La secuencia de estados que se está considerando en cada momento corresponde a la
secuencia de llamadas recursivas. Dicho de otro modo, la pila de la recursión es la que almacena
en cada entorno los estados generados.
c) Podemos utilizar una solución similar a la presentada en R.2.9, aunque esta vez
emplearemos clases y la interfaz (finalp e) (hijos e) definida en R.9.1. Nótese que no
es necesario definir la operación (clave e) si no pretendemos utilizar la función (iguales e
e2).
(defclass mc ()
((m :reader misioneros :initarg :m)
(c :reader canibales :initarg :c)
(b :reader barcas :initarg :b)))
(defun hacer-mc (b m c)
(make-instance 'mc :b b :m m :c c))
(defconstant *num-misioneros* 3)
(defconstant *num-canibales* 3)
(defconstant *num-barcas* 1)
> (mc)
0
1
2
3
4
5
6
7
8
9
10
11
(1 3 3)
(0 3 1)
(1 3 2)
(0 3 0)
(1 3 1)
(0 1 1)
(1 2 2)
(0 0 2)
(1 0 3)
(0 0 1)
(1 1 1)
(0 0 0)
NIL
**************************
SOLUCION:
a) La función backa-simple recibe el estado e a explorar, la cota de coste, y el coste g
acumulado para llegar hasta e. Si el valor f(e) = g(e) + h(e) excede la cota de coste, se
fuerza un retroceso con fracaso. El procedimiento es muy similar al backtrack-c expuesto en
R.10.1. Simplemente hay que llevar la cuenta del coste acumulado del camino.
Nótese que la función puede terminar con éxito, devolviendo la solución, con fracaso (si la poda
es igual al valor de sig-poda), o repetir la búsqueda con retroceso con el nuevo valor de sig-
poda.
Ahora podemos resolver el problema del puzzle-8 presentado en la lección 9 con la siguiente
llamada:
P.10.1. Disponemos de dos jarras, una de 5 litros de capacidad y la otra de 2 litros. La jarra grande
está llena y la pequeña vacía. Queremos que la jarra pequeña contenga 1 litro de agua. Para ello
podemos vaciar una jarra en la otra, o bien en el suelo, pero no emplear más agua que los 5 litros
iniciales. Se pide representar el problema como una búsqueda en espacio de estados y resolverlo
aplicando una búsqueda con retroceso.
P.10.2. a) Resolver nuevamente el problema P.9.2 utilizando la función BID. Comparar los
resultados con los obtenidos en P.9.2.
b) Resolver el problema del puzzle-8 utilizando las funciones bid e ida. Realizar pruebas de
búsqueda a distintas profundidades y comparar los resultados con los obtenidos en P.9.4.b-d.
P.10.3. El Mini Su Doku consiste en un tablero de 2x2 cajas. Cada caja contiene a su vez una rejilla
de 2x2 celdas. El problema consiste en rellenar cada celda con un dígito del 1 al 4 (ambos
inclusive) de modo que:
• Cada fila del tablero debe contener 4 dígitos distintos.
• Cada columna del tablero debe contener 4 dígitos distintos.
• Cada caja del tablero debe contener 4 dígitos distintos.
Por ejemplo, el tablero de la izquierda tiene como solución el tablero de la derecha:
3 4 2 3 1
2 4 1 3 2 4
3 1 3 1 4 2
2 2 4 1 3
Se pide lo siguiente:
a) Definir una clase adecuada para representar los posibles estados del Mini Su Doku.
b) Establecer un orden entre las casillas del tablero para la asignación de valores.
c) Escribir los métodos necesarios para resolver el problema mediante la función
backtrack. Concretamente, la función hijos devolverá una lista con tantos
sucesores como asignaciones consistentes existan para la siguiente casilla vacía en
el orden de asignación de valores (NIL si no hay ninguna). Por ejemplo. Para la
figura anterior de la izquierda, existirán dos sucesores posibles:
1 3 4 3
2 4 2 4
3 1 3 1
2 2
1
4
2
3 1
P.10.6. a) Modificar la implementación del algoritmo backa de R.10.2 de forma que en cada
llamada se reciba el estado e a explorar, así como su antecesor inmediato en la búsqueda.
Cuando se generen los hijos de e se eliminará el ciclo sobre su antecesor.
b) Modificar la implementación de la función ida para que emplee este nuevo procedimiento de
búsqueda más eficiente.
c) Comparar el rendimiento de la nueva función sobre el problema del puzzle-8.
____________________________________________________________________________________
Las erratas y comentarios sobre estos apuntes son bienvenidos en: lawrence@lcc.uma.es