Sunteți pe pagina 1din 17

Ministerul Educației al Republicii Moldova

Universitatea Tehnică a Moldovei

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

Divide et impera este o tehnica de elaborare a algoritmilor care constă în:

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ă.

Mergesort (sortarea prin interclasare)

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).

Quicksort (sortarea rapida)

Algoritmul de sortare quicksort, inventat de Hoare în 1962, se bazează de asemenea pe principiul


divide et impera. Spre deosebire de mergesort, partea nerecursivă a algoritmului este dedicata construirii
subcazurilor şi nu combinării soluţiilor lor.
Ca prim pas, algoritmul alege un element pivot din tabloul care trebuie sortat. Tabloul este apoi
partiţionat în două subtablouri, alcătuite de-o parte şi de alta a acestui pivot în următorul mod: elementele
mai mari decât pivotul sunt mutate în dreapta pivotului, iar celelalte elemente sunt mutate în stânga
pivotului. Acest mod de partiţionare este numit pivotare. În continuare, cele două subtablouri sunt sortate
în mod independent prin apeluri recursive ale algoritmului. Rezultatul este tabloul complet sortat; nu mai
este necesară nici o interclasare. Pentru a echilibra mărimea celor două subtablouri care se obţin la fiecare
partiţionare, ar fi ideal să alegem ca pivot elementul median. Intuitiv, mediana unui tablou T este
elementul m din T, astfel încât numărul elementelor din T mai mici decât m este egal cu numărul celor
mai mari decât m. Din păcate, găsirea medianei necesita mai mult timp decât merită. De aceea, putem pur
şi simplu să folosim ca pivot primul element al tabloului. Iată cum arată acest algoritm:

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:

1. Studiaţi noţiunile teoretice despre metoda divide et impera.


2. Implementaţi algoritmii Mergesort şi Quicksort.
3. Efectuaţi analiza empirică a algoritmilor Mergesort şi Quicksort.
4. Faceţi o concluzie asupra lucrării efectuate.

Codul programului in C++


#include<iostream>
#include<stdlib.h>
#include<vector>
#include<time.h>

using namespace std;

long num,num1, num2, num3, num4, optiune;


long n;

/*void OutVector(vector<long> vec) {


for (vector<long>::const_iterator i = vec.begin(); i != vec.end(); ++i)
cout << *i << " ";
cout << endl;
}*/
void Insertion_Sort(vector<long> tab, long st, long dr)
{
num++;
for (int i = 1; i < n; i++)
{
num++;
long j = dr;
long key = tab[i];
j = i - 1;
for (j = i-1; j>=0 && tab[j] > key;j--)
{
tab[j + 1] = tab[j];
}
tab[j + 1] = key;
num++;
}
}
void QuickSort(vector<long> tab, long st, long dr) {
num3++;
long i = st, j = dr;
long mij = tab[(st + dr) / 2];

while (i <= j) {

while (tab[i] < mij)


i++;
while (tab[j] > mij)
j--;
if (i <= j) {
swap(tab[i], tab[j]);
i++;
j--;
}
};
num4++;
if (st < j)
QuickSort(tab, st, j);
if (i < dr)
QuickSort(tab, i, dr);
}

void Merge(vector<long> tab1, long st, long mij, long dr) {


num2++;
long i, j;
vector<long> tmp(100000);
for (i = mij + 1; i>st; i--)
tmp[i - 1] = tab1[i - 1];

for (j = mij; j<dr; j++)


tmp[dr + mij - j] = tab1[j + 1];

for (long k = st; k <= dr; k++)


if (tmp[j]<tmp[i])
tab1[k] = tmp[j--];
else
tab1[k] = tmp[i++];
}

void MergeSort(vector<long> tab2, long st, long dr) {


num1++;
if (dr <= st) return;
long mij = (dr + st) / 2;
MergeSort(tab2, st, mij);
MergeSort(tab2, mij + 1, dr);
Merge(tab2, st, mij, dr);
}

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

Sortarea Merge împarte lista de sortare in doua jumătăţi egale, si le pune in


matrice diferite. Fiecare matrice e sortata recursive, mai apoi fiind unite înapoi formînd
lista finala sortata. Ca majoritatea listelor recursive, sortarea merge are un algoritm de
complexitate de O(n log n).

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

Numarul de itaratii pentru 10,100,1000,10000 elemente


Cazul Favorabil
29998

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

Timpul de executie pentru 10,100,1000,10000 elemente

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.

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