Sunteți pe pagina 1din 46

Cuprins

Introducere...................................................................................................................................2
Capitolul I. Noţiuni introductive..................................................................................................3
I.1 Noţiunea de sortare..............................................................................................................3
I.2 Clasificarea metodelor de sortare........................................................................................3
I.2.1 Metodele de sortare directă..........................................................................................3
I.2.2.Metodele de sortare avansată.......................................................................................4
I.3. Caracteristicile metodelor de sortare..................................................................................6
Capitolul II. Elemente teoretice necesare analizei algoritmilor....................................................7
II.1.Complexitatea....................................................................................................................7
II.2.Calculul de complexitate....................................................................................................7
II.2.1. Complexitatea spaţiu de memorare............................................................................8
II.2.2. Complexitatea timp de execuţie.................................................................................8
II.2.3. Cazul favorabil, cazul mediu şi cazul defavorabil....................................................11
Capitolul III. Metode de sortare avansată...................................................................................13
III.1. Quick Sort. Prezentarea metodei....................................................................................13
III.1.1. Quick Sort. Prezentarea algoritmului......................................................................13
III.1.2. Quick Sort. Analiza complexităţii...........................................................................15
III.2. Shell Sort. Prezentarea metodei.....................................................................................16
III.2.1. Shell Sort. Prezentarea algoritmului.......................................................................16
III.2.2. Shell Sort. Analiza complexităţii............................................................................17
III.3. Merge Sort. Prezentarea metodei...................................................................................18
III.3.1. Merge Sort. Prezentarea algoritmului.....................................................................18
III.3.2. Merge Sort. Analiza complexităţii..........................................................................19
III.4. Heap Sort. Prezentarea metodei.....................................................................................20
III.4.1. Heap Sort. Prezentarea algoritmului.......................................................................20
III.4.2. Heap Sort. Analiza complexităţii............................................................................22
III.5. Insertion Sort. Prezentarea metodei...............................................................................22
III.5.1. Insertion Sort. Prezentarea algoritmului.................................................................23
III.5.2. Insertion Sort. Analiza complexităţii......................................................................23
III.6. Comb Sort. Prezentarea metodei....................................................................................24
III.6.1. Comb Sort. Prezentarea algoritmului......................................................................24
III.6.2. Comb Sort. Analiza Complexităţii..........................................................................25
Capitolul IV. Descrierea aplicaţiei. Demonstraţie......................................................................26
Concluzii....................................................................................................................................44
Bibliografie................................................................................................................................45

1
Introducere

Lucrarea propune analiza din punct de vedere teoretic şi practic a unei serii de
algoritmi de sortare (Quick Sort, Shell Sort, Merge Sort, Heap Sort, Insertion Sort şi
Comb Sort) precum şi testarea acestora în cele trei cazuri (favorabil, mediu şi nefavorabil),
asupra a cinci vectori construiţi aleator, formaţi din 500 de elemente.
Pentru realizarea aplicaţiei am utilizat limbajul de programare C++, datorită înclinaţiei
personale pentru acest limbaj dar şi pentru faptul că este un limbaj popular, general şi uşor de
folosit, iar pe baza rezultatului obţinut (numărul de operaţii şi timpul de execuţie pentru
fiecare sortare în parte testată asupra celor cinci vectori construiţi aleator), am realizat grafice
cu ajutorul soft-ului Ms Excel pentru a evidenţia performanţa acestor algoritmi de sortare.
Am ales această temă deoarece consider că metodele de sortare reprezintă un domeniu
ideal pentru studiu şi cercetare, ele fiind tot mai des folosite în domenii precum matematica,
statistică matematică şi limbi (pentru realizarea de dicţionare), având un rol important atât în
construcţia algoritmilor cât şi în realizarea diferitelor tehnici de programare.
Lucrarea este alcatuită din cinci capitole.
Astfel, primul capitol conţine introducerea şi prezentarea noţiunilor introductive,
capitolul doi conţine prezentarea noţiunilor teoretice necesare analizei algoritmilor, capitolul
trei conţine prezentarea metodelor de sortare, capitolul patru conţine prezentarea aplicaţiei şi
a graficelor construite cu ajutorul soft-ului MS Excel, iar în capitolul cinci se includ
concluziile.

2
Capitolul I. Noţiuni introductive

I.1 Noţiunea de sortare

Din punct de vedere al informaticii, noţiunea de algoritm se traduce ca fiind o


metodă generală de rezolvare a unui tip de problemă, metodă ce poate fi implementată pe
calculator. Algoritmul este alcătuit dintr-o succesiune finită de operaţii, care se execută într-o
ordine stabilită, astfel încât, plecând de la datele problemei (Date de Intrare=D.I) ce
îndeplinesc numite condiţii stabilite, să obţinem soluţiile problemei (Date de Ieşire=D.E)
într-un interval de timp finit[ CITATION Raz95 \l 1033 ].
Sortarea este o metodă (un algoritm), prin intermediul căreia elementele unei liste sau
ale unui vector pot fi ordonate după anumite criterii impuse. Pentru elementele numerice,
sortarea poate fi crescătoare sau descrescătoare, iar pentru elemente alfanumerice, sortarea se
face într-o anumită ordine alfabetică.
Sortarea reprezintă una dintre cele mai utilizate metode de programare având utilizări
domenii precum matematica (Statistică Matematică), limbi (Realizarea de dicţionare), astfel,
lucrarea presupune prezentarea a 5 algoritmi de sortare avansată (Quick Sort, Shell Sort,
Merge Sort, Heap Sort si Insertion Sort) precum şi a avantajelor şi dezavantajelor acestora.

I.2 Clasificarea metodelor de sortare

Metodele de sortare se clasifică în Metode de sortare directă şi Metode de sortare


avansată.

I.2.1 Metodele de sortare directă


Metodele de sortare directă se bazează pe algoritmi de dificultate scazută şi se
caracterizează prin faptul că sunt uşor de înţeles şi de găsit.
Exemple de Metode de sortare directă:
 Selection Sort (Sortarea prin selecţie)- este una dintre cele mai simple metode
de sortare, caracterizată prin faptul că lucrează foarte bine pe liste sau vectori de dimensiuni
mici, fiecare element fiind mutat cel mult o dată.

3
 Bubble Sort (Sortarea prin metoda bulelor)- este o metodă de sortare simplă,
eficientă pentru listele sau vectorii cu un număr mic de elemente (mai mic de 15). Este cea
mai simplă metodă de sortare şi nu necesită o cunoaştere detaliată a limbajului de programare.
 Insertion Sort (Sortarea prin Inserţie)- este un algoritm de sortare simplu,
liniar, eficient pentru liste sau vectori ce conţin N elemente aproape sortate. Această metodă
de sortare se bazează pe tehnica “Jucatorului de Bridge”, fiind un algoritm aproape la fel de
simplu ca Selection Sort, dar poate mai flexibil[ CITATION GMi12 \l 1033 ].

I.2.2.Metodele de sortare avansată

Metodele de sortare avansată se bazează pe algoritmi mai complicaţi, mai greu de


înţeles dar mai eficienţi în special pentru liste şi vectori ce conţin un număr mare de elemente.
Exemple de Metode de sortare avansată:
 Quick Sort (Sortarea rapidă)- este metoda de sortare recursive, foarte rapidă,
folosind partiţionarea ca şi idee de bază. Este mai rapidă ca orice altă metodă de sortare
simplă, foarte eficientă pentru liste şi vectori cu un număr mare de elemente, însa ineficientă
pentru cele cu număr mic de elemente.
 Shell Sort (Sortarea lui Shell)- este o extensie simplă a metodei Insertion Sort
ce câştigă viteză permiţând interschimbarea elementelor aflate la mare distanţă unul faţă de
celălalt. Shell Sort este o metodă de sortare stabilă, având un timp de execuţie accesibil pentru
liste şi vectori ce conţin un număr mare de elemente (mai puţin de 5000 de elemente).
 Merge Sort (Sortarea prin interclasare)- este o metodă de sortare stabilă, care
foloseşte ca şi strategie de bază “Divide et Impera”. Este o metodă eficientă pentru liste şi
vectori ce conţin un număr mare de elemente.
 Heap Sort (Sortare cu “gramada” sau Sortare prin metoda ansamblelor)- este
una din metodele de sortare foarte performante. Este o metoda nerecursiva ce “impaca” viteza
cu consumul relative mic de memorie.
 Insertion Sort (Sortarea prin inserţie)- este o metodă de sortare simplă ce se
bazează pe construcţia treptată a listei sau vectorului sortat, principalele avantaje ale acestei
metode fiind uşurinţa de implementare şi eficienţa pentru liste şi vectori ce conţin un număr
mic de elemente.

4
 Comb Sort (Sortarea prin pieptanare)- este o îmbunătăţire a metodei de sortare
Bubble Sort.Ideea de bază a acestei sortări este de a muta “ţestoasele” (valorile mici) aproape
de sfârşitul listei, deoarece acestea încetinesc foarte mult sortarea, iar “iepurii” (valorile mari)
vor fi mutate la începutul listei[ CITATION Met12 \l 1033 ].

Sortarea o mai putem defini ca fiind procesul prin care elementele vectorului sau
listei de sortat, să fie rearanjate astfel încât cheile lor să se afle într-o anumită ordine.
Cheile reprezintă caracteristici ale elementelor ce iau valori într-o mulţime pe care
este definită o relaţie de ordine.

Exemplu:
Considerăm că setul prelucrat este alcătuit din valori scalare ce reprezintă chiar cheile
de sortare, iar scopul este ordonarea crescatoare a acestora. Astfel, scopul ordonării este de a

1 2 .... n
determina o permutare
( p ( 1 ) p ( 2 ) .... p ( n ) ) a înregistrărilor, care va pune cheile într-o
ordine crescătoare:

Kp(1) ≤ Kp(2) ≤....≤ Kp(n).

În cazul algoritmilor de sortare, există o dependenţă între reprezentarea datelor care


trebuie prelucrate şi alegerea algoritmului de prelucrare. Astfel, putem clasifica algoritmii de
sortare în:
-Algoritmi interni- în care elementele setului sunt stocate pe un suport de informaţie
ce permite accesul aleator la date.
-Algoritmi externi- în care suportul de informaţie permite doar accesul secvenţial la
date.
Pe de altă parte, în funcţie de spaţiul de manevră necesar sortării avem:

-Sortare ce foloseşte o zonă de manevră de dimensiunea setului de date. Dacă


setul initial de date este reprezentat de tabloul x[1..n], cel sortat se va obţine într-un alt tablou
y[1..n].

-Sortare in aceeasi zona de memorie (sortare pe loc- in situ). Elementele


tabloului x[1..n] îşi schimbă poziţiile astfel încât dup ă încheierea procesului să fie ordonate.

5
I.3. Caracteristicile metodelor de sortare

Metodele de sortare pot fi caracterizate prin:


 Stabilitate
O caracteristică importantă a unui algoritm de sortare este stabilitatea. O metodă de sortare
este stabilă daca ordinea relativă a elementelor ce au aceeaşi valoare a cheii nu se modifică în
procesul de sortare, adică: p(i) < p(j) pentru Kp(i) = Kp(j) şi i < j
 Naturaleţe
O altă caracteristică importantă a metodelor de sortare este naturaleţea. O metodă de sortare
este naturală dacă numărul de operaţii scade odată cu distanţa dintre tabloul iniţial şi cel
sortat. Măsura acestei distanţe este reprezentată de numărul de inversiuni al permutarii
corespunzatoare tabloului iniţial.
 Eficienţă
O altă caracteristică importantă a metodelor de sortare este eficienţa. O metodă de sortare este
considerată eficientă dacă nu necesită nu volum mare de resurse.
Din punct de vedere al spaţiului de memorie, un algoritm de sortare in situ este mai eficient
decât unul bazat pe o zonă de manevră de dimensiunea tabloului. Ȋn ceea ce priveşte timpul de
execuţie, este important să fie efectuat un număr cât mai mic de operaţii.
O metodă este considerateă optimală dacă ordinul său de complexitate este cel mai mic din
clasa metodelor din care face parte.
 Simplitate

O altă caracteristică importantă a metodelor de sortare este simplitatea. O metodă de


sortare este considerată simplă dacă este intuitivă şi uşor de înţeles[CITATION Teh13 \l 1033 ].

Capitolul II. Elemente teoretice necesare analizei


algoritmilor

II.1.Complexitatea

6
Prin complexitatea unui algorim se înţelege dependenţa dintre dimensiunea datelor de
intrare şi timpul de execuţie. Performanţele unei metode de sortare depind de gradul de sortare
parţială a vectorului, deci ar fi normal să intre ca parametru în expresia timpului de execuţie.
De obicei, se calculează timpul de execuţie pentru diferite grade de sortare, cazul cel mai
defavorabil, cazul mediu, cazul cel mai favorabil.
În studiul complexităţii algoritmilor de sortare vom folosi drept criterii de evaluare
numărul de comparaţii de chei şi numărul de mutări de chei. Ȋn general, cheile se mută în
procesul de sortare pe distanţe ce depind de gradul de sortare iniţială. De aceea, nu va fi de
ajuns să calculăm numărul de comparaţii de chei şi numărul de mutări de chei doar în funcţie
de n, numărul de elemente din secvenţă, ci va trebui să facem o diferenţiere în funcţie de
valoarea gradului de sortare[ CITATION Pro96 \l 1033 ].

II.2.Calculul de complexitate

Analiza complexităţii unui algoritm are ca scop determinarea volumului de resurse de


calcul necesare pentru execuţia programului. Prin resurse înţelegem:
-Spaţiul de memorie necesar pentru stocarea datelor pe care algoritmul le
prelucrează.
-Timpul necesar pentru execuţia prelucrărilor specificate în algoritm.
Ȋn general, pentru o problemă se pot găsi o mulţime de algoritmi. Ȋn cazul sortării,
eficienţa unui algoritm poate depinde de numărul de articole de sortat, de gradul de sortare
parţială, sau de tipul de memorie în care este păstrată secvenţa. De aceea avem nevoie de o
metodologie pentru a analiza aceşti algoritmi.
Compararea diferiţilor algoritmi trebuie făcută ţinând cont de memora calculatorului
(memorie ocupată şi timp de utilizat de microprocesor). Pentru rezolvarea problemelor
moderne din ce in ce mai complexe, trebuiesc elaboraţi algoritmi eficienti. În general, se
urmăreşte ca în urma analizei algoritmilor să rezulte o dependentă între timpul de execuţie şi
dimensiunea datelor de prelucrat.
Pentru a compara algoritmi ce pot rezolva o aceeşi problemă dată trebuie să ţinem cont
de urmatoarele două criteii:
-Complexitatea spaţiu de memorare a unui algoritm;
-Complexitatea timp de execuţie a unui algoritm.

7
II.2.1. Complexitatea spaţiu de memorare

Estimează cantitatea de memorie necesară algoritmului pentru a reţine datele de


intrare, datele de ieşire şi intermediare. Spaţiul de memorie este de două tipuri:
-Constant - este folosit pentru memorarea codului programului executabil, a
constantelor, variabilelor simple şi a structurilor de date de dimensiune constantă alocate
static, fiind independent de volumul şi tipul datelor de intrare;
-Variabil - folosit pentru structurile de date alocate dinamic, precum şi pentru
apelurile de proceduri şi funcţii. Dimensiunea spaţiului de memorie variabil depinde de tipul
şi volumul datelor de intrare, dar şi de instanţa problemei de rezolvat (starea problemei la un
anumit moment dat al execuţiei). Progresele tehnologice (crearea unor memorii cu capacităţi
de stocare din ce în ce mai mari) duc la scăderea importanţei complexităţii spaţiu de
memorare.

II.2.2. Complexitatea timp de execuţie

Timpul de execuţie al unui algoritm nu este constant, ci el depinde de dimensiunea


datelor (este o funcție de n, unde n= reprezintă dimensiunea intrării).
Analiza complexităţii timpului de execuţie al unui algoritm se realizează din
următoarele motive:
- timpul de execuţie al programului, depinde de numărul de operaţii elementare
efectuate de algoritmul ce stă la baza conceperii programului şi rezolvării problemei
(algoritmul are comportări diferite pentru seturi de date de intrare diferite);
- timpul de execuţie depinde de tipul calculatorului pe care se execută programul
(programul este rapid pe calculatoare puternice, dar lent pe calculatoare mai slabe);
- timpul de execuţie al programului depinde şi de limbajul de programare în care este
implementat (acelaşi program scris în limbajul C++ s-ar putea să fie mai rapid decât acelaşi
program implementat în Pascal);
- din motive practice, nu putem testa pe calculator programe pentru cazuri oricât de
mari, deoarece ar dura ore, zile şi chiar ani;
- se pot obţine informaţii empirice asupra duratei programelor numai pentru seturi
particulare de date de intrare, nu pentru situaţii generale.

