Sunteți pe pagina 1din 17

Tema 5.

METODЕ GENERALЕ DE CONSTRUIRE A


ALGORITMILOR: METODA DIVIDE ET IMPERA Metode de
construire a algoritmilor –oferă metodе generalе de rezolvare unei clase de
probleme:
⮚metoda backtracking;
⮚ metoda divide et impera;
⮚ metoda greedy;
⮚ metoda programării dinamice.
Fiecare dintre aceste metode de construire a algoritmilor se poate folosi pentru
anumite clase de probleme, iar în cazul în care – pentru aceeaşi clasă de probleme
– se pot folosi mai multe metode de construire a algoritmilor, criteriul de alegere
va fi eficienţa algoritmului.
METODA DIVIDE ET IMPERA
Metoda divide et impera se poate folosi pentru problemele care pot fi descompuse în
subprobleme similare cu problema iniţială (care se rezolvă prin aceeaşi metodă) şi care
prelucrează mulţimi de date de dimensiuni mai mici, independente unele de altele (care
folosesc mulţimi de date de intrare disjuncte).

Ideea principală a algoritmului DIVIDE ET IMPERA este să împărțiți problema în


subprobleme și să le rezolvata.
Unde se aplică
Căutare binară.
Merge sort.
Sortare rapidă.
Înmulțirea matricei prin metoda
Strassen. Algoritmul lui Karatsuba.
Exemplu 1. Căutare unui număr maxim din masiv nesortat
12 15 21 -35 9 49 31 55 89 13

Comparare metodei
N Сăutare liniară Metoda „Divide et Impera“
(cel mai rău caz) O(log2n)
О(n)
10 О(10)=10 O(log210)=3.32
100 О(100)=100 O(log2100)=6.64
1 000 О(1000)=1000 O(log21000)=9.96
10 000 О(10000)=10000 O(log210000)=13.28
100 000 О(100000)=100000 O(log2100000)=16.6
1 000 000 О(1000000)=1000000 O(log2100000)=19.93
Exemplu 2. Merge sort
Împărțiți un sir de n numere în
2 jumătăți

Sortați recursiv cele două


jumătăți
Combinând două jumătăți
sortate într-o singură secvență
sortată

Secvență C++ ( Merge sort )


