Estrategia utilitzable quan totes les accions tenen el mateix cost
Explora tots els estats a una profunditat abans d'explorar els estats a profunditat
Garanteix trobar la solució òptima
Definim la frontera com una cua (FIFO)
Els estats ja visitats es guarden en una llista o conjunt (per evitar cicles)
Implementació
defcerca_amplada(estat_inicial):
"""Cerca en amplada en un problema."""
frontera = collections.deque([estat_inicial])
visitats = set()
while frontera:
estat = frontera.popleft()
visitats.add(estat)
if es_solucio(estat):
return estat
for succesor in succesors(estat):
if succesor notin visitats:
frontera.append(succesor)
Exemple: Botelles d'aigua (I)
Estat inicial: (0,0) - Estat final: (2,*) o (*, 2)
Funcio de succesió: Operacions de buidar, omplir i trasvasar
Cada node és un parell de valors (a,b) que representen l'estat de les botelles
La busca en amplitud explora l'arbre per nivells
Podem observar que solament s'explora un nombre molt reduït de tots els possibles estats
Propietats
Propietat
Valor
Comentaris
Completitud
Sí
Si l'espai de cerca és de memòria, la solució es trobarà en algun moment.
Optimalitat
Sí
Si totes les accions tenen el mateix cost, la primera solució trobada serà òptima.s
Complexitat temporal
On és el factor de ramificació i és la profunditat de la solució
Complexitat espacial
On és el factor de ramificació i és la profunditat de la solució
Problemes
La complexitat espacial és un problema real.
Per exemple, suposem que cada estat ocupa 1KB i que el factor de ramificació és 10.
Si la solució es troba a una profunditat de 10, necessitarem 10GB de memòria.
Si es troba a una profunditat de 100, necessitarem 10TB.
Si es troba a una profunditat de 1000, necessitarem 10PB.
Típicament, ens quedarem sense espai abans de quedar-nos sense temps.
Búsqueda en profunditat
L'estratègia de cerca en profunditat és similar a la de cerca en amplitud
Utilitza una pila (LIFO) en lloc d'una cua
Aquesta estratègia no garanteix trobar la solució òptima
L'algorisme arriva fins a una profunditat màxima i després retrocedeix fins a trobar un camí alternatiu
Implementació
defcerca_profunditat(estat_inicial):
"""Cerca en profunditat en un problema."""
frontera = collections.deque([estat_inicial])
while frontera:
estat = frontera.pop()
if es_solucio(estat):
return estat
for succesor in succesors(estat):
ifnot cicle(problema, succesor):
frontera.append(succesor)
Exemple: Botelles d'aigua (I)
Estat inicial: (0,0) - Estat final: (2,*) o (*, 2)
Funcio de succesió: Operacions de buidar, omplir i trasvasar
Cada node és un parell de valors (a,b) que representen l'estat de les botelles
La busca en profunditat explora l'arbre fins a trobar una solució
Si no troba una solució, torna enrere fins a trobar un camí alternatiu
Si les solucions son infinites, l'algorisme pot no acabar mai
Propietats
Propietat
Valor
Comentaris
Completitud
No
Si l'espai de cerca és finit, la solució es trobarà en algun moment
Optimalitat
No
La primera solució trobada no té perquè ser òptima
Complexitat temporal
On és el factor de ramificació i és la profunditat màxima de l'arbre. En valors d' molt grans, pot ser molt alta
Complexitat espacial
On és el factor de ramificació i és la profunditat màxima de l'arbre. És molt millor que la de la cerca en amplitud si no hi ha cicles; si hi ha cicles, és la mateixa que la de la cerca en amplitud
Quan utilitzar-la?
En la pràctica, la cerca en profunditat és molt més ràpida que la cerca en amplitud
La cerca en profunditat no necessita tant espai com la cerca en amplitud
La cerca en profunditat és molt útil quan:
El factor de ramificació és molt gran
La solució es troba a una profunditat molt baixa
No ens importa trobar la solució òptima
Verifiquem que no es creen cicles
Búsqueda en profundidad limitada
La cerca en profunditat limitada és una variant de la cerca en profunditat
En aquest cas, la cerca s'atura quan s'arriba a una profunditat màxima
Si la solució es troba a una profunditat , no es trobarà
La cerca en profunditat limitada és completa si és suficientment gran
Ens permet evitar el problema de la cerca en profunditat quan les solucions son infinites
Implementació
defcerca_profunditat_limitada(estat_inicial, l):
"""Cerca en profunditat limitada en un problema."""
frontera = collections.deque([estat_inicial])
while frontera:
estat = frontera.pop()
if es_solucio(estat):
return estat
for succesor in succesors(estat):
ifnot cicle(problema, succesor) and profunditat(succesor) < l:
frontera.append(succesor)
Búsqueda en profundidad iterativa
Solució al problema de la cerca en amplitud y la cerca en profunditat utilitzant una única estratègia
La cerca en profunditat iterativa és una cerca en profunditat limitada amb creixent
Comença amb i va incrementant fins a trobar la solució
Traçat de l'algorisme (I)
Traçat de l'algorisme (II)
Traçat de l'algorisme (III)
Implementació
defcerca_profunditat_iterativa(estat_inicial):
"""Cerca en profunditat iterativa en un problema."""
l = 0whileTrue:
solucio = cerca_profunditat_limitada(estat_inicial, l)
if solucio isnotNone:
return solucio
l += 1
Propietats
Propietat
Valor
Comentaris
Completitud
Sí
Si l'espai de cerca és finit, la solució es trobarà en algun moment
Optimalitat
Sí
La primera solució trobada serà òptima
Complexitat temporal i espacial
Com la de la cerca en profunditat
(com a màxim)
Búsqueda de cost uniforme
La cerca de cost uniforme és una variant de la cerca en amplitud
En aquest cas, la frontera s'ordena segons el cost del camí a cada estat (cua de prioritat)
Estats visitats: de manera iterativa, es van visitant tots els que tenen un cost menor que l'actual
Sí totes les accions tenen el mateix cost, la cerca de cost uniforme és equivalent a la cerca en amplitud
Exemple: Viatjar per Romania (I)
Estat inicial: Arad
Funcio de succesió: Carreteres.
Cost: Distància entre ciutats (en Km)
Comprovar si un estat és final: Estat = Bucharest
Solució: Seqüència de ciutats que ens porten d'Arad a Bucharest
Exemple: Viatjar per Romania (II)
Representació de l'arbre de cerca
Cada node és un parell de valors (a,b) que representen l'estat de les botelles
La busca en amplitud explora l'arbre per nivells
Podem observar que solament s'explora un nombre molt reduït de tots els possibles estats
Implementació
defcerca_cost_uniforme(estat_inicial):
"""Cerca de cost uniforme en un problema."""
frontera = priority_queue([(0, estat_inicial)])
visitats = set()
while frontera:
cost_actual, estat = frontera.pop()
visitats.add(estat)
if es_solucio(estat):
return estat
for cost, succesor in succesors(estat):
if succesor notin visitats:
frontera.append(cost + cost_actual, succesor)
Propietats
Propietat
Valor
Comentaris
Completitud
Sí
Si l'espai de cerca és finit, la solució es trobarà en algun moment
Optimalitat
Sí
La primera solució trobada serà òptima
Complexitat temporal i espacial
On és el factor de ramificació i és el cost de la solució òptima
Gestió de fronteres
La gestió de fronteres és un problema important en els algorismes de cerca
Els algorismes que hem vist son tots molt semblants, la diferència està en com gestionen la frontera
Conceptualment sempre es tracta d'una cua amb prioritat
En la pràctica, per a les busquedes en profunditat i amplada podem utilitzar una cua o una pila
Per estalviar-nos el sobrecost d' de la cua de prioritat
Podriem, fins i tot, programar una implementació on pugam variar l'objecte frontera.
Búsqueda informada
Definició
L'algorisme de búsqueda de cost uniforme és un algoritme molt eficient, té, però alguns problemes
Busca en totes les direccions, sense tenir en compte la direcció cap a la solució
Per tant, analitza més estats dels que seria estrictament necessari
En aquesta part de la unitat veurem técniques per solucionar aquestos problemes
Heurístiques
Una heurística és:
Una funció que ens permet estimar el cost d'arribar a la solució des d'un estat
Dissenyada per un problema concret
Heurístiques per rutes:
Distància en línia recta (euclidiana)
Distància manhattan
Exemple: Viatjar per Romania
Heurística: Distància en línia recta (euclidiana)
Búsqueda voraç
Si solament utilitzem la heurística per decidir quin estat de la frontera seguim:
Búsqueda voraç
Més eficient que la búsqueda de cost uniforme
No garanteix trobar la solució òptima
height:330px
Búsqueda voraç
En verd la ruta correcta i en roig la nostra
Que podem fer perqué el nostre algorisme trobi la solució correcta?
Implementació
defcerca_voraç(estat_inicial):
"""Cerca voraç en un problema."""
frontera = priority_queue([(0, estat_inicial)])
visitats = set()
while frontera:
cost_actual, estat = frontera.pop()
visitats.add(estat)
if es_solucio(estat):
return estat
for cost, succesor in succesors(estat):
if succesor notin visitats:
frontera.append(heuristica(succesor), succesor)
Propietats
Propietat
Valor
Comentaris
Completitud
Sí
Si l'espai de cerca és finit, trobarà una solució en algun moment
Optimalitat
No
La primera solució trobada no té perquè ser òptima
Complexitat temporal i espacial
On és el factor de ramificació i és la profunditat màxima de l'arbre
A*
L'algorisme A* és una combinació de la búsqueda de cost uniforme i la búsqueda voraç
La búsqueda de cost uniforme ordena pel cost del camí o cost cap enrere:
La búsqueda voraç ordena pel cost de la heurística o cost endavant:
L'algorisme A* ordena per la suma dels dos:
Garanteix trobar la solució òptima (si és admissible)
Exemple: Viatjar per Romania (I)
Exemple: Viatjar per Romania (II)
Exemple: Viatjar per Romania (III)
Implementació
defcerca_a_estrella(estat_inicial):
"""Cerca A* en un problema."""
frontera = priority_queue([(0, estat_inicial)])
visitats = set()
while frontera:
cost_actual, estat = frontera.pop()
visitats.add(estat)
if es_solucio(estat):
return estat
for cost, succesor in succesors(estat):
if succesor notin visitats:
cost_acumulat_h = cost + cost_actual + h(succesor)
frontera.append(cost_acumulat_h,
succesor)
Propietats
Propietat
Valor
Comentaris
Completitud
Sí
Optimalitat
Sí
Complexitat temporal i espacial
On és el factor de ramificació i és la profunditat de la solució
Condició: Aquestes propietats es compleixen si la heurística és admissible
Heurístiques admissibles (I)
Una heurística és admissible si:
No sobreestima el cost de la solució
És a dir, si el cost real de la solució és , la heurística és admissible si
Si la heurística NO és admissible:
L'algorisme A* és equivalent a la búsqueda voraç
Trobar una heurística admissible és un problema difícil.
Exemple: Puzzle 8 (I)
Técnica útil redüir el problema a un problema més senzill
Relaxació de les regles del joc
Permetre que les peces s'intercanviïn entre elles
Permetre que les peces es moguin a qualsevol posició, si està buida
Permetre que les peces es moguin a qualsevol posició, sense restriccions (1+2)
Exemple: Puzzle 8 (II)
La primera opció ens porta la heurística distància manhattan
Equival a un problema on hem de lliscar les peces fins a la seva posició.
Suma de les distàncies horitzontals i verticals de cada peça a la seva posició final
És admissible perquè no sobreestima el cost de la solució
La tercera opció ens porta la heurística nombre de peces fora de lloc
Equival a un problema on hem de deixar directament en la seva posició.
Suma de les peces que no estan a la seva posició final
És admissible perquè no sobreestima el cost de la solució
Propietat Óptima de les heurístiques admissibles (I)
Si és una ruta óptima fins a amb cost .
serà una ruta subòptima fins a amb cost , sent > .
serà una subpart de la ruta òptima desde la frontera
Es possible que agafem abans d'?.
No, perquè > i > , perquè la heurística és admissible
Així, > >
Les subrutes en la ruta òptima sempre seran més barates que en la ruta subòptima
Propietat Óptima de les heurístiques admissibles (II)
A* explora els nodes en ordre creixent de
Va agregant, de forma gradual, corves de nivell de grau
Cada corba de nivell representa un conjunt de nodes amb un valor d' inferior a un valor concret
Propietat Óptima de les heurístiques admissibles (III)
Si tenim dues heurístiques admissibles i , amb per a tots els estats
Llavors, és més informativa que
Per tant, serà més eficient que
Es per això que, preferirem l'heurística Manhattan a l'heurística de peces fora de lloc
Limitacions de l'algorisme A*
L'algorisme A* és òptim i una millora respecte a la búsqueda de cost uniforme
Però, l'algorisme A* té dues limitacions:
Espai de memòria: L'espai de memòria necessari pot ser molt gran
Temps d'execució: El temps d'execució pot ser molt gran
Per això, s'han desenvolupat variants de l'algorisme A* que intenten millorar aquestes limitacions
En aquesta unitat veurem dues:
A* de profunditat iterativa
A* ponderat
A* de profunditat iterativa
L'algorisme A* de profunditat iterativa és una variant de l'algorisme A*
Molt semblant a l'algorisme de profunditat iterativa
Utilitza la funció per tallar, en compte de la profunditat
Ens permet reduir l'espai de memòria necessari
A costa de tindre que visitar alguns nodes més d'una vegada
Implementació (I)
defcerca_a_limitada(estat_inicial, l):
"""Cerca A* limitada en un problema."""
frontera = priority_queue([(0, estat_inicial)])
visitats = set()
while frontera:
cost_actual, estat = frontera.pop()
visitats.add(estat)
if es_solucio(estat):
return estat
for cost, succesor in succesors(estat):
if succesor notin visitats and cost_acumulat_h < l:
cost_acumulat_h = cost_actual + cost + h(succesor)
frontera.append(cost_acumulat_h, succesor)
Implementació (II)
defcerca_a_iterativa(estat_inicial):
"""Cerca A* iterativa en un problema."""
l = 0whileTrue:
solucio = cerca_a_limitada(estat_inicial, l)
if solucio isnotNone:
return solucio
l += 1
A* Ponderat
Definició
L'algorisme A* ponderat és una variant de l'algorisme A*
Es defineix un factor de ponderació que determina el pes de la heurística
L'algorisme A* ponderat ordena per
Si , l'algorisme A* ponderat és equivalent a l'algorisme A*
Si , l'algorisme A* ponderat és s'apropa a la búsqueda voraç
Utilitat
L'algorisme A* ponderat és útil per:
Reduir el cost de l'espai de memòria
Reduir el cost de l'espai de temps
A costa d'una solució no tan òptima
En l'exemple de la dreta en una (la b)
S'estudien 7 vegades menys estats
Per una solució un menys eficient
Implementació
defcerca_a_ponderat(estat_inicial, epsilon):
"""Cerca A* ponderat en un problema."""
frontera = priority_queue([(0, estat_inicial)])
visitats = set()
while frontera:
cost_actual, estat = frontera.pop()
visitats.add(estat)
if es_solucio(estat):
return estat
for cost, succesor in succesors(estat):
if succesor notin visitats:
cost_acumulat_h = cost_actual + cost + h(succesor) * epsilon
frontera.append(cost_acumulat_h, succesor)
Anytime A*
Podem aprofitar l'algorisme A* ponderat per construir un algorisme Anytime A*
Busquem el camí òptim amb un gran
Anem reduint fins a que
Així, obtenim una bona solució en un temps raonable
Si tenim temps, podem seguir buscant una solució millor, fins arribar a la solució òptima