8
Analiza complexităţii timp de execuţie a unui algoritm constă în următoarele etape
generale:
1) stabilirea operaţiilor elementare efectuate de algoritm;
2) determinarea costurilor acestor operaţii elementare;
3) alegerea operaţiei elementare reprezentative;
4) determinarea ordinului de mărime pentru numărul de operaţii elementare.
Operaţia elementară- este acea operaţie sau grup de operaţii care au timpul de
execuţie independent de datele de intrare ale problemei. Se consideră operaţii elementare,
operaţiile aritmetice (adunare, scădere, înmulţire, împărţire), comparaţiile şi cele logice
(negaţie, disjuncţie şi conjuncţie).
Costul unei operaţii elementare este dat de timpul necesar execuţiei acestei operaţii
elementare. Timpul unei operaţii elementare este fix. Fiecare operaţie elementară are alt timp
de execuţie.
Timpul de execuţie al unui algoritm destinat rezolvării unei probleme de dimensiune n
se notează cu T (n). Pentru a determina timpul de execuţie trebuie stabilit un model de calcul
şi o unitate de măsură. Vom considera un model de calcul (maşină de calcul) caracterizat
prin:
-Prelucrările sunt realizate în mod secvenţial;
-Operaţiile elementare sunt efectuate în timp constant indiferent de valoarea
operanzilor;
-Timpul de acces la informaţie nu depinde de poziţia acesteia.
Pentru a stabili o unitate de măsură, trebuiesc stabilite mai întai operaţiile elementare
şi considerat ca unitate de măsură timpul acestora de execuţie. Astfel, timpul de execuţie va fi
exprimat prin numărul de operaţii elementare efectuate.
Algoritmul are complexitatea timp T(n) de ordinul f(n).

Notaţii:
T(n) = complexitatea timp a algoritmului;
n = ordinul de mărime al datelor de intrare în algoritm;
O = ordinul de mărime al complexităţii timp;
f(n)= o funcţie (estimată) dependentă de numărul operaţiilor elementare
reprezentative din algoritm.

Definiţie:

9
Algoritmul are complexitatea timp T(n) de ordinul f(n) dacă există constantele c0 > 0
şi n0 > 0 astfel încât T(n) <= co*f(n), pentru orice n ≥ 0, adică pentru valori foarte mari ale lui
n, T(n) tinde să ia aceleaşi valori cu f(n). Pentru aceasta se foloseşte notaţia T(n)= O(f(n)),
numită şi notaţie asimptotică.
Ordinul de mărime al complexităţii timp (O) oferă o limită superioară pentru timpul
de execuţie al algoritmului. Această limită nu va fi depăşită, oricât de mare va fi n. Vom căuta
să determinăm o "cea mai mică funcţie" f(n) şi o "cea mai mică'' valoare pentru c0, astfel încât
complexitatea timp T(n) să fie cât mai "mică".
De exemplu, dacă T(n.)=2n2+12n+5, atunci termenul dominant este 2n2, f(n) este n2,
iar ordinul de mărime al lui T(n) este n2, deci algoritmul are complexitate timp O(n2).
Pentru a indica complexitatea timpului de execuţie al unui algoritm se folosesc
următoarele exprimări:
- algoritmul este constant (nu depinde de n) dacă T(n) = O(l);
- algoritmul este logaritmic dacă T(n) = O(log n);
- algoritmul este liniar dacă T(n) = O(n);
- algoritmul este liniar logaritmic dacă T(n) = O(n log n);
- algoritmul este pătratic dacă T(n) = O(n2);
- algoritmul este cubic dacă T(n) = O(n3);
- algoritmul este polinomial dacă T(n) = O(nk), unde k > 3 ;
- algoritmul este exponenţial dacă T(n)= O(kn), unde k ≥ 2;
- algoritmul este factorial dacă T(n) = O(n!).
Notaţia asimptotică O permite stabilirea următoarelor relaţii:
O(l) ≤ O(log n) ≤ O(n) ≤ O(n log n) ≤ O(n2) ≤ O(n3) ≤ O(nk) ≤ O(kn) ≤ O(n!)
Algoritmii preferaţi sunt cei care au ordinul de complexitate (O) situat spre stânga acestui şir.
Sunt consideraţi acceptabili algoritmii cu complexitate timp "polinomială", deoarece
programele ce-i implementează au o durată rezonabilă la executarea cu ajutorul calculatorului[
CITATION Bur96 \l 1033 ].

II.2.3. Cazul favorabil, cazul mediu şi cazul defavorabil

Ȋn general, numărul de operaţii executate nu depinde doar de dimensiunea problemei


ci şi de proprietăţile datelor de intrare, astfel pentru instanţe diferite ale problemei se execută
un număr diferit de prelucrări. Ȋn aceste situaţii, numărul de operaţii executate nu poate fi

10
calculat exact, astfel că se vor calcula margini ale acestuia analizând cele două cazuri
extreme: cazul cel mai favorabil şi cazul cel mai defavorabil.

-Cazul favorabil
Corespunde acelor instanţe ale problemei pentru care numărul de operaţii efectuate
este cel mai mic. Analiza în cazul cel mai favorabil permite identificarea unei limite inferioare
a timpului de execuţie. Această analiză este utilă pentru a identifica algoritmi ineficienţi (dacă
un algoritm are un cost mare în cel mai favorabil caz, atunci el nu poate fi considerat o
variantă acceptabilă). Există situaţii în care frecvenţa instanţelor corespunzătoare
celui mai favorabil caz sau apropiate acestuia este mare. De exemplu algoritmul de
sortare prin inserţie, se comportă bine în cazul în care tabloul este deja sortat, iar această
comportare
rămâne valabilă şi pentru tablouri ”aproape” sortate.

-Cazul mediu
Ȋn general, cazurile extreme (cazul cel mai favorabil şi cel mai favorabil) se întalnesc
rar, fiind utilă o altă măsură de complexitate a algoritmilor şi anume timpul mediu de
execuţie.
Stabilirea acestei distribuţii de probabilitate presupune împărţirea mulţimii instanţelor
posibile ale problemei în clase astfel încât pentru instanţele din aceeaşi clasă numărul de
operaţii efectuate să fie acelaşi.
Dacă ν(n) reprezintă numărul cazurilor posibile (clase de instanţe pentru care
algoritmul efectuează acelaşi număr de operaţii), Pk este probabilitatea de apariţie a cazului k
iar Tk(n) este timpul de execuţie corespunzător cazului k atunci timpul mediu este dat de
relaţia:

v(n)
Tm(n)=∑ Tk (n) Pk ;
k=1

Dacă toate cazurile sunt echiprobabile (Pk = 1/ν(n)) se obţine

v ( n)
Tm(n)=∑ Tk ( n ) Pk /v (n).
k=1

-Cazul defavorabil

11
Corespunde acelor instanţe ale problemei pentru care numărul de operaţii efectuate
este cel mai mare. Analiza acestui caz furnizează o limită superioară a timpului de execuţie .
Pentru
compararea algoritmilor interesează în special cel mai defavorabil caz, deoarece
furnizează cel mai mare timp de execuţie relativ la orice date de intrare de dimensiune dată.
Pe de altă parte
pentru anumiţi algoritmi cazul cel mai defavorabil este relativ frecvent [ CITATION
Com08 \l 1033 ].

12
Capitolul III. Metode de sortare avansată

III.1. Quick Sort. Prezentarea metodei

Quick Sort (sortare rapidă) este o metodă cu performanţe spectaculare concepută de


către C.A.R. Hoare. Această metodă se bazează pe ideea de a creşte eficienţa
interschimbărilor prin mărirea distanţei dintre elementele implicate.
Quick Sort este o metodă de sortare recursivă, bazată pe tehnica “Divide et Impera”,
foarte rapidă, ce foloseşte ca şi idee de bază partiţionarea (mai este numită şi “sortare prin
partiţionare”). Este o metodă de sortare simplă, foarte eficientă pentru liste şi vectori cu un
număr foarte mare de elemente[ CITATION Sor15 \l 1033 ].