#include <iostream> void MergeSort(int *A, int first, int last){
using namespace std; {
void Merge(int *A, int first, int if (first<last) {
last){ int middle, start, final, j; MergeSort(A, first, (first+last)/2);//
int *mas=new int[100]; MergeSort(A, (first+last)/2+1, last); //
middle=(first+last)/2; // Merge(A, first, last); //
start=first; // }
final=middle+1; // }
for(j=first; j<=last; j++) // };
if ((start<=middle) && int main(int argc, char** argv){
((final>last) || int i, n;
(A[start]<A[final]))) { int *A=new int[100];
mas[j]=A[start]; cout<<"Introduce N - the number of elements > "; cin>>n;
start++; for (i=1; i<=n; i++)
} { cout<<i<<" Element > "; cin>>A[i];}
else { MergeSort(A, 1, n); //
mas[j]=A[final]; cout<<"Sorted massive: "; //
final++; for (i=1; i<=n; i++) cout<<A[i]<<" ";
} delete []A;
for (j=first; j<=last; j++) system("pause>>void");
A[j]=mas[j]; return 0;
delete[]mas; }
};

Vizionați video video despre Merge sort with Transylvanian-


saxon (German) folk dance
https://www.youtube.com/watch?
v=XaqR3G_NVoo&feature=emb_log o
Exemplu 3. Сalculați suma elementelor matrice folosind metoda Divide et Impera
Fie un vector V cu n elemente întregi, indexate de la 1 la n. Să se determine suma lor.

Problema este binecunoscută. Cum o rezolvăm prin metoda Divide et Impera? Care sunt subproblemele?

A împărți problema în subprobleme constă de fapt în a împărți vectorul în doi subvectori, cu număr (aproape) egal de elemente. Primul
subvector ar fi V cu elementele indexate de la 1 la n/2 (prima jumătate a lui V), iar al doilea ar fi a doua jumătate – elementele indexate de la
n/2+1 la n.

Prima jumătate este un vector, dar a jumătate nu mai este un vector, elementele nu mai sunt indexate de la 1 la ..., deci cele două
subprobleme nu mai sunt de același tip (sau cel puțin nu în mod direct).

Putem reformula problema inițială astfel:


Fie un vector V cu n elemente întregi, indexate de la 1 la n. Determinați suma elementelor din secvență delimitată de indicii 1 și n.

Vom realiza o funcție care să determine pentru vectorul V suma elementelor din secvența delimitată de indicii st și dr. Pentru a rezolva
problema dată vom apela funcția cu parametrii st=1 și dr=n. Această abordare are două avantaje:

• putem rezolva problema prin metoda divide et impera – o secvență poate fi împărțită în alte după secvențe, de dimensiuni mai mici; •
putem folosi funcția realizată pentru a determina suma elementelor din orice secvența a vectorului.

Pentru secvența delimitată de st și dr, procedăm astfel:


• dacă st == dr, atunci suma este V[st];
• altfel:
o împărțim problema în subprobleme: determinăm mijlocul secvenței, m = (st + dr) / 2; astfel, obținem două secvențe: prima
conține elementele cu indici între st și m, iar a doua conține elementele cu indici între m+1 și dr (observăm că cele două
secvențe nu au elemente comune – subproblemele sunt independente);
o rezolvăm cele două subprobleme în același mod:
▪ determinăm S1 = suma din prima secvență
▪ determinăm S2 = suma din a doua secvență;
o combinăm rezultatele: suma pe secvența inițială este egală cu S1 + S2.

Secvență C++ (suma elementelor matrice folosind metoda Divide et Impera)


#include <iostream> int main(int argc, char** argv) {
using namespace std; int i,n;
int Suma(int V[], int st, int dr){ int *V=new int[100];
if(st == dr)
return V[st]; // problema elementara //citire n si V
else { cout<<"Introduce N - the number of elements > ";
cin>>n;
// împartim problema în subprobleme for (i=1; i<=n; i++){
int m = (st + dr) / 2; cout<<i<<" Element > "; cin>>V[i];
}
// rezolvam prima subproblema cout << “Suma = ”<<Suma(V,1,n);
int s1 = Suma(V, st, m); return 0;
}
// rezolvam a doua subproblema
int s2 = Suma(V, m + 1, dr);

// combinam rezultatele
return s1 + s2;
}
}

Exemplu 4. Sortarea rapidă (QuickSort)


Prin această metodă de sortare se execută următoarele operaţii prin care sunt rearanjate elementele
din cadrul vectorului:

✔ Primul element din vector, numit pivot, este mutat în cadrul vectorului pe poziţia pe
care trebuie să se găsească în vectorul sortat.
✔ Toate elementele mai mici decât el vor fi mutate în vector în faţa sa.
✔ Toate elementele mai mari decât el vor fi mutate în vector după el.
12 15 21 9 49 31 55 89 13
Pivotul
13 9 12 15 21 49 31 55 89
Pivotul

Folosind metoda divide et impera problema iniţială va fi descompusă în subprobleme,


