Algoritmi/Matematičke osnove

Pre nego što počnemo da se bavimo algoritamskim tehnikama, moramo da izučimo neophodne matematičke osnove (matematički aparat). Prvo ćemo da pokrijemo matematičke definicije termina koji se koriste u knjizi. Proširenjem matematičkog vokabulara, bićete mnogo precizniji i u stanju da mnogo jednostavnije formulišete problem. Zatim ćemo obraditi tehnike analize vremena izvršenja algoritma. Posle svakog bitnog algoritma koji je obrađen u knjizi, sledi analiza vremena izvršenja i dokaz ispravnosti.

Asimptotska notacija

uredi

Uz ispravnost, važna odlika korisnog algoritma je vreme izvršenja i utrošeni memorijski prostor. Vreme i memorija su važni resursi i među njima postoji bitna razlika u načinu korišćenja, čak i ako ih ima u izobilju. Kako se može izmeriti potrošnja resursa? Jedan način je da napravite funkciju koja odražava njihovo upotrebu u odnosu na neke karakteristike ulaza. Jedna od uobičajenih karakteristika ulaza je veličina skupa podataka. Na primer, pretpostavimo da je ulaz nekog algoritma vektor od   celih brojeva. Vreme izvršenja algortma možemo da opišemo funkcijom   koja zavisi od  .

 

gde je vrednost funkcije   izražena u nekoj jedinici vremena. (U ovom razmatranju glavni fokus je na vremenu, ali isto može da se primeni i na utrošak memorije). Retko se kao jedinica vremena koristi sekunda, jer vreme izvršenja zavisi od mašine, njenog operativnog sistema i trenutnog opterećenja. Umesto u sekundama, vreme se najčešće izražava kao potreban broj nekih osnovnih operacija računara. Na primer, neke od osnovnih operacija koje se koriste su: potreban broj sabiranja ili množenja; broj poređenja (komparacija); izvedeni broj zamena memorijskih lokacija; ili broj izvršenih mašinskih instrukcija. Generalno, mogli bismo se referisati samo na izvršenje navedenih osnovnih operacija.

Da li je ovo dobar pristup za određivanje utroška resurs jednog algoritma? I da i ne. Kada su dva algoritma slična po utrošku vremena, može da pomogne precizna funkcija koja bi odredila koji je algoritam brži u datim uslovima. Ali u mnogim slučajevima je teško ili nemoguće dati tačan analitički opis potrebnog broja operacija, naročito kada algoritam izvršava operacije uslovno - u zavisnosti od vrednosti ulaza. Umesto toga, ono što je zaista važno nije tačno vreme potrebno da se izvrši određena funkcija, već stepen promene utroška resursa u zavisnosti od ulaza. Konkretno, razmotrićemo dve prikazane funkcije koje daju potrebno vreme izvršenja u odnosu na veličinu ulaznog skupa podataka:

 
 

One izgledaju prilično različito, ali kako se ponašaju? Pogledajmo nekoliko grafika ovih funkcija (  u crvenoj,   u plavoj boji):

 
Grafikoni f i g, za opseg 0 do 5
 
Grafikoni f i g, za opseg 0 do 15
 
Grafikoni f i g, za opseg 0 do 100
 
Grafikoni f i g, za opseg 0 do 1000


U prvom, jako ograničenom grafiku krive izgledaju različito. U drugom krive kreću u sličnom pravcu, u trećem postoji samo mala razlika i u poslednjem su skoro identične. U stvari one se približavaju dominantnom članu. Što je n veće, ostali članovi postaju manje značajni u odnosu na  . Kao što se može videti, promena koeficijenata članova nižeg reda algoritma polinomijalnog vremena ne pomaže mnogo. Ono što je stvarno važno je koeficijent člana najvišeg reda. Zato smo usvojili posebnu notaciju za ovu vrstu analize. Tako je:

 

Zanemarujemo članove nižeg reda. Možemo da kažemo da je:

 

Ovo nam pruža način da lakše poredimo različite algoritme. Za sortiranje   elemenata umetanjem (insertion sort), potrebno je reda veličine   koraka. Za sortiranje spajanjem (merge sort) potrebno je   koraka. Tako, ako je skup ulaznih podataka dovoljno velik, sortiranje spajanjem je brže od sortiranja umetanjem.

Generalno pišemo da važi:

 

kada je zadovoljeno:

 

Ovo znači da   važi ako i samo ako postoje konstante   i   veće od nule, tako da za svaki  ,   funkcija je pozitivna i manja ili jednaka  . Primetimo da znak jednakosti koji se koristi u gornjem zapisu, opisuje relaciju između   i   , a ne istinsku ekvivalentnost. U svetlu toga, neki definišu veliko O u terminima skupova, tako da je:

 