III.1.1. Quick Sort. Prezentarea algoritmului

Sortarea prin partitionare se realizeaza conform urmatorului algoritm:


- Fie x un element oarecare al tabloului de sortat a1 , ..., an .

- Tabloul se parcurge de la stânga spre dreapta până se găseşte primul element ai > x .

- Tabloul se parcurge de la dreapta spre stânga până se găseşte primul element aj < x .

- Elementele ai şi aj , se interschimbă;

- Parcurgerea tabloului se continua de la stânga respectiv de la dreapta (din punctele în


care s-a ajuns anterior), până se găsesc alte două elemente care se interschimbă, ş.a.m.d.
- Procesul se termină când cele două parcurgeri se "întâlnesc" undeva în interiorul
tabloului.
- Rezultatul final este că şirul iniţial este partiţionat într-o partiţie dreapta cu chei mai
mari decât x şi o partiţie stânga cu chei mai mici decât x.

-Partiţionarea unui tablou- Pseudocod:

13
procedure Partiţionare {Partiţionează tabloul a[s..d]} *selectează elementul x (de regulă
de la mijlocul intervalului de partiţionat)
repetă
*se caută primul element a[i]>x, parcurgând intervalul de la stânga la dreapta
*se caută primul element a[j]<x, parcurgând intervalul de la dreapta la stânga
Dacă (i<=j) atunci
*interschimbă pe a[i] cu a[j]

până când parcurgerile se întâlnesc (i>j)

- Ȋn continuare, sortarea se realizează astfel:


- După prima partiţionare a secvenţei de elemente se aplică aceeaşi procedură celor
două partiţii rezultate.
- Apoi se aplică aceeaşi procedură celor patru partiţii ale acestora, ş.a.m.d.
- Procesul, se termină când fiecare partiţie se reduce la un singur element.

-Quick Sort- Pseudocod:

procedure QuickSort(s,d);
*partitiţionează intervalul s,d faţă de Mijloc
dacă există partiţie stânga atunci
QuickSort(s,Mijloc-1)

dacă există partiţie dreapta atunci


QuickSort(Mijloc+1,d);

III.1.2. Quick Sort. Analiza complexităţii

14
Ȋn analiza performanţa metodei Quick Sort, se analizează mai întâi partiţionarea.
Timpul de execuţie al metodei Quick Sort depinde de faptul că partiţionarea este echilibrată
sau nu, iar acesta din urmă de elementele alese ca „pivot” pentru partiţionare.
Dacă partiţionarea este echilibrată, algoritmul este la fel de rapid ca sortarea prin
interclasare, altfel, algoritmul se executa la fel de încet ca sortarea prin inserare.

-Partiţionarea în cel mai defavorabil caz:


Cea mai defavorabilă comportare a algoritmului Quick Sort apare în situaţia în care
procedura de partiţionare produce un vector de n-1 elemente şi unul de 1 element.
Presupunem că această partiţionare dezechilibrată apare la fiecare pas al algoritmului.
Deoarece timpul de partiţionare este de O(n), şi T(1) = O(1), formula recursiva pentru timpul
de execuţie a algoritmului Quick Sort este: T(n) = T(n - 1) + O(n)
Ȋn cazul cel mai defavorabil, procedura de partiţie pune întotdeauna într-o parte a vectorului
numai un singur element. Timpul de execuţie rezultat este O(n2).
Dacă partiţionarea este dezechilibrata la fiecare pas recursiv al algoritmului, atunci timpul de
execuţie este O(n2). Timpul de execuţie este O(n2) chiar şi în cazul în care vectorul de intrare
este ordonat .

-Partiţionarea în cel mai favorabil caz:


Dacă algoritmul de partiţionare produce doi vectori de n/2 elemente, algoritmul Quick Sort
lucrează mult mai repede.
Când procedura de partiţie produce întotdeauna parţi egale (cazul cel mai favorabil), timpul
de execuţie rezultat este O(n log2 n).
Ȋn acest caz, formula de recurenţă este: T(n) = 2T(n/2) + O(n), iar soluţia este T(n) = O(n
log2 n). Astfel, partiţionarea cea mai buna produce un algoritm de sortare mult mai rapid.
Partiţionarea echilibrată arată că timpul mediu de execuţie a algoritmului de sortare rapidă
este mult mai apropiat de timpul cel mai bun decât de timpul cel mai rău[ CITATION
Vla10 \l 1033 ].

-Partiţionarea în cazul mediu

15
Ȋn cazul mediu, procedura de partiţie produce un amestec de partiţionări bune şi rele.
Într-un arbore de recurenţa pentru cazul mediu al procedurii de partiţie, partiţionările bune şi
rele sunt distribuite aleator. Presupunem totuşi că partiţionările bune şi rele alternează pe
niveluri, şi că partiţionările bune corespund celui mai bun caz, iar cele rele celui mai
defavorabil caz.
Combinarea unei partiţionări rele şi a uneia bune produce doi vectori de dimensiune (n - 1)/2
şi respectiv (n - 1)/2, cu un cost total de 2n - 1 = O(n). Partiţionarea rezultata este favorabilă,
astfel timpul de execuţie al algoritmului Quick Sort, când partiţionările bune şi rele alternează,
este acelaşi ca în cazul partiţionărilor bune: O (n log2 n).

III.2. Shell Sort. Prezentarea metodei

Metoda Shell (sau sortarea prin inserţie cu micşorarea incrementului)a fost descoperită
de către Donald L. Shell.
Algoritmul Shell Sort reprezintă o generalizare a algoritmului Insertion Sort, fiind un
algoritm de sortare performant ce câştigă viteză permiţând interschimbarea elementelor aflate
la mare distanţă unul faţă de celălalt. Este un algoritm de performanţă ce lucrează pe tablouri
de dimensiune n, fiind de clasă O(n2).
Shell Sort este o metodă de sortare stabilă, având un timp de execuţie accesibil pentru
liste şi vectori ce conţin un număr mare de elemente (mai puţin de 5000 de elemente).

III.2.1. Shell Sort. Prezentarea algoritmului

Ȋn cazul algoritmului Insertion Sort, pentru a insera un element nou în lista de


elemente deja sortate, se deplasează fiecare element în parte, cu câte o poziţie spre dreapta
atât timp cât avem elemente mai mari decât el. Ȋn concluzie, fiecare element înaintează spre
poziţia sa finală cu câte o poziţie.
Algoritmul Shell Sort lucrează asemănător, doar că mută elementele spre poziţia finală cu
mai mult de o poziţie. Se lucrează în iteraţii. Primei iteraţii i se aplică un Insertion Sort cu salt
s1 mai mare decât 1, însemnând că fiecare element din sirul iniţial este mutat spre stânga
cu câte s1 poziţii, cât timp întâlneste elemente mai mari decât el.
Se repetă astfel iteraţiile cu salturi din ce în ce mai mici s2, s3, s4, etc. Ultima iteraţie
făcându-se cu saltul 1, această ultimă iteraţie reprezentând un Insetion Sort clasic.

16
După fiecare iteraţie sirul devine din ce în ce “mai sortat”, iar cum
algoritmul Insertion Sort funcţionează cu atât mai repede cu cât sirul este mai sortat,
vom obţine o îmbunătăţire de viteză[ CITATION She10 \l 1033 ].

-Shell Sort- Pseudocod:


int salt[n] := {99, 25, 3, 7, …., n};
int număr_iteraţii := n;
pentru iter:= numar_iteraţii-1 până la 0
pentru i := salt până la n-1
aux := a[i];
j := i – salt[iter];
cât timp (j>=0) şi (a[j] > aux)
a[j + salt[iter]] := a[j];
j := j – salt[iter];
sfârşit cât timp;
a[j + salt[iter]] := aux;
sfârşit pentru;
sfârşit pentru;

III.2.2. Shell Sort. Analiza complexităţii

