Sunteți pe pagina 1din 12

Capitolul 2 METODE DE CUTARE I PROGRAMARE

2.1.

Cutarea n lime

n teoria grafurilor, breadth-first search (BFS) este un algoritm de cutare n grafuri, care ncepe cu vrful rdcin i exploreaz toate nodurile vecine. Apoi, pentru fiecare dintre aceste noduri se exploreaz nodurile vecine nc necercetate, .a.m.d.., pn cnd scopul a fost atins. BFS este o metod de cutare, care intete extinderea i examinarea tuturor nodurilor unui graf, cu scopul de a gsi soluia. Din punct de vedere al algoritmului, toate nodurile fii obinute prin expansiunea unui nod sunt adugate ntr-o coad de tipul FIFO (First In First Out). n implementrile tipice, nodurile care nu au fost nc examinate de ctre vecinii corespunztori sunt plasate ntr-un recipient (asemntor unei cozi sau unei liste de legtur), numit deschis, iar odat examinai sunt plasai n recipientul nchis.

2.1.1. Algoritmul
1. Introducerea nodului rdcin n coad. 2. Extragerea unui nod din captul listei i examinarea acestuia. Dac elementul cutat se identific cu acest nod, se renun la cutare i se returneaz rezultatul. Altfel, se plaseaz toi succesorii (nodurile fii) (neexaminai nc) acestui nod la sfritul cozii (acesta n cazul n care exist) 3. Dac coada este goal, fiecare nod al grafului a fost examinat se renun la cutare i se ntoarce la not found. 4. Repet ncepnd cu Pasul 2.

2.1.2. Implementarea C++


n continuare este implementarea algoritmului de mai sus, unde neexaminaii pn n momentul de fa sunt gestionai de ctre tabloul printe. Fie structura struct i structura de noduri
struct Vertex { ... std::vector<int> out; ... };

