El algoritmo A*

Anuncio
El algoritmo A*
1. Empezar con ABIERTO conteniendo sólo el nodo inicial. Poner el valor g de ese nodo a
O, su valor h' al que corresponda, y su valor f' a h' + O, es decir h'. Inicializar CERRADOS
como la lista vacía.
2. Repetir el siguiente procedimiento hasta que se encuentre el nodo meta: si no existen
nodos en ABIERTOS, informar del fallo. De lo contrario, tomar aquel nodo de ABIERTOS
con mejor valor f'. Llamarle MEJORNODO. Quitarlo de ABIERTOS. Colocarlo en
CERRADOS. Mirar si MEJORNODO es un nodo meta. Si lo es, salir e informar de la solución
(bien sea MEJORNODO si lo único que queremos es el nodo, o el camino que se ha creado
entre el estado inicial y MEJORNODO si estamos interesados en el camino). En caso
contrario, generar los sucesores de MEJORNODO, pero no poner MEJORNODO apuntando
aún a ellos (antes debemos mirar si alguno de ellos ya ha sido generado). Para cada
SUCESOR, hacer lo siguiente:
1. Poner a SUCESOR apuntando a MEJORNODO. Estos enlaces hacia atrás hacen
posible recuperar el camino una vez que se ha encontrado la solución.
2. Calcular g(SUCESOR) = g(MEJORNODO) + costes de ir desde MEJORNODO
hasta SUCESOR.
3. Mirar si SUCESOR está contenido en ABIERTOS (es decir, si ya ha sido generado pero
no procesado). Si es así, llamemos a ese nodo VIEJO. Puesto que este nodo ya existe en
el grafo, podemos desechar SUCESOR, y añadir VIEJO a la lista de los sucesores de
MEJORNODO. Ahora debemos decidir si el enlace paterno de VIEJO debería apuntar a
MEJORNODO. Debería ocurrir así si el camino que hemos encontrado hasta SUCESOR es
de menor coste que el mejor camino actual hasta VIEJO (puesto que SUCESOR y VIEJO
son en realidad el mismo nodo). Para ver si cuesta menos llegar hasta VIEJO a través de
su padre actual o hasta SUCESOR a través de MEJORNODO, comparar sus valores g. Si
VIEJO tiene menor (o igual) coste, no necesitamos hacer nada. Si SUCESOR tiene menor
coste, entonces hacer que el enlace paterno de VIEJO apunte hacia MEJORNODO, grabar
el nuevo camino óptimo en g(VIEJO), y actualizar f'(VIEJO).
4. Si SUCESOR no estaba en ABIERTOS, ver si estaba en CERRADOS. En caso afirmativo,
llamar VIEJO al nodo de CERRADOS, y añadir VIEJO a la lista de los sucesores de
MEJORNODO. Mirar si el nuevo camino es mejor que el viejo tal como se hizo en el paso
2.3 y actualizar apropiadamente el enlace paterno y los valores de g y f'. Si acabamos de
encontrar un mejor camino a VIEJO, debemos propagar la mejora a los sucesores de
VIEJO. Esto es un poco delicado. VIEJO apunta a sus sucesores. Cada sucesor a su vez
apunta a sus sucesores, y así sucesivamente hasta que cada rama termina en un nodo
que bien ya está en ABIERTOS o no tiene sucesores. Por tanto, para propagar el nuevo
coste hacia abajo, podemos hacer una búsqueda transversal en profundidad del árbol,
empezando en VIEJO, cambiando el valor g de cada nodo (y por tanto su valor f'),
terminando cada rama cuando se alcanza bien un nodo sin sucesores o bien un nodo para
el que ya se ha encontrado un camino equivalente o mejor.4 Es fácil examinar esta
La segunda comprobación garantiza que el algoritmo acabará aunque haya ciclos en el grafo. Si
hay un ciclo, la segunda vez que visitemos un nodo veremos que el camino no es mejor que la
primera vez que lo visitamos, y la propagación se detendrá.
4
condición. El enlace paterno de cada nodo apunta hacia atrás a su mejor predecesor
conocido. Conforme propagamos a un nodo siguiente, debemos mirar si su predecesor
apunta al nodo del que estamos viniendo. Si lo hace así, debemos continuar la
propagación. Si no, su valor g ya refleja el mejor camino del que forma parte. Por tanto la
propagación debe parar allí. Pero es posible que al propagar el nuevo valor de g hacia
abajo, el camino que estamos siguiendo pueda volverse mejor que el camino a través del
antecesor actual. Por tanto debemos comparar los dos. Si el camino a través del antecesor
actual es aún mejor, debemos detener la propagación. Si el camino a través del cual
estamos propagando es ahora mejor, inicializar el antecesor y continuar la propagación.
5. Si SUCESOR no estaba ya en ABIERTOS o en CERRADOS, ponerlo en ABIERTOS y
añadirlo a la lista de sucesores de MEJORNODO. Calcular f'(SUCESOR) = g(SUCESOR) +
h'(SUCESOR).
Podemos hacer algunas observaciones interesantes sobre este algoritmo. La
primera concierne al papel de la función g. Nos permite escoger el nodo a expandir a
continuación sobre la base, no sólo de cuán bueno parece el nodo en sí mismo (medido
por h'), sino también sobre la base de cuán bueno era el camino hasta el nodo. Al
incorporar g en f' no siempre elegiremos como nuestro siguiente nodo a expandir el nodo
que parece más cercano a la meta. Esto es Útil si nos preocupa el camino que elijamos.
Pero si sólo nos importa llegar a una solución de la forma que sea, podemos definir
siempre g como 0, con lo que también elegiríamos siempre el nodo que parece más
cercano a la meta. Si queremos encontrar un camino que tenga el menor número de
pasos, entonces debemos poner el coste de ir desde un nodo a su sucesor como una
constante, usualmente 1. Si, por otra parte, queremos encontrar el camino de menor
coste y algunos operadores cuestan más que otros, entonces debemos poner el coste de ir
de un nodo a otro de forma que refleje dichos costes. Así pues, el algoritmo A* puede
usarse tanto si estamos interesados en encontrar un camino con el coste total mínimo,
como si simplemente queremos encontrar cualquier camino de la forma más rápida
posible.
La segunda observación atañe a h', el estimador de h, la distancia de un nodo a la
meta. Si h' es un estimador perfecto de h entonces A* convergerá. Inmediatamente a la
meta sin búsqueda. Cuanto mejor sea h' más cerca estaremos de este enfoque directo. Si
por otra parte, h' es siempre O, la búsqueda estará controlada por g. Si g también es O, la
estrategia de búsqueda se realizará al azar. Si g es siempre 1, se realizará una búsqueda
en anchura. Todos los nodos de un nivel tendrán valores g más bajos, y por tanto f' más
bajos, que cualquier nodo del siguiente nivel. ¿Qué ocurre, por otra parte, si h' no es ni
perfecto ni O? ¿Podemos decir algo interesante sobre el comportamiento de la búsqueda?
La respuesta es afirmativa si podemos garantizar que h' nunca sobrestimará h. En ese
caso, el algoritmo A* garantiza que encontraremos un camino óptimo (determinado por g)
a la meta, si éste existe. Podemos ver esto fácilmente a partir de algunos ejemplos.5
Consideremos la situación mostrada en la figura 3.13. Supongamos que el coste de
todos los arcos es 1. Inicialmente, todos los nodos excepto A están en ABIERTOS (aunque
la figura muestra la situación dos pasos más tarde, después de haber expandido B y E).
Para cada nodo, se indica f' como la suma de h' y g. En este ejemplo, el nodo B tiene la f'
más baja, 4, por lo que lo expandiremos en primer lugar. Supongamos que tiene un único
sucesor E, que también parece estar a tres movimientos de la meta. Ahora f'(E) es 5, igual
Un algoritmo que siempre encuentre el camino óptimo a una meta, si existe, se llama admisible
[Nilsson, 1980].
5
que f'(C). Supongamos que resolvemos esta disyuntiva a favor del camino que estábamos
siguiendo actualmente. Entonces expandiremos E a continuación. Supongamos que
también él tiene un único sucesor F', también a tres movimientos de la meta. Es evidente
que estamos haciendo movimientos sin realizar ningún progreso. Pero f'(F) = 6, que es
mayor que f'(C). Por tanto expandiremos C a continuación. Así vemos que al subestimar
h(B) hemos desperdiciado algún esfuerzo. Pero en un momento u otro descubrimos que B
estaba más lejos de lo que pensábamos, por lo que podemos regresar y encontrar otro
camino.
Consideremos ahora la situación mostrada en la figura 3.14. De nuevo expandimos E en el
primer paso. En el segundo paso volvemos a expandir E. En el siguiente paso expandimos
F, y finalmente generamos G, para un camino de solución de longitud 4. Pero supongamos
que existe un camino directo desde D a la solución, que produce un camino de longitud 2.
Nunca lo encontraremos. Al sobrestimar h'(D) hacemos que D parezca tan malo que
podemos encontrar alguna otra solución que en realidad sea peor (aunque parezca
mejor), sin siquiera expandir D. En general, si h' puede sobrestimar h, no podemos
asegurar que encontremos el camino de menor coste a la solución, a menos que
expandamos el grafo entero hasta que todos los caminos sean más largos que la mejor
solución. Pero como normalmente no necesitamos asegurarnos de tener la mejor solución,
éste no es un asunto que nos preocupe demasiado.
La tercera observación que podemos hacer sobre el algoritmo A* tiene que ver con las
relaciones entre árboles y grafos. El algoritmo se estableció en su forma más general para
aplicarlo a los grafos. Naturalmente, puede simplificarse para aplicarse a árboles,
omitiendo el mirar si un nuevo nodo está en ABIERTOS o CERRADOS. Esto da lugar a que
la generación de nodos sea más rápida, pero puede producir que se realice la misma
búsqueda varias veces si los nodos están duplicados con frecuencia.
Puede demostrarse que bajo ciertas condiciones, el algoritmo A* es óptimo en cuanto
genera el menor número de nodos posible en el proceso de encontrar una solución al
problema. Bajo otras condiciones no es óptimo. Para una discusión formal de estas
condiciones, ver [Gelperin, 1977] y [Martelli, 1977].
Descargar