Eficienţa algoritmului Shell Sort este greu de analizat şi greu de comparat cu alte
metode de sortare, nici timpul de execuţie nu este cunoscut sigur. Timpul de execuţie nu este
sensibil la ordinea initială a elementelor. Insertion Sort este mai lent pentru cã schimbã doar
elementele apropiate. Shell Sort nu efectuazã mai mult de n comparaţii (pentru creşterile: 1,
4, 13, 40, 121,….). Shell Sort este o metodă aleasă pentru multe aplicaţii de sortare pentru că
are un timp de execuţie accesibil chiar şi pentru o tabelă moderat de mare (mai putin de 5000
elemente) şi este o metodă stabilă.
Cazul cel mai favorabil corespunde şirului ordonat crescător iar cel mai defavorabil
celui ordonat descrescător[ CITATION Int11 \l 1033 ].

Cazul favorabil : O(n log2 n);


Cazul mediu:-;

17
Cazul defavorabil : O(n2);

III.3. Merge Sort. Prezentarea metodei

Merge Sort (sau sortare prin interclasare) a fost inventat de catre John von Neumann
in anul 1945. Este un algoritm ce foloseşte tehnica Divide et Impera, avand o complexitate de
O(n log n). Această metodă de sortare are la bază interclasarea a doi vectori: fiind daţi doi
vectori ordonaţi se va obţine un al treilea vector ordonat ce va conţine elementele celor doi
vectori.

III.3.1. Merge Sort. Prezentarea algoritmului

Algoritmul de sortare Merge Sort se execută conform următorilor paşi:

- Dacă lista are o lungime egala cu 0 sau 1, atunci este deja sortată. Dacă
nu este sortată:
- Lista nesortată se împarte în două subliste aproximativ egale.
- Cele două liste vor fi interclasate și se va obține lista inițială sortată.
Pentru această metodă de sortare, principiul Divide et Impera poate fi privit astfel:
Divide: Împarte lista de n elemente care urmează a fi sortată, în doua subliste de câte n/2
elemente.
Stăpâneşte: Sortează recursiv cele doua subliste utilizând sortarea prin interclasare.
Combină: Interclasează cele două subliste sortate pentru a obţine rezultatul final [ CITATION
Mer15 \l 1033 ].

-Merge Sort- Pseudocod:


MergeSort(v, start, end) // v – vector, start – limită inferioră, end – limită
superioară
if (start == end) return; // condiția de oprire
mid = (start + end) / 2; // etapa divide
MergeSort(v, start, mid); // etapa stăpânește
MergeSort(v, mid+1, end);
Merge(v, start, end); // etapa combină

18
 
Merge(v, start, end) // interclasare sub-vectori
mid = (start + end) / 2;
i = start;
j = mid + 1;
k = 1;
while (i <= mid && j <= end)
if (v[i] <= v[j]) u[k++] = v[i++];
else u[k++] = v[j++];
 
while (i <= mid)
u[k++] = v[i++];
 
while (j <= end)
u[k++] = v[j++];
 
copy(v[start..end], u[1..k-1]);

III.3.2. Merge Sort. Analiza complexităţii

Pentru stabilirea complexităţii algoritmului Merge Sort, se remarcă:


- în cazul unui tablou cu n elemente, numărul de înjumătăţiri succesive până se ajunge
la zone de lungime 1 sau 2 este de log2n;
-numărul de operaţii elementare executate este de ordinul O(n log(n)).
Altfel spus, dacă notăm cu C(n) numărul operaţiilor elementare pentru sortarea unui
tablou cu n elemente şi avem în vedere că, la fiecare pas al algoritmului, se fac două invocări
recursive ale metodei de sortare şi o interclasare, iar interclasarea are complexitatea n, atunci:
C(n) = 2.C(n/2)+n
Ţinând cont de acest raţionament, putem scrie:
C(n) = 2(2C(n/4)+n/2)+n = 4C(n/4) + n + n
Algoritmul se opreşte după log2(n) astfel de paşi, când se obţine
C(n) = nC(n/n) + n + n + ... + n = n.log2(n)
Ȋn consecinţă, complexitatea algoritmului Merge Sort (sortare prin interclasare) este
O(n log2(n))[ CITATION Ign05 \l 1033 ].

Cazul favorabil : O(n log n);


Cazul mediu: O(n log n);
Cazul defavorabil : O(n log n);

19
III.4. Heap Sort. Prezentarea metodei

Heap Sort (sau “sortare prin metoda ansamblelor”) a fost inventată de Wiliams în anul
1964 şi este unul dintre algoritmii de sortare foarte performanţi ce combină calităţile sortării
prin inserţie cu cele ale sortării prin interclasare.
Algoritmul Heap Sort este un algoritm nerecursiv (nu se autoapeleaza), însa este
aproape la fel de performant ca algoritmii recursivi (ex: Quick Sort). HeapSort este un
algoritm de sortare “in situ”, adică nu necesită structuri de date suplimentare, sortarea
făcându-se folosind numai spaţiul de memorie al tabloului ce trebuie sortat [ CITATION Hea15 \l
1033 ].

III.4.1. Heap Sort. Prezentarea algoritmului

Proceduri utilizate în algoritmul de sortare:


-RECONSTITUIE-HEAP are timpul de execuţie O(lg n) şi este de primă importanţă
în întreţinerea proprietăţii de heap.
- CONSTRUTEŞTE-HEAP are un timp de execuţie liniar şi generează un heap dintr-
un vector neordonat furnizat la intrare.
-HEAPSORT se executăîn timpul O(nlgn) şi ordoneazăun vectorîn spaţiul alocat
acestuia.
-EXTRAGE-MAX şi INSEREAZĂse executăîn timpul O(lg n);utile în generarea
unei cozi de prioritate dintr-un heap.
Procedura Reconstituie-Heap este un subprogram important în prelucrarea heap-
urilor.
Datele de intrare ale acestei proceduri sunt un vector A şi un indice i din vector. Când se
apelează Reconstituie-Heap, se presupune că subarborii, având ca rădăcini nodurile
Stânga(i) respectiv Dreapta(i), sunt heap-uri. Dar, dacă elementul A[i] este mai mic decât
descendenţii săi, acesta nu respectă proprietatea de heap. Procedura Reconstituie-Heap
“scufundă” în heap valoarea A[i], astfel încât subarborele care are în rădăcină valoarea
elementului de indice i, să devina un heap[CITATION Wik \l 1033 ].

20
-Heap Sort- Pseudocod:
RECONSTITUIE-HEAP(A, i)
1: left←STÂNGA(i)
2: right←DREAPTA(i)
3: dacăleft≤dimensiune-heap[A] şi A[left] > A[i] atunci
4: maxim ←left
5: altfel
6: maxim ←i
7: dacăright≤dimensiune-heap[A] Şi A[right] > A[maxim] atunci
8: maxim ←right
9: dacămaxim ≠i atunci
l0: schimbăA[i] ↔A[maxim]
11: RECONSTITUIE-HEAP(A, maxim)
CONSTRUIEŞTE-HEAP(A)
1: dimesiune-heap[A] ←lungime[A]
2: pentrui ←lungime[A]/2, 1 execută
3: RECONSTITUIE-HEAP(A, i)
HEAPSORT(A)
1: CONSTRUIEŞTE-HEAP(A)
2: pentru i ←lungime[A], 2 execută
3: schimbăA[1] ↔A[i]
4: dimesiune-heap[A] ←dimesiune-heap[A] −1
5: RECONSTITUIE-HEAP(A, 1)
EXTRAGE-MAX-D1N-HEAP(A)
1: dacă dimesiune-heap[A] < 1 atunci
2: eroare"depăşire inferioară heap"
3: max ←A[1]
4: A[1] ←A[dimensiune-heap[A]]
5: dimensiune-heap[A] ←dimensiune-heap[A] −1
6: RECONSTITUIE-HEAP(A, 1)
7: returnează maxim
INSEREAZĂ-ÎN-HEAP(A, cheie)
1: dimensiune-heap[A] ←dimensiune-heap[A] + 1

21
2: i ←dimensiune-heap[A]
3: cât timpi > 1 şi A[PĂRINTE(i)] < cheie execută
4: A[i] ←A[PĂRINTE(i)]
5: i ←PĂRINTE(i)
6: A[i] ←cheie

III.4.2. Heap Sort. Analiza complexităţii

-Procedura Reconstituie-Heap are timpul de execuţie O (log2 n), iar în întreţinerea


proprietăţii de heap, este de primă importanţă.