std::vector<Vertex> graph(vertices); bool BFS(const std::vector<Vertex>& graph, int start, int end) { std::queue<int> next; std::vector<int> parent(graph.size(), 0); parent[start] = -1; next.push(start); while (!next.empty()) { int u = next.front(); next.pop(); if (u == end) return true; for (std::vector<int>::const_iteratorj=graph[u].out.begin(); j != graph[u].out.end(); ++j) { // Look through neighbors. int v = *j; if (parent[v] == 0) { // If v is unvisited. parent[v] = u; next.push(v); } } } return false; }

Sunt stocai prinii fiecrui nod, de unde se poate deduce drumul.

2.1.3. Complexitate i optimalitate


Complexitate n spaiu. Avnd n vedere faptul c toate nodurile descoperite pn la momentul de fa trebuiesc salvate, complexitatea n spaiu a breadth-first search este O(V + E ) , unde V reprezint numrul nodurilor, iar E numrul muchiilor grafului. Alt modalitate de a consemna acest lucru: complexitate O B M , unde B reprezint cea mai lunga ramur, iar M lungimea maxim a drumului arborelui. Aceast cerere imens de spaiu este motivul pentru care breadth-first search nu este practic n cazul problemelor mai ample. Complexitatea n timp. Odat ce, n cel mai ru caz breadth-first search trebuie s ia n considerare toate drumurile ctre toate nodurile, complexitatea n timp a acestui tip de cutare este de O(| V | + | E |) . Cel mai bun caz n aceast cutare este conferit de complexitatea O(1) . Are loc atunci cnd nodul este gsit la prima parcurgere. Completitudine. Metoda breadth-first search este complet. Aceast nseamn c dac exist o soluie , metoda breadth-first search o va gsi, indiferent de tipul grafului. Cu toate acestea, dac graful este infinit i nu exist nici o soluie, breadth-first search va eua.

( )

10

Optimalitate. Pentru costul unitar pe muchii, bread-first search este o metod optim. n general, breadth-first search nu este o metod optim, i aceasta deoarece returneaz ntotdeauna rezultatul cu cele mai puine muchii ntre nodul de start i nodul vizat. Dac graful este un graf ponderat, i drept urmare are costuri asociate fiecrei etape, aceast problem se rezolv mbuntind metoda breadth-first search astfel nct s se uniformizeze costurile de cutare, identificate cu: costurile drumului. Totui, dac graful nu este ponderat, i prin urmare toate costurile etapelor sunt egale, breadth-first search va identifica cea mai apropiat i optim soluie.

2.1.4 Aplicaii ale BFS


Breadth-first search poate fi folosit pentru rezolvarea unei game variate de probleme de teoria grafurilor, printre care: Gsirea tuturor componentelor conexe dintr-un graf. Identificarea tuturor nodurilor ntr-o component conex. Gsirea celui mai scurt drum ntre nodurile u i v (ntr-un graf neponderat). Testarea bipartiiei unui graf. Gsirea Componentelor Conexe Mulimea vrfurilor accesate prin metode BFS reprezint cea mai mare component conex care conine vrful de start. Testarea bipartiiei BFS poate fi folosit pentru testarea bipartiiei, ncepnd cutarea cu orice vrf i atribuind etichete alternative vrfurilor vizitate n timpul cutrii. Astfel, se atribuie eticheta 0 vrfului de start, 1 tuturor vecinilor si, 0 vecinilor acelor vecini, i aa mai departe. Dac ntr-un anumit moment al procesului un vrf are vecini vizitai cu aceeai etichet, atunci graful nu este bipartit. Dac parcurgerea se sfrete fr a se produce o astfel de situaie, atunci graful este bipartit.

2.2.

Cutarea n adncime

Depth-first search (DFS) este un algoritm cutare a arborelui, structurii arborelui, sau a grafului. Formal, DFS reprezint o cutare care evolueaz prin expansiunea la primul vrf fiu a arborelui ce ia natere pe msur ce se coboar n adncime, pn n momentul n care vrful int este descoperit sau pn cnd se ntlnete un vrf care nu are fii. La pasul urmtor, cutarea se reia (backtracking), revenind la nodul cel mai recent vizitat, ns pentru care explorarea nu este ncheiat. ntr-o implementare nerecursiv, toate vrfurile recent vizitate sunt adugate ntr-o stiv de tipul LIFO (Last In First Out), n scopul explorrii acestora. Complexitatea n spaiu a DFS este cu mult mai mic dect cea a BFS (Breadth-First Search). De asemenea se preteaz mult mai bine metodelor euristice de alegere a
11

ramurilor asemntoare. Complexitatea n timp a ambilor algoritmi este proporional cu numrul vrfurilor plus numrul muchiilor grafului corespunztor (O(V + E )) . Cutarea n adncime se poate folosi i la ordonarea liniar a vrfurilor grafului (sau arborelui). Exist trei astfel de posibiliti: O preordine reprezint o listare a vrfurilor n ordinea n care au fost vizitai prin intermediul algoritmului cutrii n adncime. Aceasta este o modalitate natural i compact de descriere a progresului cutrii. O preordine a unei expresii arbore este ceea ce numim expresie n notaia Polonez. O postordine reprezint o listare n care cel din urm vrf vizitat este primul element al listei. O postordine a unui expresii arbore este de fapt expresia n oglind a expresiei n notaie Polonez. O postordine inversat (n oglind) este, de fapt, reversul postordinii, i.e. o listare a vrfurilor n ordinea invers a celei mai recente vizite a vrfurilor in cauz. n cutarea unui arbore, postordinea inversat coincide cu preordinea, ns, n general, difer atunci cnd se caut un graf. Spre exemplu cnd se caut graful:

ncepnd cu vrful A, preordinile posibile sunt A B D C, respectiv A C D B (n funcie de alegerea algoritmului de a vizita mai nti vrful B sau vrful C), n timp ce postordinile inversate (n oglind) sunt: A B C D i A C B D. Postordinea inversat produce o sortare topologic a oricrui graf orientat aciclic. Aceast ordonare este folositore i n analiza fluxului de control, reprezentnd adesea o liniarizare natural a fluxului de control. Graful mai sus amintit poate reprezenta fluxul de control ntr-un fragment de cod ca cel de mai jos:
if (A) then { B } else { C } D

i este natural s considerm c acest cod urmeaz ordinea A B C D sau A C B D, ns nu este normal s urmeze ordinea A B D C sau A C D B.
12

PSEUDOCOD (recursiv) dfs(v) process(v) mark v as visited for all vertices i adjacent to v not visited dfs(i)

O alt variant
dfs(graph G) { list L = empty tree T = empty choose a starting vertex x search(x) while(L is not empty) { remove edge (v, w) from beginning of L if w not yet visited { add (v, w) to T search(w) } } } search(vertex v) { visit v for each edge (v, w) add edge (v, w) to the beginning of L }

Aplicaii Iat civa algoritmi n care se folosete DFS: Gsirea componentelor conexe. Sortarea topologic. Gsirea componentelor tare conexe.

2.3.
optim.

Metoda Greedy
Descrierea metodei Greedy Metoda Greedy (greedy = lacom) este aplicabil problemelor de Considerm mulimea finit A = {a1 ,..., a n }

i o proprietate p definit pe mulimea submulimilor lui A: / p(0) = 1 p : P(A) {0,1} cu p(X) = 1 p(Y) = 1, Y X O submulime S A se numete soluie dac p(S) = 1.

13

Dintre soluii va fi aleas una care optimizeaz o funcie de cost p : P(A) R dat. Metoda urmrete evitarea cutrii tuturor submulimilor (ceea ce ar necesita un timp de calcul exponenial), mergndu-se "direct" spre soluia optim. Nu este ns garantat obinerea unei soluii optime; de aceea aplicarea metodei Greedy trebuie nsoit neaprat de o demonstraie. Distingem doua variante generale de aplicare a metodei Greedy: Prima variant alege n mod repetat cte un element oarecare al mulimii A i l adaug soluiei curente S numai dac n acest mod se obine tot o soluie. n a doua variant procedura prel realizeaz o permutare a elementelor lui A, dup care elementele lui A sunt analizate n ordine i adugate soluiei curente S numai dac n acest mod se obine tot o soluie. Exemplu. Se consider mulimea de valori reale A = {a1 ,..., a n } . Se caut submulimea a crei sum a elementelor este maxim. Vom parcurge mulimea i vom selecta numai elementele pozitive, care vor fi plasate n vectorul soluie s.
k0 for i = 1,n if ai > 0 then k k + 1; sk ai write(s)

2.4.

Metoda backtracking

Un algoritm este considerat "acceptabil" numai dac timpul su de executare este polinomial, adic de ordinul O(nk) pentru un anumit k; n reprezint numrul datelor de intrare. Pentru a ne convinge de acest lucru, vom considera un calculator capabil s efectueze un milion de operaii pe secund. n tabelul urmtor apar timpii necesari pentru a efectua n3, 2n i 3n operaii, pentru diferite valori mici ale lui n: n = 20 n3 2n 1 sec 3n 58 min n = 40 12,7 zile 3855 secole n = 60 0,2 sec 366 secole 1013 secole

Tabelul de mai sus arat c algoritmii exponeniali nu sunt acceptabili.

14

unde : X {0,1} este o proprietate definit pe X. Din cele de mai sus rezult c generarea tuturor elementelor produsului cartezian X nu este acceptabil. Metoda backtracking ncearc micorarea timpului de calcul. X este numit spaiul soluiilor posibile, iar sintetizeaz condiiile interne. Vectorul X este construit progresiv, ncepnd cu prima component. Nu se trece la atribuirea unei valori lui x, dect dac am stabilit valori pentru x1 ,..., x k 1 i k 1 (x1 ,..., x k 1 ) = 1 . Funciile k : X1 ... X n {0,1} se

Descrierea metodei Backtracking Fie produsul cartezian X = X1 ... X n . Cutam x X cu (x) = 1 ,

numesc condiii de continuare i sunt de obicei restriciile lui la primele k variabile. Condiiile de continuare sunt strict necesare, ideal fiind s fie i suficiente. Distingem urmtoarele cazuri posibile la alegerea lui xk: 1) "Atribuie i avanseaz": mai sunt valori neanalizate din Xk i valoarea xk aleas satisface k=> se mrete k. 2) "ncercare euat": mai sunt valori neconsumate din Xk i valoarea xk aleas dintre acestea nu satisface k=> se va relua, ncercndu-se alegerea unei noi valori pentru xk. 3) "Revenire": nu mai exist valori neconsumate din Xk (Xk epuizat) ntreaga Xk devine disponibil i k <- k-1. "Revenire dup determinarea unei soluii": este reinut soluia. 4) Reinerea unei soluii const n apelarea unei proceduri retsol care prelucreaz soluia i fie oprete procesul (dac se dorete o singur soluie), fie prevede k k-1 (dac dorim s determinm toate soluiile). Notm prin Ck X k mulimea valorilor consumate din Xk. Algoritmul este urmtorul:

Ci<- 0, i; k<-l; while k > 0 if k = n+1 then retsol (x); k<- k-1; else if C X k k then alege v X \ C ; C C {v} ; k k k k if (x ,..., x k 1 k 1 , v) = 1 then x v ; k<- k+l; k else / else Ck<- 0 ; k<- k-l;

15

Pentru cazul particular simplific


k<-l;

X1 = ... = X n = {1,...,s} , algoritmul se

x i <-0,

i = 1, , n;

while k > 0 if k = n+1 then retsol (x); k<-k-l; else if x < s k then x x + 1 ; k k if (x ,..., x ) = 1 k 1 k then k<- k+l; else else x <-0; k<-k-l; k

2.5.

Metoda divide et impera

Metoda Divide et Impera ("desparte i stpnete") consta n mprirea repetat a unei probleme de dimensiuni mari n mai multe subprobleme de acelai tip, urmat de rezolvarea acestora i combinarea rezultatelor obinute pentru a determina rezultatul corespunztor problemei iniiale. Pentru fiecare subproblem procedm n acelai mod, cu excepia cazului n care dimensiunea ei este suficient de mic pentru a fi rezolvat direct. Este evident caracterul recursiv al acestei metode. Schema general Descriem schema general pentru cazul n care aplicm metoda pentru o prelucrare oarecare asupra elementelor unui vector. Funcia DivImp, care ntoarce rezultatul prelucrrii asupra unei subsecvene a p ,..., a u , va fi apelat prin DivImp (1,n).
function DivImp(p,u) if u-p < then r <- Prel (p, u) else m <- Interm (p,u); r1 <- DivImp (p,m); r2 <- DivImp (m+1,u); r <- Combin (r1,r2) return r end;

unde:

p + u - funcia Interm ntoarce un indice n intervalul p..u; de obicei m = ; 2 - funcia Prel ntoarce rezultatul subsecvenei p .. u, dac aceasta este suficient de mic; - funcia Combin ntoarce rezultatul asamblrii rezultatelor pariale r1 i r2.
16

2.6.

Metoda Branch and Bound

Prezentare general Metoda Branch and Bound se aplic problemelor care pot fi reprezentate pe un arbore: se ncepe prin a lua una dintre mai multe decizii posibile, dup care suntem pui n situaia de a alege din nou dintre mai multe decizii; vom alege una dintre ele etc. Vrfurile arborelui corespund strilor posibile n dezvoltarea soluiei. Deosebim dou tipuri de probleme: 1) Se caut un anumit vrf, numit vrf rezultat, care nu are descendeni. 2) Exist mai multe vrfuri finale, care reprezint soluii posibile, dintre care cutm de exemplu pe cel care minimizeaz o anumit funcie. Dei metoda este aplicabil pe arbori. Exist multe deosebiri, dintre care menionm: - ordinea de parcurgere a arborelui; - modul n care sunt eliminai subarborii care nu pot conduce la o soluie; - faptul ca arborele poate fi infinit (prin natura sa sau prin faptul c mai multe vrfuri pot corespunde la o aceeai stare). n general arborele de stri este construit dinamic. Este folosit o list L de vrfuri active, adic de stri care sunt susceptibile de a fi dezvoltate pentru a ajunge la soluie / soluii. Iniial, lista L conine rdcina arborelui, care este vrful curent. La fiecare pas, din L alegem un vrf (care nu este neaprat un fiu al vrfului curent!), care devine noul vrf curent. Cnd un vrf activ devine vrf curent, sunt generai toi fiii si, care devin vrfuri active (sunt inclui n L). Apoi din nou este selectat un vrf curent. Legat de modul prin care alegem un vrf activ drept vrf curent, deci implicit legat de modul de parcurgere a arborelui, facem urmtoarele remarci: - cutarea n adncime nu este adecvat, deoarece pe de o parte arborele poate fi infinit, iar pe de alt parte soluia cutat poate fi de exemplu un fiu al rdcinii diferit de primul fiu i cutarea n adncime ar fi ineficient: se parcurg inutil stri, n loc de a avansa direct spre soluie; - cutarea pe lime conduce totdeauna la soluie (dac aceasta exist), dar poate fi ineficient dac vrfurile au muli fii. Metoda Branch and Bound ncearc un "compromis" ntre cele dou cutri menionate mai sus, atand vrfurilor active cate un cost pozitiv, ce intenioneaz sa fie o msur a gradului de "apropiere" a vrfului de o soluie. Alegerea acestui cost este decisiv pentru a obine un timp de executare ct mai bun i depinde de problema concret, dar i de abilitatea
17

