====== Laborator 13: Best-first search și A* ======
=====1. Introducere - Best-First Search=====
====1.1 Noţiuni introductive====
===Definiţie BFS(Best First)===
În sensul cel mai general, putem spune că un algoritm de căutare este de tipul Best-First dacă păstrează o evidenţă cu **candidaţii** şi alege să verifice mereu candidaţii cu potenţial mai mare înainte de a-i verifica pe cei cu potenţial mai mic(cu alte cuvinte, **verifică întâi candidatul cel mai bun - cu cel mai mare potenţial**).
===Cum definim potenţialul?===
*Trebuie să atribuim un "**potenţial de rezultat**" fiecărui candidat, adică să găsim o metodă cu care să decidem dacă un candidat prezintă mai mult interes decât altul, dacă avem aşteptări mai mari de a găsi rezultatul dorit alegând acest candidat.
*În multe cazuri, este mai ușor să măsurăm cât de "rău" este un candidat. Funcţia de "**cost potenţial**" ne ajută să descriem **potenţialul de rezultat**. Cu cât este mai mare costul la care ne aşteptăm de la un candidat, cu atât este mai mic potenţialul lui de a ne duce la rezultatul căutat. Cu alte cuvinte, putem spune că **cel mai bun** candidat este **cel mai puţin costisitor** candidat.
*După ce definim o funcţie de acest fel, putem introduce candidaţii într-o coadă prioritară(**priority queue**) în funcţie de costul aşteptat în aşa fel încât **primul** candidat din coadă să fie **cel mai bun** candidat.
====1.2 Greedy vs. Dijkstra====
Folosind noţiunile de mai sus, încercăm să identificăm funcţiile de cost potenţial pentru următoarea problemă: găsirea unui drum de cost minim de la un nod sursă(S) la un nod destinaţie(D) într-un graf.
*Candidaţii = nodurile din graf care urmează să fie incluse în soluţie
*Un candidat face parte din soluţie dacă drumul căutat trece prin el
*Nodurile S şi D au **costul potenţial = 0**, dar considerăm nodul D "nedescoperit" iniţial şi plecăm din nodul S
*Toate celelalte noduri au iniţial **costul potenţial infinit**
*Presupunem că ne aflăm la nodul curent(C) extras de la începutul cozii şi evaluăm nodurile vecine ale acestuia
===Greedy===
Definim funcţia de **cost potenţial** h(N) = posibilul cost din nodul curent(C), prin nodul vecin(N), până la nodul destinaţie(D)
Metoda Greedy construieşte soluţia alegând mereu **optimul local**, cu alte cuvinte
*h(C) = 0, pentru că deja am inclus nodul C în soluţia parţială
*h(N) = costul muchiei (C,N), pentru orice nod N care **nu este deja** în soluţia parţială
===Dijkstra===
Definim funcţia de **cost potenţial** g(N) = costul minim al drumului din punctul iniţial(S) până în nodul N
Algoritmul lui Dijkstra actualizează costul minim al drumului de la S la fiecare punct pe măsură ce explorează graful. Presupunem că actualizăm, în acelaşi timp, şi **costul potenţial**.
===Observaţii===
*În general, nu este **necesar** şi nici **suficient** ca funcţia de cost potenţial a unui nod să fie 0 pentru ca acel nod să facă parte din soluţia finală.
*Metoda Greedy poate duce la funcţia g(N) = 0 pentru noduri care par bune pe moment, dar nu se poate ajunge la D fără revenirea în nodul C(căutarea unui nou optim local după ce acesta a eşuat complet).
*La algoritmul lui Dijkstra, în afară de nodurile S şi D, celelalte noduri au cost potenţial nenul(excepţie dacă există muchii de cost 0).
Costul potenţial are rol doar în etapa de explorare a grafului. Pentru extragerea nodurilor ce formează drumul de cost minim, **nu** interpretăm costul potenţial. Este mult mai uşor să reţinem, într-un **vector de părinţi**, nodul "curent" din care am ajuns la nodul "următor", în momentul în care actualizăm costul potenţial al nodului următor.
===Performanţe===
*Metoda Greedy obţine **foarte repede** un rezultat(un drum de la S la D), dar, în multe cazuri, acesta **nu este cel mai scurt**(de cost minim).
*Algoritmul lui Dijkstra cu coadă prioritară are nevoie de **mai mult timp** pentru a obţine un rezultat, dar este **garantat** că, în momentul în care descoperă nodul D, a **găsit cel mai scurt drum** şi algoritmul se poate opri.
===Propunere===
*Putem găsi o cale de mijloc care să fie mai rapidă decât algoritmul lui Dijkstra şi care să ducă la soluţia dorită?
*Când putem defini o funcţie f(N) care să fie o combinaţie a funcţiilor g(N) şi h(N)) care să ducă la un algoritm mai bun?
====1.3 Algoritmul A*====
Algoritmul A* este definit, în sens general, ca fiind un algoritm de căutare de tip BFS(Best-First) ce foloseşte funcţia de **cost potenţial**
f(N) = g(N) + h(N), unde
g(N) = costul minim pentru un drum de la S la N
h(N) = estimarea pentru costului minim pentru un drum de la N la D
Definirea funcţiei g nu ridică probleme(putem folosi aceeaşi funcţie ca la algoritmul lui Dijkstra), dar, din punct de vedere al funcţiei h, numită funcţie **euristică**, distingem câteva cazuri:
*dacă funcţia h(N) = 0 peste tot, algoritmul coincide cu Dijkstra cu coadă prioritară -> soluţie optimă garantată, dar lent
*dacă funcţia h(N) < costul minim al drumului de la N la D, oricare ar fi N -> soluţie optimă garantată, mai rapid
*dacă funcţia **h(N) = costul minim al drumului de la N la D**, oricare ar fi N -> **cel mai rapid algoritm cu soluţia optimă garantată**
*dacă funcţia h(N) > costul minim al drumului de la N la D(**chiar şi pentru câteva** noduri N) -> optimul **nu** e garantat, dar e şi mai rapid
*dacă funcţia h(N) domină, atunci g(N) nu mai are efect, iar algoritmul A* devine Greedy Best-First Search.
===Concluzie===
O estimare iniţială bună creşte performanţa algoritmului şi păstrează rezultatul optim.
Este uşor să alegem o funcţie euristică bună atunci când avem **informaţii suplimentare** despre graf. Dacă ştim că există nişte **proprietăţi particulare**, ne putem folosi de ele.
=====2. Exemple=====
====Problema taxiului====
Presupunem următoarul caz:
*într-un oraş, oricare două străzi sunt **paralele** sau **perpendiculare** între ele;
*orice zonă mărginită de străzi are formă pătrată de latură = Lat(metri), iar pe colţurile **pătratului** sunt **intersecţii**;
*unele intersecţii sunt **blocate** şi ştim care sunt acestea
*se dau intersecţiile S şi D
===Cerinţa===
Dacă putem reduce orice intersecţie la un **punct**, găsiţi drumul cel mai scurt pe care poate merge un taxi din S în D(fără a trece prin intersecţiile blocate)
===Rezolvare===
*Putem reprezenta intersecţiile cu o matrice.
*Ne putem deplasa doar pe **orizontală** sau pe **verticală**, deci, în cel mai bun caz, lungimea drumului minim de la un punct oarecare N la D poate fi exprimată in funcţie de coordonatele intersecţiilor
*Fie (Nx, Ny) = poziţia lui N, fie (Dx, Dy) = poziţia lui D şi fie dMin(N, D) = lungimea drumului minim de la N la D.
*Atunci, **în cel mai bun caz**, **dMin(N, D) = |Nx - Dx| + |Ny - Dy|**
*Putem defini h(N) = dMin(N, D) ca funcţie euristică pentru acest caz