-Procedura Construieşte-Heap are un timp de execuţie linear, iar dintr-un vector


neordonat, furnizat la intrare, generează un heap.

-Procedura Heap Sort are timpul de execuţie O(n log2 n) şi ordonează un vector în
spaţiul alocat acestuia.

Cazul favorabil : O(n log n);


Cazul mediu: O(n log n);
Cazul defavorabil : O(n log n);

III.5. Insertion Sort. Prezentarea metodei

Insertion Sort (sau sortarea prin inserţie), este un algoritm eficient pentru sortarea
unei liste sau vector ce conţin un numar mic de elemente. Sortarea prin inserţie funcţionează
în acelaşi mod în care oamenii aranjează un pachet de cărţi de joc. Se începe cu pachetul
aşezat pe masă, cu faţa în jos şi cu mâna stângă goală. Apoi, luăm câte o carte de pe masă şi o
inserăm în poziţia corectă în mâna stângă. Pentru a găsi poziţia corectă pentru o carte dată, o
comparăm cu cărţile aflate deja în mâna stângă, de la dreapta la stânga, sau de la stânga la
dreapta.

III.5.1. Insertion Sort. Prezentarea algoritmului

La fiecare pas al algoritmului, elementului x[i] i se caută poziţia potrivită în subtabloul


destinaţie x[1..i−1] (tablou care este deja ordonat) comparând pe x[i] cu elementele din

22
x[1..i−1] începând cu x[i−1] şi generând spaţiu, prin deplasarea către dreapta a elementelor
mai mari decât x[i]. Structura generală este prezentată în algoritmul inserţie.
-Insertion Sort- Pseudocod:

1: pentru j ← 2, lungime[a] executa


2:     cheie ← a[j]   
3:     i ← j - 1
4:     cât timp i > 0 şi a[i] > cheie executa
5:         a[i + 1] ← a[i]
6:         i ← i – 1
7:     a[i + 1] ← cheie  {Insereaza a[j] în sirul sortat a[1..j - 1]}

III.5.2. Insertion Sort. Analiza complexităţii

Timpul de execuţie necesar algoritmului Insertion Sort depinde de datele de intrare.


Cazul cel mai favorabil corespunde şirului ordonat crescător iar cel mai defavorabil
corespunde celui ordonat descrescător.
Ȋn cazul cel mai defavorabil, sortarea prin inserţie are un timp de execuţie de O(n2) .

În general, un algoritm se consideră ca fiind mai eficient decât altul dacă timpul său
de execuţie în cazul cel mai defavorabil are un ordin de creştere mai mic. Aceasta evaluare
poate fi incorectă pentru date de intrare de dimensiune mica, însă în cazul unor date de
intrare de dimensiuni foarte mari, un algoritm de tipul O(n2), de exemplu, va fi executat în
cazul cel mai defavorabil mult mai repede decât unul de tipul O(n3).
Aşadar, sortarea prin inserţie are un ordin de complexitate O(n2)[ CITATION Ins14 \l 1033 ].

III.6. Comb Sort. Prezentarea metodei

Comb Sort (sau sortare prin pieptănare) este o metodă de sortare relativ simplă,
proiectată iniţial de Włodzimierz Dobosiewicz în 1980 şi redescoperită în anul 1991 de
Stephen Lacey şi Richard Box.
Comb Sort este o îmbunătăţire a metodei de sortare Bubble Sort.
Ideea de bază a acestei sortări este de a muta “ţestoasele” (valorile mici) aproape de
sfârşitul listei, deoarece acestea încetinesc foarte mult sortarea, iar “iepurii” (valorile mari)
vor fi mutate la începutul listei[ CITATION CAl15 \l 1033 ].

23
III.6.1. Comb Sort. Prezentarea algoritmului

Ȋn cazul metodei Bubble Sort, când oricare două elemente sunt comparate, există
întotdeauna un decalaj egal cu 1. Ideea de bază a metodei de sortare prin pieptănare este că
decalajul poate fi mai mare decât 1.
Cu alte cuvinte, bucla interioară a metodei Bubble Sort, ce realizează schimbul dintre
elemente, este modificată pe măsură ce distanţa dintre elementele schimbate scade (pentru
fiecare iteraţie a buclei exterioare) în trepte ale factorului de micşorare, adică [dimensiunea
intrărilor/factor de micşorare, dimensiunea intrărilor/factor de micşorare^2, dimensiunea
intrărilor/factor de micşorare^3,…, 1]. Spre deosebire de metoda Bubble Sort, în care
decalajul este constant, adică este egal cu 1.
Decalajul începe ca lungime a listei sortate împărţită la factorul de micşorare (în
general 1.3), lista fiind sortată cu această valoare. Apoi, decalajul este împărţit la factorul de
micşorare, lista este sortată cu noul decalaj, iar procesul se repetă până când decalajul devine
1. Ȋn acest moment, sortarea prin pieptănare continuă să folosească decalajul care este egal
cu 1, până când lista este complet sortată. Etapa finală a acestei sortări este asemănătoare cu
cea a metodei Bubble Sort, doar că au fost mutate mai multe “ţestoase” (valori mici).

III.6.2. Comb Sort. Analiza Complexităţii

Factorul de micşorare are un mare efect asupra eficienţei sortării prin pieptănare.
Ȋn articolul original, autorii au sugerat pentru factorul de micşorare valoarea 1.3. Se
consideră că o valoare prea mică încetineşte algoritmul, deoarece trebuiesc făcute mai multe
comparaţii, iar în cazul unei valori prea mari, nu se vor mai face comparaţii.
Stephen Lacey şi Richard Box au testat metoda Comb Sort pe 200000 de liste
aleatorii şi au găsit factorul de micşorare egal cu 1.3 ca fiind cel mai bun [ CITATION Alg15 \l
1033 ].
Sortarea prin pieptănare are următorul ordin de complexitate:

Cazul favorabil : O(n);


Cazul mediu: O(n2);

24
Cazul defavorabil : O(n2);

Capitolul IV. Descrierea aplicaţiei. Demonstraţie

Lucrarea propune analiza din punct de vedere teoretic şi practic a unei serii de
algoritmi de sortare (Quick Sort, Shell Sort, Merge Sort, Heap Sort, Insertion Sort şi
Comb Sort) precum şi testarea acestora în cele trei cazuri (favorabil, mediu şi nefavorabil),
asupra a cinci vectori construiţi aleator, formaţi din 500 de elemente.
Ȋn realizarea aplicaţiei au fost parcurse mai multe etape, etape ce vor fi detaliate pe
larg în acest capitol.
Pentru realizarea aplicaţiei a fost ales limbajul de programare C++ deoarece este
unul dintre cele mai populare limbaje de programare, fiind un limbaj general, compilat, ce
suportă programare procedurală, programare orientată pe obiecte şi abstractizare a datelor.
Programul C++ a fost implementat cu ajutorul platformei CodeBlocks, versiunea
13.12mingw, versiune ce conţine compilatorul GCC (sau GNU Compiler Collection) care
este unul dintre cele mai performante compilatoare pentru C/C++ şi depanatorul GDB (sau
GNU Debugger) ce permite programatorului să vizualizeze ceea ce se întâmplă în cadrul

25
unui program în timp ce se execută sau ce anume s-a întâmplat înainte ca un program să se
termine cu o eroare.
Pentru exprimarea rezultatelor obţinute (timpul de execuţie şi numărul de operaţii
efectuate) în urma implementării programului şi pentru evidenţierea comportamentului
metodelor avansate de sortare în cele trei cazuri (favorabil, mediu şi nefavorabil) au fost
realizate grafice cu ajutorul soft-ului Microsoft Excel 2010, din pachetul Microsoft Office
2010, datorită faptului că este unul dintre cele mai folosite programe pentru realizarea de
grafice, este uşor de folosit, generând pe baza tabelelor, de la grafice simple până la grafice
în format 2D sau 3D.
Ȋn funcţie de declaraţii şi definiri de funcţii, constante şi variabile folosite pentru
realizarea programului C++ au fost utilizate următoarele biblioteci:
#include <iostream> -Bibliotecă pentru intrări şi ieşiri standard de date;
#include <cstdlib> -Bibliotecă pentru generarea de numere aleatoare;
#include <ctime> -Bibliotecă pentru manipularea datei şi timpului;
#include <fstream> -Bibliotecă pentru lucrul cu fişiere.

