Departamentul ISA
RAPORT
Lucrarea de laborator nr.2
Analiza si proiectarea algoritmilor
A efectuat:
st. gr. TI-173 Roșca Florin
A verificat:
lect., univ. Andrievschi-Bagrin Veronica
Chisinau 2018
LUCRAREA DE LABORATOR NR.2
Tema: Metoda divide et impera
Scopul lucrării:
Studierea metodei divide et impera.
Analiza și implementarea algoritmilor bazați pe metoda divide et impera.
Note de curs
1. Descompunerea cazului ce trebuie rezolvat într-un număr de subcazuri mai mici ale aceleiaşi
probleme.
2. Rezolvarea succesivă şi independentă a fiecăruia din aceste subcazuri.
3. Combinarea subsoluţiilor astfel obţinute pentru a găsi soluţia cazului iniţial.
Algoritmul formal al metodei divide et impera:
function divimp(x)
{returnează o soluţie pentru cazul x}
1: if x este suficient de mic
2: then return adhoc(x)
3: {descompune x în subcazurile x1, x2, …, xk}
4: for i 1 to k do yi divimp(xi)
5: {recompune y1, y2, …, yk în scopul obţinerii soluţiei y pentru x}
6: return y
unde adhoc este subalgoritmul de bază folosit pentru rezolvarea micilor subcazuri ale problemei în cauză.
Fie T[1 .. n] un tablou pe care dorim sa-l sortam crescător. Prin tehnica divide et impera putem proceda
astfel: separăm tabloul T în două părţi de mărimi cât mai apropiate, sortăm aceste părţi prin apeluri
recursive, apoi interclasăm soluţiile pentru fiecare parte, fiind atenţi să păstrăm ordonarea crescătoare a
elementelor. Obţinem următorul algoritm pentru rezolvarea unei subprobleme:
procedure
mergesort (T, p, r)
1: if p < r
2: then q p r/2
3: mergesort (T, p, q)
4: mergesort(T, q+1, r)
5: merge (T, p, q, r)
Algoritmul mergesort ilustrează perfect principiul divide et impera:
- divide problema în subprobleme;
- stăpâneşte subproblemele prin rezolvare;
- combină soluţiile subproblemelor.
In algoritmul mergesort, suma mărimilor subcazurilor este egală cu mărimea cazului iniţial. Această
proprietate nu este în mod necesar valabilă pentru algoritmii divide et impera. Este esenţial ca subcazurile
să fie de mărimi cât mai apropiate (sau, altfel spus, subcazurile să fie cât mai echilibrate).
procedure
quicksort (T, p, r )
1: if p < r
2: then q Partition (T, p, r )
3: quicksort (T, p, q)
4: quicksort t(T, q+1, r)
Intuitiv, ne dăm seama că algoritmul quicksort este ineficient, dacă se întâmpla în mod sistematic ca
subcazurile să fie puternic neechilibrate Dacă elementele lui T sunt distincte, cazul cel mai nefavorabil
este atunci când iniţial tabloul este ordonat crescător sau descrescător, fiecare partiţionare fiind total
neechilibrată.
Sarcina de baza:
while (i <= j) {
int main() {
clock_t start_1, finish_1;
clock_t start_2, finish_2;
clock_t start_3, finish_3;
cout << "Introduceti numarul de elemente a vectorului: ";
cin >> n;
while (1) {
vector<long>v1;
vector<long>v2;
vector<long>v3;
vector<long>v4;
if (!v1.empty()) v1.clear();
if (!v2.empty()) v2.clear();
if (!v3.empty()) v3.clear();
if (!v4.empty()) v4.clear();
cout << "1.Caz favorabil" << endl
<< "2.Caz mediu" << endl
<< "3.Caz nefavorabil" << endl
<< "0.Exit" << endl;
cout << "Optiune: ";
cin >> optiune;
switch (optiune) {
case 1: {
for (long i = 0; i<n; i++) {
v1.push_back(i+1);
v2.push_back(i+1);
v3.push_back(i+1);
v4.push_back(i + 1);
}
break;
}
case 2: {
for (long i = 0; i<n; i++) {
v1.push_back(rand() % 10001);
v2.push_back(v1[i]);
v3.push_back(v1[i]);
v4.push_back(v1[i]);
}
break;
}
case 3: {
for (long i = 0; i<n; i++) {
v1.push_back(n - i);
v2.push_back(n - i);
v3.push_back(n - i);
v4.push_back(n - i);
}
break;
}
case 0: {
return 0;
}
}
system("cls");
//cout << "\tVECTORUL INITIAL " << endl;
//OutVector(vrt);
cout << endl;
num = 0;
num1 = 0;
num2 = 0;
num3 = 0;
num4 = 0;
cout << endl << "\tINSERTION SORT " << endl;
start_1 = clock();
Insertion_Sort(v4, 0, v4.size() - 1);
finish_1 = clock() - start_1;
//OutVector(xrt);
cout << endl << "Numarul de iteratii: " << num << endl;
cout << "Timpul: " << (float)finish_1 / CLOCKS_PER_SEC << " secunde" <<
endl << endl;
cout <<endl<< "\tMERGE SORT "<<endl;
start_2 = clock();
MergeSort(v2, 0, v2.size() - 1);
finish_2 = clock() - start_2;
//OutVector(srt);
cout <<endl<< "Numarul de iteratii: " << num1+num2 << endl;
cout << "Timpul: " << (float)finish_2 / CLOCKS_PER_SEC << " secunde" <<
endl<<endl;
cout << endl << "\tQUICK SORT " << endl;
start_3 = clock();
QuickSort(v3, 0, v3.size() - 1);
finish_3 = clock() - start_3;
//OutVector(qrt);
cout <<endl<< "Numarul de iteratii: " << num3+num4 << endl;
cout << "Timpul: " << (float)finish_3 / CLOCKS_PER_SEC << " secunde" <<
endl;
cin.get();
cin.get();
system("cls");
}
return 0;}
Repere :
Merge Sort
Quick Sort
Metoda quick sort este o metoda divide şi stăpîneşte, care foloseşte funcţii
recursive. Este o metoda mult mai rapida decît sortarea merge, dar metoda de sortare
quick e grea de scris in cod.
Algoritmul recursive consta din 4 paşi:
Daca in matrice are unu sau mai puţine elemente de sortat face return.
Alegem un element ca punct pivot. De obicei este utilizat elementul cel mai din stînga
împărţim matricea in doua-o parte cu elementele mai mari ca pivotul si alta mai mici.
Repetam recursive algoritmul pentru ambele jumătăţi.
Eficienta depinde de care element a fost ales ca pivot. In cel mai rău caz, eficienta quick
sort este de O(n2), cînd cel mai din stînga element a fost ales ca pivot. Pentru ca eficienta
sa fie mai mare alegem la întâmplare un număr numai in cazul cand elementele din
matrice nu sunt aranjate întîmplător. Cînd pivotul e ales la întîmplare, metoda quick sort
are o complexitate algoritmica de O(n log n).
Insertion Sort
Sortarea prin insertie este apoape asemanatoare cu sortarea prin selectie. Tabloul este
delimitata in doua parti - o parte sortata si o parte nesortata. La inceput, partea sortata contine
primul element al tabloului si partea nesortata contine restul tabloului. La fiecare iteratie,
algoritmul ia primul element din partea nesortata si il insereaza in locul potrivit al partii sortate.
Cand partea nesortata nu mai are nici un element, algoritmul se opreste.
Screens
Reprezentarea rezultatelor
Algoritmul
Date de intrare
Cel mai favorabil caz (nr iterații)
Cazul mediu
(nr iterații)
Cel mai nefavorabil caz(nr de iterații)
Insertion Sort
10
19
19
19
100
199
199
199
1000
1999
1999
1999
10000
19999
19999
19999
Mergesort
10
28
28
28
100
298
298
298
1000
2998
2998
2998
10000
29998
29998
29998
Quicksort
10
100
1000
10000
12
126
1022
11808
20
294
3766
17280
18
175
1521
16809
Algoritmul
Date de intrare
Cel mai favorabil caz (timpul)
Cazul mediu
(timpul)
Cel mai nefavorabil caz(timpul)
Insertion Sort
10
0
0
0
100
0
0.009
0.02
1000
0.003
0.337
0.632
10000
0.012
31.681
62.302
Mergesort
10
0.003
0.001
0.002
100
0.026
0.024
0.018
1000
0.127
0.104
0.105
10000
1.154
1.155
1.148
Quicksort
10
100
1000
10000
0.004
0.06
0.001
0.009
0.112
0.001
0.005
0.065
19999
Iteratii
11808
2998
1999
298
199
126 1022
28
12
19
10 100 1000 10000
Cazul Mediu
29998
19999
Iteratii
17280
2998
1999
1754
16
19
28 298
199
178
10 100 1000 10000
Cazul Nefavorabil
29998
19999
Iteratii
11810
2998
1999
1022
28
19
14 298
199
126
10 100 1000 10000
Cazul Favorabil
1.15
Timpul
0.13
0.06
0 0.03
0 0 0.01
10 100 1000 10000
Cazul Mediu
31.68
Timpul
1.16
0 0.02
0.01
0 0.34
0.1
0.01 0.11
10 100 1000 10000
Cazul Nefavorabil
62.3
Timpul
0 0.02
0 0.63
0.11
0.01 1.15
0.07
10 100 1000 10000
Concluzie
Efectuind lucrarea data am studiat algoritmii QuickSort și MergeSort si Insertion Sort,
aplicindui în practica și observînd variațiile numărului de iterații la diferite mărimi a tablourilor
și în diferite situații precum tablouri deja sortate, cu numere aleatorii și sortate invers. După
datele obținute și o mulțime de rezultate obținute pot spune ca QuickSort sort este un algoritm
mai rapid decit celelalte. Am efectuat aceste 3 programe în C++ cu algoritmii de sortare quiqsort
şi mergesort si My_sort (Insertion) În urma datelor obtinute ne-am confirma că algoritmul
Quicksort este mai rapid decît Mergesort. Insa nu prea efectiv la numere mari.