astfel: PAS 1. Se rearanjează vectorul, determinându-se poziţia în care va fi mutat pivotul
(m).
PAS 2. Problema iniţială (sortarea vectorului iniţial) se descompune în două subprobleme, prin
descompunerea vectorului în doi subvectori: vectorul din stânga pivotului şi vectorul din
dreapta pivotului, care vor fi sortaţi prin aceeaşi metodă. Aceşti subvectori, la rândul lor, vor
fi şi ei rearanjaţi şi împărţiţi de pivot în doi subvectori etc.
PAS 3. Procesul de descompunere în subprobleme va continua până când, prin
descompunerea vectorului în subvectori, se vor obţine vectori care conţin un singur element.
Subprogramele specifice algoritmului divide et impera vor avea
următoarea semnificaţie:
• În subprogramul divizeaza() se va rearanja vectorul şi se va determina poziţia
pivotului xm, care va fi folosită pentru divizarea vectorului în doi subvectori: [xs, x m-
1] şi [x m+1, xd].
• Subprogramul combina() nu mai este necesar, deoarece combinarea soluţiilor se face
prin rearanjarea elementelor în vector
În subprogramul divizeaza() vectorul se parcurge de la ambele capete către poziţia în care trebuie
mutat pivotul. Se vor folosi doi indici: i – pentru parcurgerea vectorului de la începutul lui către
poziţia pivotului (i se va incrementa) şi j – pentru parcurgerea vectorului de la sfârşitul lui către
poziţia pivotului (j se va decrementa). Cei doi indici vor fi iniţializaţi cu capetele vectorului (i=s,
respectiv j=d) şi se vor deplasa până când se întâlnesc, adică atât timp cât i<j. În momentul în care
cei doi indici s-au întâlnit înseamnă că operaţiile de rearanjare a vectorului s-au terminat şi pivotul
a fost adus în poziţia corespunzătoare lui în vectorul sortat. Această poziţie este i (sau j) şi va fi
poziţia m de divizare a vectorului.
În exemplele următoare sunt prezentate două versiuni pentru subprogramul divizeaza():
Versiunea 1. Se folosesc variabilele logice: pi, pentru parcurgerea cu indicele i, şi pj,
pentru parcurgerea cu indicele j. Ele au valoarea: 1 – se parcurge vectorul cu acel indice, şi
0 – nu se parcurge vectorul cu acel indice; cele două valori sunt complementare
Versiunea 2.
Observaţie. În

ambele cazuri algoritmul continuă cu divizarea vectorului în subvectorii cu indicii [1,2] şi


[4,5] şi rearanjarea elementelor în cei doi subvectori.
Complexitatea algoritmului de sortare rapidă.
Pentru divizarea problemei în subprobleme se calculează mijlocul intervalului de indici şi
O(D(n))=Θ(1). Pentru combinarea soluţiilor se parcurge vectorul cu ajutorul celor doi
indici, de la primul element până la ultimul element, şi O(D(n))=O(n×Θ(1))=O(n). Timpul
de execuţie este T(n)= 2×T(n/2)+n.
Considerând că n=2k, rezultă:
T(n)=T(2k)+2k = 2× (T(2 k-1)+ 2 k-1) +2k = 2×T(2 k-1) +2k +2k = ... =
2×2× (T(2 k-2)+ 2 k-2) +2 k +2 k = 22× (T(2 k-2)+ 2 k-2) +2k +2k +2k = k×2k = log2n ×n.
Ordinul de complexitate al algoritmului este O(n×log2n).
Vizionați video despre Quick-sort with Hungarian (Küküllőmenti legényes) folk
dance https://www.youtube.com/watch?v=ywWBy6J5gz8
Exemplu 5. Turnul din Hanoi
Turnul din Hanoi sau Turnurile din Hanoi este
un joc matematic sau puzzle. Este format din trei
tije și un număr variabil de discuri, de diferite
mărimi, care pot fi poziționate pe oricare din cele
3 tije. Jocul începe având discurile așezate în
stivă pe prima tijă, în ordinea mărimii lor, astfel
încât să formeze un turn. Scopul jocului este
acela de a muta întreaga stivă de pe o tijă pe alta,
respectând următoarele reguli:
• Doar un singur disc poate fi mutat, la un moment
dat.
• Fiecare mutare constă în luarea celui mai de sus
disc de pe o tija și glisarea lui pe o altă tijă,
chiar și deasupra altor discuri care sunt deja
prezente pe acea tijă.
• Un disc mai mare nu poate fi poziționat deasupra
unui disc mai mic.
Cu 3 discuri, jocul se rezolvă în 7 mișcări.
Numărul minim de mișcări pentru a rezolva jocul
este 2n-1
Secvență C++ (Turnul din Hanoi)

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