Programul începe cu secvenţele de construcţie a cinci vectori ce vor conţine un


număr de 500 de elemente, elemente ce vor fi generate aleator şi nu vor fi introduse de către
utilizator.
Vectorii sunt declaraţi ca şi pointeri în care vor fi stocate adresa de memorie a
datelor de tip Integer : typedef int * vector.
Ȋn funcţiile de construcţie a vectorilor a fost folosită funcţia srand(), funcţie ce
declanşează generatorul de numere aleatoare rand(). Generatorul va furniza valori cuprinse
între 0 şi RAND_MAX.

Primul vector construit este un vector complet aleator.

Fig.1 Secvenţă de construcţie a vectorului complet_aleator

Următorii doi vectori construiţi sunt numiţi vectori relativ aleatori, unul va conţine

26
elemente care cresc, iar celălat, elemente care scad.

Fig. 2 Secvenţă de construcţie a vectorilor relativ_aleatori


Ultimii doi vectori construţi sunt numiţi vectori direcţionat aleatori, fiind semisortaţi
crescator respectiv descrescator.

Fig. 3 Secvenţă de construcţie a vectorilor direcţionat_aleatori


Ȋn continuare sunt implementate şase metode avansate de sortare asupra fiecăruia
dintre vectorii construiţi aleator. Sortările sunt implementate în ordine după cum urmează:
Quick Sort, Shell Sort, Merge Sort, Heap Sort, Insertion Sort şi Comb Sort.
Pentru fiecare sortare în parte a fost determinat timpul de execuţie cu ajutorul
funcţiei clock():
start = clock();
stop = clock();
cout<<"Timpul de execuţie este: "<<float( clock () - start ) /
CLOCKS_PER_SEC<<" secunde !"<<endl;
şi numărul de operaţii efectuate cu ajutorul contorului nr, care a fost iniţializat cu
valoarea 0 şi incrementat la fiecare operaţie efectuată.
Ȋn cazul metodei Quick Sort ce foloseşte partiţionarea ca şi idee de bază, mai intâi a
fost calculat mijlocul vectorului, cu ajutorul căruia vectorul a fost împărţit în alţi doi
subvectori.
Subvectorii sunt parcurşi de la dreapta la stânga şi de la stânga la dreapta cu ajutorul

27
contorilor i şi j, iar de fiecare dată când sunt găsite elemente a i >x şi aj<x elementele sunt
interschimbate, procesul repetându-se până când cele două parcurgeri se întâlnesc undeva în
interiorul tabloului.

Fig.4 Quick Sort. Secvenţă de interschimbare a elementelor

Ȋn cazul metodei Quick Sort nu a mai fost folosită funcţia clock() deoarece Quick
Sort este o metodă de sortare recursivă, adică se autoapelează, iar timpul de execuţie este
generat automat.

28
Fig. 5. Vector complet_aleator sortat cu QuickSort

Rezultatele obţinute, adică numărul de operaţii şi timpul de execuţie pentru fiecare


sortare în parte aplicată celor cinci vectori, sunt afişate într-un fişier extern numit MyFile:

outputFile.open("MyFile.txt");
Rezultatele obţinute vor fi afişate astfel:

29
Fig.6 Rezultate Quick Sort afişate în fişierul extern MyFile

Pe baza rezultatelor obţinute, au fost create tabele în MS Excel cu ajutorul cărora au


fost generate grafice pentru a evidenţia comportamentul sortării în cele trei cazuri (favorabil,
mediu şi defavorabil).
Pentru cazul mediu au fost luate în considerare rezultatele obţinute în urma sortării
vectorului complet_aleator, pentru cazul favorabil au fost luate în considerare rezultatele
obţinute în urma sortării vectorului relativ_aleator sortarea fiind crescătoare, iar pentru cazul
defavorabil, au fost luate în considerare rezultatele obţinute în urma sortării vectorului
relativ_aleator, sortarea fiind descrescătoare. Din cauza faptului că elementele vectorilor
sunt aşezate în ordine aleatoare, la fiecare compilare a programului, rezultatele vor fi
diferite.Tabelele şi graficele au fost realizate separat pentru timpul de execuţie şi numărul de
operaţii obţinute la un moment dat.

30
Fig. 7 Quick Sort-grafic pentru exprimarea timpului de execuţie

Fig.8 Quick Sort-grafic pentru exprimarea numărului de operaţii

Din graficele de mai sus putem observa că în cazul favorabil (în care sortarea
produce întotdeauna părţi egale), Quick Sort este cea mai rapidă având timpul de execuţie

31
T(n) = O(n log2 n). Sortarea are acelaşi timp de execuşie T(n) = O(n log2 n) şi în cazul
mediu din cauza faptului că partiţiile bune şi rele alternează, iar în cazul cel mai defavorabil
în care partiţionarea este dezechilibrată, timpul de execuţie este O(n2), sortarea fiind lentă.

Metoda Shell Sort ce reprezintă o generalizare a metodei Insertion Sort, mută


elementele spre poziţia finală cu mai mult de o poziţie, lucrând în iteraţii. Primei iteraţii i se
aplică un Insertion Sort cu salt s1 mai mare decât 1, însemnând că fiecare element din sirul
iniţial este mutat spre stânga cu câte s1 poziţii, cât timp întâlneste elemente mai mari decât
el.
Se repetă astfel iteraţiile cu salturi din ce în ce mai mici s2, s3, s4, etc. Ultima iteraţie
făcându-se cu saltul 1, această ultimă iteraţie reprezentând un Insetion Sort clasic.
Rezultatele obţinute vor fi afişate în fişierul extern MyFile.

Fig.9 Rezultate Shell Sort afişate în fişierul extern MyFile

Graficele rezultate în urma rezultatelor obţinute sunt următoarele:

32
Fig.10 Shell Sort-grafic pentru exprimarea timpului de execuţie

Fig.11 Shell Sort-grafic pentru exprimarea numărului de operaţii


Din graficele obţinute putem observa că Shell Sort este mai rapidă în cazul favorabil în
care sortarea se va face crescător având timpul de execuţie O(n log2 n), în cazul defavorabil în
care sortarea se face descrescător timpul de execuţie este O(n2), iar în cazul mediu timpul de

33
execuţie nu este cunoscut sigur, acesta depinzând de decalajul dintre elemente.
Metoda Merge Sort este o sortare recursivă ce foloseşte ca idee de bază principiul
Divide et Impera. Algoritmul împarte vectorul în alţi doi subvectori ce vor fi sortaţi recursiv
prin interclasare, iar cei doi subvectori sortaţi sunt interclasaţi pentru a obţine rezultatul
final.
Rezultatele obţinute vor fi afişate în fişierul extern MyFile.

Fig.12 Rezultate Merge Sort afişate în fişierul extern MyFile


Graficele rezultate în urma rezultatelor obţinute sunt următoarele:

Fig.13 Merge Sort-grafic pentru exprimarea timpului de execuţie

34
Fig.14 Merge Sort-grafic pentru exprimarea numărului de operaţii
Ȋn cazul metodei Merge Sort numărul de operaţii este egal în toate cele tre cazuri
(favorabil, mediu şi defavorabil), însă timpul de execuţie demonstrează faptul că sortarea
este mai rapidă în cazul favorabil, chiar dacă este acelaşi pentru cele trei cazuri şi anume
O(n log n).

Metoda Heap Sort este o metodă ce combină calităţile sortării prin inserţie cu cele
ale sortării prin interclasare. Este un algoritm nerecursiv.
Ȋn cazul acestui algoritm la fiecare pas se determină cel mai mare element dintre
A[i], A[STÂNGA(i)] şi A[DREAPTA(i)], iar indicele său se păstrează în variabila maximă.
DacăA(i) este cel mai mare, atunci subarborele având ca rădăcină nodul i este un heap şi
procedura se termină. În caz contrar, cel mai mare element este unul dintre cei doi
descendenţi şi A[i] este interschimbat cu A[maxim]. Astfel, nodul i şi descendenţii săi
satisfac proprietatea de heap. Nodul maxim are acum valoarea initial a lui A[i], fiind posibil
ca subarborele de rădăcină maximă să nu îndeplinească proprietatea de heap. Rezultă

necesitatea apelării recursive a procedurii RECONSTITUIE-HEAP pentru acest arbore.