programatorului. Costul unui vrf va fi totdeauna mai mic dect cel al descendenilor (fiilor) si. De fiecare dat drept vrf curent este ales cel de cost minim (cel considerat ca fiind cel mai "aproape" de soluie). Din analiza teoretic a problemei deducem o valoare lim care este o aproximaie prin adaos a minimului cutat: atunci cnd costul unui vrf depaseste lim, vrful curent este ignorat: nu este luat n considerare i deci este eliminat ntregul subarbore pentru care este rdcin. Dac nu cunoatem o astfel de valoare lim, o iniializm cu + . Se poate defini o funcie de cost ideal, pentru care c(x) este dat de: nivelul pe care se afl vrful x dac x este vrf rezultat; + dac x este vrf final, diferit de vrf rezultat; min {c(y) | y fiu al lui x} dac x nu este vrf final. Aceast funcie este ideal din dou puncte de vedere: nu poate fi calculat dac arborele este infinit; n plus, chiar dac arborele este finit, el trebuie parcurs n ntregime, ceea ce este exact ce dorim s evitm; dac totui am cunoate aceast funcie, soluia poate fi determinat imediat: plecm din rdcin i coborm mereu spre un vrf cu acelai cost, pn ajungem n vrful rezultat. Neputnd lucra cu funcia ideal de mai sus, vom alege o aproximaie d a lui c, care trebuie s satisfac condiiile: 1) n continuare, dac y este fiu al lui x avem d(x) < d(y); 2) d(x) s poat fi calculat doar pe baza informailor din drumul de la rdcin la x; 3) este indicat ca d c pentru a ne asigura c dac d(x ) > lim, atunci i c(x) > lim, deci x nu va mai fi dezvoltat. O prim modalitate de a asigura compromisul ntre cutrile n adncime i pe lime este de a alege funcia d astfel nct, pentru o valoare natural k, s fie ndeplinit condiia: pentru orice vrf x situat pe un nivel nx i orice vrf situat pe un nivel ny nx + k, s avem d(x) > d(y), indiferent dac y este sau nu descendent al lui x. Condiia de mai sus spune c niciodat nu poate deveni activ un vrf aflat pe un nivel ny nx + k dac n L apare un vrf situat pe nivelul nx, adic nu putem merge "prea mult" n adncime. Dac aceasta condiie este ndeplinit, este valabil urmtoarea propoziie: Propoziie. n ipoteza c este ndeplinit condiia de mai sus i dac exist soluie, ea va fi atins ntr-un timp finit, chiar dac arborele este infinit.
18

Algoritmul Branch & Bound pentru probleme de optim S presupunem c dorim s determinm vrful final de cost minim i drumul de la rdcin la el. Fie lim aproximarea prin adaos considerat mai sus. Algoritmul este urmtorul (rad este rdcina arborelui, iar ifinal este vrful rezultat):
i<-rad; L<={i}; min<-lim; calculm d(rad); tata(i)<-0 while L i <= L {este scos vrful i cu d(i) minim din min-ansamblul L} for toi j fii ai lui i calculm d(j); calcule locale asupra lui j; tata(j)<-i if j este vrf final then if d(j)< min then min<-d(j); ifinal<-j elimin din L vrfurile k cu d(k) min (*) else if d(j)<min then j => L if min =lim then write {'Nu exist soluie') else writeln (min); i<-ifinal while i 0 write{i); i<-tata{i)

La (*) am inut cont de faptul c dac j este descendent al lui i, atunci d(i ) < d(j).

19

20

S-ar putea să vă placă și