kada je:

 

Veliko O notacija je samo gornja granica – supremum; oba sledeća iskaza su tačna:

 
 

Ako uzmemo da znak jednakosti označava ekvivalenciju, dobićemo jako čudan rezultat, tj. da je:

 

što je očigledna besmislica. Zato je definicija na bazi teorije skupova zgodna. Zabuna može da se izbegne time što ćete znak jednakosti interpretirati kao jednosmeran tj.:

 

ne implicira da važi i:

 

Uvek držite O na desnoj strani znaka jednakosti.

Veliko omega

uredi

Ponekad nam nije dovoljno da odredimo samo gornju granicu vremena izvršenja neke funkcije. Veliko omega nam daje donju granicu – infinum. Generalno, kažemo da je:

 

kada važi:

 

tj.   ako i samo ako postoje konstante c i n0 takve da je za svako n>n0, f(n) pozitivna i veća ili jednaka cg(n). Tako, na primer, možemo da tvrdimo da je:

 , (c=1/2, n0=4) ili
 , (c=1, n0=3),

ali ne možemo da je

 

Veliko teta

uredi

Kada je data funkcija istovremeno i O(g(n)) i Ω(g(n)), kažemo da je Θ(g(n)), i tada imamo tesne granice funkcije. Funkcija f(n) je Θ(g(n)) kada važi:

 

Ali u većini slučajeva kada pokušavamo da utvrdimo da je zadata funkcija  , umesto da koristimo gornju definiciju, pokazujemo da je ona istovremeno i O(g(n)) i Ω(g(n)).

Malo o i omega

uredi

Kada asimptotske granice nisu tesne, to možemo da izrazimo tako što kažemo da je   ili  . Definiše se:

f(n) je o(g(n)) akko   i
f(n) je ω(g(n)) akko  

Primetimo da je funkcija f u o(g(n)) kada za svaki koeficijent g, g na kraju postaje veća od f, dok je za O(g(n)) potrebno da postoji jedan koeficijent za koji g postaje najmanje jednak f.

Analiza algoritama: rešavanje diferencnih jednačina

uredi

Za sortiranje n elemenata spajanjem:  . Ovo opisuje jednu iteraciju sortiranja: prostor problema   se svodi na dve polovine ( ), koje se spajaju po okončanju svih rekurzivnih poziva ( ). Ovaj sistem obeležavanja (notacija) je osnova za analizu algoritama, tako da treba što pre da se na njega naviknete. Postoje teoreme koje možete da koristite da bi procenili vreme velikog O za funkciju čija se diferencna (rekurentna) jednačina uklapa i određeni šablon.

Glavna teorema

uredi

Razmotrimo diferencnu jednačinu koja se uklapa u formulu:

 

za a ≥ 1, b > 1 i k ≥ 0. Ovde je a broj rekurzivnih poziva za jedan poziv funkcije, n je veličina ulaza, b je faktor za koji se ulaz smanjuje i k je polinomijalni red operacije koja se pojavljuje svaki put kada se pozove funkcija (osim za bazne slučajeve). Na primer, kod algoritma sortiranja spajanjem, koji ćemo pokriti kasnije, imamo da je

 

zato što se dva potproblema pozivaju (rešavaju) za svaku iteraciju slučaja koji nije bazni i veličina vektora se svaki put deli na pola.   je na kraju „pobednički“ deo ove podele i pobednički algoritam: on troši linearno vreme za spajanje rezultata dva rekurzivna poziva u jedan konačni rezultat.

Ako zamislimo rekurzivne pozive T kao formiranje stabla, postoje tri moguća slučaja u kojima može da se utvrdi gde se većina vremena izvršenja algoritma troši („većina“ u smislu koji se odnosi na njegovo asimptotsko ponašanje):

  1. stablo može da bude razgranato pri vrhu i tada se većina vremena se troši za vreme poziva blizu korena;
  2. stablo može da bude uravnoteženo, kada je utrošak vremena ravnomerno raspoređen;
  3. stablo može da bude razgranato pri dnu, kada se većina vremena troši na pozive blizu listova.

U zavisnosti od toga u kom se od tri navedena statusa stablo nalazi, T će imati drugačiju složenost:

Glavna teorema

Dato   for a ≥ 1, b > 1 and k ≥ 0:

  • Ako je  , onda   (razgranato pri vrhu)
  • Ako je  , onda   (uravnoteženo)
  • Ako je  , onda   (razgranato pri dnu)

Za gornji slučaj sortiranja spajanjem

 

imamo da je

 

iz čega sledi da je  , što čini da je stablo uravnoteženo. Prema glavnoj teoremi složenost sortiranja spajanjem je

 .