Rezultatele obţinute vor fi afişate în fişierul extern MyFile.

35
Fig.15 Rezultate Heap Sort afişate în fişierul extern MyFile
Graficele rezultate în urma rezultatelor obţinute sunt următoarele:

Fig.16 Heap Sort-grafic pentru exprimarea timpului de execuţie

36
Fig.17 Heap Sort-grafic pentru exprimarea numărului de operaţii
Ȋn toate cele trei cazuri algoritmul Heap Sort are timpul de execuţie egal cu O(n log n), însă
putem observa că în cazul favorabil sortarea Heap Sort este mult mai rapidă executând un număr
minim de operaţii.

Fig.18 Rezultate Insertion Sort afişate în fişierul extern MyFile


Ȋn cazul metodei Insertion Sort, la fiecare pas al algoritmului, elementului x[i] i se
caută poziţia potrivită în subtabloul destinaţie x[1..i−1] (tablou care este deja ordonat)
comparând pe x[i] cu elementele din x[1..i−1] începând cu x[i−1] şi generând spaţiu, prin

37
deplasarea către dreapta a elementelor mai mari decât x[i].
Rezultatele obţinute vor fi afişate în fişierul extern MyFile.
Graficele rezultate în urma rezultatelor obţinute sunt următoarele:

Fig.19 Insertion Sort-grafic pentru exprimarea timpului de execuţie

Fig.20 Insertion Sort-grafic pentru exprimarea numărului de operaţii


Ȋn cazul mediu şi defavorabil, metoda Insertion Sort are timpul de execuţie egal cu O(n2),

38
iar în cazul favorabil timpul de execuţie este O(n). Putem astfel observa din grafice că sortarea este
mai rapidă în cazul favorabil.
Metoda Comb Sort este o îmbunătăţire a metodei Bubble Sort. Acest algoritm mută valorile
mici spre sfârşitul vectorului, iar valorile mari spre începutul acestuia. Ȋn cazul metodei Bubble
Sort, când oricare două elemente sunt comparate, există întotdeauna un decalaj egal cu 1.
Ideea de bază a metodei de sortare prin pieptănare este că decalajul poate fi mai mare decât
1.
Decalajul începe ca lungime a listei sortate împărţită la factorul de micşorare (în
general 1.3), lista fiind sortată cu această valoare.
gap = (int) ((float) gap / 1.3);
Rezultatele obţinute vor fi afişate în fişierul extern MyFile.

Fig.21 Rezultate Comb Sort afişate în fişierul extern MyFile


Metoda Comb Sort are un timp de execuţie egal cu O(n2) în cazurile favorabil şi
defavorabil, iar în cazul mediu O(n).
Din grafice putem observa că în cele trei cazuri a fost executat acelaşi număr de
operaţii, iar timpul de execuţie este cel mai mic în cazul favorabil.
Graficele rezultate în urma rezultatelor obţinute sunt urmatoarele:

39
Fig.22 Comb Sort-grafic pentru exprimarea timpului de execuţie

Fig.23 Comb Sort-grafic pentru exprimarea numărului de operaţii


Pentru a putea compara cele şapte metode de sortare în cele trei cazuri (favorabil,
mediu şi defavorabil), pentru a le putea compara şi stabili care este cea mai eficientă metodă
în cazul vectorilor de dimensiuni mari construiţi aleator au fost realizate graficele de pe
paginile următoare..

40
Fig. 24 Grafic pentru compararea metodelor de sortare din punct de vedere al
timpului de execuţie

41
Fig. 24 Grafic pentru compararea metodelor de sortare din punct de vedere al
numărului de operaţii

42
După cum putem observa, cele mai eficiente metode de sortare sunt Merge Sort şi
Comb Sort, având în cazul favorabil (sortarea vectorului relativ_aleator crescător) cel mai mic
timp de execuţie şi anume 0.098 secunde.
Cea mai ineficientă metodă de sortare din punct de vedere al timpului de execuţie este
metoda Quick Sort având un timp de execuţie de 0.156 secunde.
Din punct de vedere al numărului de operaţii cea mai eficientă metodă este metoda
Merge Sort având în cazul favorabil cel mai mic număr de operaţii-499.
După cum putem observa, cele mai eficiente metode de sortare sunt Merge Sort şi
Comb Sort, având în cazul favorabil (sortarea vectorului relativ_aleator crescător) cel mai mic
timp de execuţie şi anume 0.098 secunde.
Cea mai ineficientă metodă de sortare din punct de vedere al timpului deCea mai
ineficientă metodă de sortare este considerată metoda Comb Sort, având în cazul defavorabil
cel mai mare număr de operaţii- 16837.

43
Concluzii

Ȋn lucrarea de faţă am realizat o prezentare şi o analiză din punct de vedere teoretic şi


practic a unei serii de algoritmi de sortare avansată (Quick Sort, Shell Sort, Merge Sort, Heap
Sort, Insertion Sort, Comb Sort) şi am prezentat conceptele teoretice ce stau la baza acestora
precum şi complexitatea lor în timp şi spaţiu.
Am implementat aceste metode de sortare asupra a cinci vectori construiţi aleator,
formaţi dintr-un număr de 500 de elemente, iar pentru a compara şi alege cele mai eficiente
metode de sortare pe baza rezultatelor obţinute (număr de operaţii şi timp de execuţie) am
realizat grafice în MS Excel.
Folosind aceste rezultate am subliniat diferenţele dintre algoritmi precum şi
comportamentul lor în cele trei cazuri (favorabil, mediu şi defavorabil).
Ȋn concluzie, putem declara algoritmul de sortare Merge Sort ca fiind cel mai eficient
dintre algoritmii analizaţi, atât din punct de vedere al timpului de execuţie cât şi al numărului
de operaţii efectuate.

44
Bibliografie

Proiectarea şi analiza algoritmilor - algoritmi de sortare. (1996). Bucuresti: All Educational.

(2008, 12 2). Retrieved from Complexitatea algoritmilor:


http://upm.ro/intranet/ecalin/cd_educational/cd/progr/complexitate_curs.html

Shell Sort. (2010, 5 18). Retrieved from Sorting Algorithms: http://www.iti.fh-


flensburg.de/lang/algorithmen/sortieren/shell/shellen.htm

Intro to Algorithms. (2011, 3 11). Retrieved from Sorting Algorithms - Shellsort:


http://faculty.simpson.edu/lydia.sinapova/www/cmsc250/LN250_Weiss/L12-
ShellSort.htm

Metode de Sortare. (2012, Iulie 1). Retrieved from Weebly: http://sortari.weebly.com/

(2013, 30). Retrieved from Tehnici avansate de sortare a tablourilor:


http://www.cs.upt.ro/sda/lab_sort_av.htm

Insertion Sort. (2014, 12 3). Retrieved from CForBeginners:


http://cforbeginners.com/insertionsort.html

(2015, 7 1). Retrieved from Sortarea rapidă (Quick Sort): http://infoscience.3x.ro/c+


+/Sortarea_rapid%E3.htm

Algorithm Implementation/Sorting/Comb sort. (2015). Retrieved from WikiBooks:


https://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Comb_sort

C Algorithms – Comb Sort. (2015, 7 1). Retrieved from exforsys:


http://www.exforsys.com/tutorials/c-algorithms/comb-sort.html

Heap Sort. (2015). Retrieved from Wikipedia: https://en.wikipedia.org/wiki/Heapsort

Merge Sort. (2015, 6 30). Retrieved from Wikipedia:


https://en.wikipedia.org/wiki/Merge_sort

Sorting algorithms/Heapsort. (2015). Retrieved from Wikipedia:


http://rosettacode.org/wiki/Sorting_algorithms/Heapsort

Burdescu, D. (1996). Analiza complexităţii algoritmilor. Craiova: Tipografia Universităţii


Craiova.

Ignat, I. (2005). Structuri de date si algoritmi. Bucuresti: Albastra.

45
Razvan Andonie, I. (1995). Algoritmi fundamentali. O perspectiva C++. Cluj-Napoca:
Editura Libris.

Ungureanu, G. M. (2012). Structuri de date si algoritmi. Bucuresti: Matrix Rom.

Vlad Sebastian Ionescu, E. L. (2010). Algoritmica C++. Bucuresti: Matrix Rom.

46

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