Documente Academic
Documente Profesional
Documente Cultură
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
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 ].
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:
5
I.3. Caracteristicile metodelor de sortare
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
7
II.2.1. Complexitatea spaţiu de memorare
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 ].
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
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ă
- 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ă;
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]
procedure QuickSort(s,d);
*partitiţionează intervalul s,d faţă de Mijloc
dacă există partiţie stânga atunci
QuickSort(s,Mijloc-1)
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.
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).
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).
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 ].
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 ].
17
Cazul defavorabil : O(n2);
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.
- 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 ].
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]);
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 ].
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
-Procedura Heap Sort are timpul de execuţie O(n log2 n) şi ordonează un vector în
spaţiul alocat acestuia.
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.
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:
Î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 ].
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).
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:
24
Cazul defavorabil : O(n2);
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.
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.
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.
Ȋ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
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
30
Fig. 7 Quick Sort-grafic pentru exprimarea timpului de execuţie
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ă.
32
Fig.10 Shell Sort-grafic pentru exprimarea timpului de execuţie
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.
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ă
35
Fig.15 Rezultate Heap Sort afişate în fişierul extern MyFile
Graficele rezultate în urma rezultatelor obţinute sunt următoarele:
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.
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:
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.
39
Fig.22 Comb Sort-grafic pentru exprimarea timpului de execuţie
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
44
Bibliografie
45
Razvan Andonie, I. (1995). Algoritmi fundamentali. O perspectiva C++. Cluj-Napoca:
Editura Libris.
46