C/C++: Algoritmi divide et impera

Postat în Algoritmi, C/C++ , pe data 18 January 2009

1 Tehnica divide et impera

Divide
et impera
este o tehnica de elaborare a
algoritmilor care consta in:

    Descompunerea cazului ce trebuie
rezolvat intr-un numar de subcazuri mai mici ale aceleiasi probleme.
    Rezolvarea succesiva si independenta
a fiecaruia din aceste subcazuri.
    Recompunerea subsolutiilor astfel
obtinute pentru a gasi solutia cazului initial.

Sa presupunem ca avem un algoritm A cu timp patratic. Fie c o
constanta, astfel incat timpul pentru a rezolva un caz de marime n este
tA(n cn2.
Sa presupunem ca este posibil sa rezolvam un astfel de caz prin descompunerea
in trei subcazuri, fiecare de marime n/2.
Fie d o constanta, astfel incat timpul necesar pentru descompunere si
recompunere este t(n dn.
Folosind vechiul algoritm si ideea de descompunere-recompunere a subcazurilor,
obtinem un nou algoritm B, pentru care:

tB(n) = 3tA(n/2)+t(n 3c((n+1)/2)2+dn = 3/4cn2+(3/2+d)n+3/4c

Termenul 3/4cn2 domina pe ceilalti cand n
este suficient de mare, ceea ce inseamna ca algoritmul B este in esenta cu 25% mai rapid decat algoritmul A. Nu am reusit insa sa schimbam ordinul
timpului, care ramane patratic.

Putem sa continuam in mod recursiv acest
procedeu, impartind subcazurile in subsubcazuri etc. Pentru subcazurile care nu
sunt mai mari decat un anumit prag n0, vom folosi
tot algoritmul A. Obtinem astfel
algoritmul C, cu timpul

Conform rezultatelor din Sectiunea 5.3.5, tC(n)
este in ordinul lui nlg 3. Deoarece lg 3 @ 1,59,
inseamna ca de aceasta data am reusit sa imbunatatim ordinul timpului.

Iata o descriere generala a metodei divide
et impera:

function divimp(x)
{returneaza o solutie pentru cazul x}
if
x este suficient de mic then return adhoc(x)
{descompune x in subcazurile x1, x2, , xk}
for i 1 to k do
yi divimp(xi)
{recompune y1, y2, , yk in scopul obtinerii solutiei y
pentru x}
return
y

unde adhoc
este subalgoritmul de baza folosit pentru rezolvarea micilor subcazuri ale
problemei in cauza (in exemplul nostru, acest subalgoritm este A).

Un algoritm divide et impera trebuie sa
evite descompunerea recursiva a subcazurilor suficient de mici, deoarece,
pentru acestea, este mai eficienta aplicarea directa a subalgoritmului de baza.
Ce inseamna insa suficient de mic?

In exemplul precedent, cu toate ca valoarea
lui n0 nu influenteaza ordinul timpului,
este influentata insa constanta multiplicativa a lui nlg 3, ceea ce poate avea un rol considerabil in eficienta algoritmului.
Pentru un algoritm divide et impera oarecare, chiar daca ordinul timpului nu
poate fi imbunatatit, se doreste optimizarea acestui prag in sensul obtinerii
unui algoritm cat mai eficient. Nu exista o metoda teoretica generala pentru
aceasta, pragul optim depinzand nu numai de algoritmul in cauza, dar si de
particularitatea implementarii. Considerand o implementare data, pragul optim
poate fi determinat empiric, prin masurarea timpului de executie pentru
diferite valori ale lui n0 si cazuri de
marimi diferite.

In general, se recomanda o metoda hibrida
care consta in: i) determinarea
teoretica a formei ecuatiilor recurente; ii)
gasirea empirica a valorilor constantelor folosite de aceste ecuatii, in
functie de implementare.

Revenind la exemplul nostru, pragul optim
poate fi gasit rezolvand ecuatia

tA(n) = 3tA(n/2+ t(n)

Empiric, gasim n@ 67, adica valoarea pentru care nu mai are importanta daca
aplicam algoritmul A in mod direct,
sau daca continuam descompunerea. Cu alte cuvinte, atata timp cat subcazurile
sunt mai mari decat n0, este bine sa
continuam descompunerea. Daca continuam insa descompunerea pentru subcazurile
mai mici decat n0, eficienta
algoritmului scade.

Observam ca metoda divide et impera este
prin definitie recursiva. Uneori este posibil sa eliminam recursivitatea printr-un
ciclu iterativ. Implementata pe o masina conventionala, versiunea iterativa poate fi ceva mai rapida (in limitele
unei constante multiplicative). Un alt avantaj al versiunii iterative ar fi
faptul ca economiseste spatiul de memorie. Versiunea recursiva foloseste o
stiva necesara memorarii apelurilor recursive. Pentru un caz de marime n, numarul apelurilor recursive este de
multe ori in W(log n),
uneori chiar in W(n).

Pagini: 1 2 3 4 5 6 7 8 9 10

Lasă un comentariu, un sfat sau o întrebare: