You are on page 1of 17

SVEUČILIŠTE/UNIVERZITET „VITEZ“ VITEZ

FAKULTET INFORMACIONIH TEHNOLOGIJA


STUDIJ PRVOG CIKLUSA I. GODINA STUDIJA
SMJER: INFORMACIJSKE TEHNOLOGIJE

REKURZIVNI POZIVI FUNKCIJA

SEMINARSKI RAD

Travnik, April 2022


SVEUČILIŠTE/UNIVERZITET „VITEZ“ VITEZ
FAKULTET INFORMACIONIH TEHNOLOGIJA
STUDIJ I. CIKLUSA I. GODINA STUDIJA
SMJER: INFORMACIJSKE TEHNOLOGIJE

REKURZIVNI POZIVI FUNKCIJA

SEMINARSKI RAD

STUDENT:
PREDMET: Strukture podataka i algoritmi
PROFESOR: Mahir Zajmović
ASISTENT: Potpis studenta:________________

Travnik, April 2022


SADRŽAJ

1 UVOD.....................................................................................................................................1
1.1 Problem, predmet i objekat istraživanja..........................................................................2
1.2 Svrha i ciljevi istraživanja................................................................................................2
1.3 Struktura rada..................................................................................................................2
1.4 Metode rada....................................................................................................................2
2 POJAM REKURZIJE..............................................................................................................3
3 VRSTE i primjeri rekurzivnih funkcija.....................................................................................6
3.1 Faktorijel..........................................................................................................................6
3.2 Sumiranje niza.................................................................................................................6
3.3 Stepenovanje..................................................................................................................7
3.4 Fibonačijev niz.................................................................................................................8
3.5 NZD (Euklidov algoritam)................................................................................................9
3.6 Hanojske kule (kule Hanoja)...........................................................................................9
3.7 Uzajamna rekurzija.......................................................................................................10
4 DOBRE I LOŠE STRANE REKURZIJE...............................................................................11
5 ZAKLJUČAK.........................................................................................................................13
6 LITERATURA.......................................................................................................................14
1 UVOD

Rekurzija je jedna od osnovnih algoritamskih tehnika u programiranju. Potprogram se


smatra rekurzivnim ukoliko neposredno ili putem drugog potprograma poziva sam sebe. Da bi
ovakva ideja imala smisla, rekurzivni potprogram ne smije pozivati sam sebe pri svakom
izvršavanju, jer se tada proces izvršavanja ne bi nikad zaustavio. Kada potprogram (funkcija)
zadanu operaciju izvršava bez pozivanja samog sebe, uvijek mora postojati uvijet prekida.
Mnoge definicije i logički odnosi se mogu iskazati rekurzivno.

Kod rekurzivnih algoritama problem možemo riješiti kroz jednu funkciju koja višestruko
poziva samu sebe dok ne postigne osnovno rješenje ili kroz više funkcija koje se višestruko
međusobno pozivaju po istom principu rješavanja rekurzije. Algoritam možemo rješavati
primjenom rekurzije samo u slučaju da algoritam ima završetak, u protivnom se algoritam očito
ne može riješiti ni rekurzivnim ni drugim postupkom. S obzirom da za implementaciju rekurzije
koristimo funkcije, za svaki poziv funkcije mora se koristiti sistemski stog (stackframe) za
čuvanje vrijednosti lokalnih varijabli funkcije i povratne adrese pozivnog programa. Kako je
veličina sistemskog stoga ograničena i definirana kompajlerom to može biti ograničavajući
faktor za primjenu rekurzije.
1.1 Problem, predmet i objekat istraživanja

Kao predmet istraživanja ovaj rad razmatra pojam rekurzije, vrste rekurzivnih funkcija, primjenu
rekurzije, te prednosti i nedostatke.

1.2 Svrha i ciljevi istraživanja

Ovaj rad ima za cilj razmotriti na koji način se rekurzija primjenjuje u programiranju te da se na
taj način uvidi značaj primjene korištenja rekurzije nekog osnovnog algoritma.

1.3 Struktura rada

Rad se sastoji od pet osnovnih cjelina: uvod, definiranje pojma rekurzije, vrste i primjeri
rekurzivnih funkcija, dobre i loše strane rekurzije, te zaključak.

U uvodu se upoznajemo sa pojmom rekurzije i njegovim osnovnim karakteristikama.

Centralni dio rada ima za cilj da pobliže definira pojam rekurzivne funkcije, ta da objasni koje su
to vrste rekurzivnog algoritma kao i njegovu primjenu u praksi.

Naposlijetku će se u zaključku propitivati koje su to prednosti a koji nedostaci rekurzivnih


funkcija.

1.4 Metode rada

Kao metoda istraživanja koristit će se opis metoda rekurzivne funkcije, različite vrste primjene
rekurzije koje se koriste u programiranju, te njene prednosti i nedostaci.

2
2 POJAM REKURZIJE

Pojam rekurzije potiče iz matematike i ima veliku primjenu u programiranju. U


matematičkom smislu rekurzija predstavlja definisanje problema uz pomoć samog tog problema.
U matematici postoji veliki broj primjera rekurzije, a najpoznatiji su Fibonačijevi brojevi koji se
definišu na sljedeći način:

F(n) = F(n-1)+F(n-2).

Ovaj izraz znači da se n-ti fibonačijev broj izračunava kao zbir n-1-og i n-2-og
fibonačijevog broja, koji se opet izračunavaju na isti način kao n-ti broj.

Drugi način definisanja rekurzije kaže da rekurzija predstavlja način definisanja problema
preko pojednostavljene verzije istog tog problema. Jedan primjer kojim se ovo može opisati je
rješavanje problema pronalaska puta do kuće (problem ćemo označiti izrazom “pronađi put do
kuće”). Rekurzijom bi se ovaj problem mogao opisati u tri koraka, na sledeći način:

1. ako si kod kuće, ostani u mjestu,

2. ako nisi, napravi jedan korak prema kući,

3. pronađi put do kuće.

Tačka pod brojem 3 u ovom opisu problema predstavlja poziv istog problema iz definicije,
ali posle učinjene jedne jednostavne akcije, a to je jedan korak prema kući, problem je
pojednostavljen.

Opisani postupak se može uopštiti, čime dobijamo generalni algoritam koji rješava probleme
rekurzijom, i on se sastoji od sledeća tri koraka:

1. trivijalni slučaj (kojim se prekida proces izračunavanja),

2. izvršavanje jedne akcije koja nas vodi ka trivijalnom slučaju,

3. rekurzivni poziv.

Ovako opisani postupak rješavanja problema ima algoritamski oblik i za većinu problema se
može implementirati.

3
U matematici i informatici, rekurzija je pristup u kojem se neki pojam, objekat ili funkcija
definiše na osnovu jednog ili više baznih slučajeva i na osnovu pravila koja složene slučajeve
svode na jednostavnije.

Rekurzija se implementira preko funkcija i predstavlja pojavu u kojoj funkcija poziva samu
sebe. Korištenjem rekurzije moguće je simulirati rad petlje, odnosno ponavljanja bloka naredbi.
Rekurzija se često koristi u rješavanju raznih matematičkih problema, kao što su izračunavanje
faktorijela nekog broja, fibonačijevih brojeva i sl, ali se koristi i u programerskim zadacima kao
što su sortiranje nizova, pretraživanje složenih struktura podataka i rješavanje složenih
programerskih problema (na primjer raspored kraljica na šahovskoj tabli).

void recursion() {
...
recursion(); /* funkcija poziva samu sebe */
...
}

int main() {
recursion(); /* poziv rekurzivne funkcije */

U implementaciji rekurzije moramo biti oprezni da ne izazovemo beskonačnu petlju. Na


primjer, ako bismo htjeli da implementiramo program koji računa zbir prvih n prirodnih brojeva,
ovaj problem bismo mogli da definišemo rekurzivno na sljedeći način, suma prvih n brojeva je
jednaka sumi prvih n-1 brojeva plus broj n. Ako ovu logiku implementiramo direktno dobijamo
program koji je dat na sljedećem listingu. Međutim, ovaj program će ući u beskonačnu petlju, jer
funkcija suma stalno poziva sama sebe.

int suma(int n);

int main()
{
int n = 6;
int rez = suma(6);
printf("Suma prvih %d brojeva je %d \n", n, rez);
return 0;
}

4
int suma(int n){
int rez;
rez = n + suma(n-1);
return rez;
}

Da bi se izbjegla beskonačna petlja u rekurzivnom pozivu, mora se uvesti trivijalni slučaj,


odnosno slučaj koji predstavlja izlaz iz rekurzije. U primjeru sabiranja prvih n prirodnih brojeva,
za slučaj n=1 zbir prvih n prirodnih brojeva ne zahtjeva sabiranje već odmah možemo vratiti
rezultat. Ovdje smo u funkciji suma dodali slučaj za n=1 koji ne ulazi u rekurziju već samo vraća
vrijednost funkcije i to nam je trivijalni slučaj. Na ovaj način petlja poziva funkcije suma se
zaustavlja u momentu kada n dobije vrijednost 1.

int suma(int n);

int main()
{
int n = 6;
int rez = suma(6);
printf("Suma prvih %d brojeva je %d \n", n, rez);
return 0;
}

int suma(int n){


int rez;
if(n==1)
return 1;
rez = n + suma(n-1);
return rez;
}

Pored klasične rekurzije o kojoj je do sada bilo riječi postoji i takozvana uzajamna ili
indirektna rekurzija koja se odnosi na situaciju kada dvije funkcije jedna drugu pozivaju
rekurzivno. Na ovaj način funkcija ne poziva samu sebe direktno, nego posredno preko druge
funkcije. Ova situacija se može opisati sledećim programskim kodom:

void funkcijaA(){
...
funkcijaB();
...
}

void funkcijaB(){
...
funkcijaA();
....

5
}

3 VRSTE I PRIMJERI REKURZIVNIH FUNKCIJA

U programiranju, rekurzija je tehnika u kojoj funkcija poziva samu sebe, direktno ili
indirektno. Rekurzivne funkcije su pogodne za širok spektar informatičkih problema, ali
pored svojih dobrih strana imaju i loše.

3.1 Faktorijel

Funkcija faktorijel (za prirodni broj 𝑛, vrijednost 𝑛! jednaka je proizvodu svih prirodnih
brojeva od 1 do 𝑛) može se definisati na (primitivno) rekurzivan način:

- 0! = 1 (bazni slučaj)
- za 𝑛 > 0 važi: 𝑛! = 𝑛 · (𝑛 − 1)! (rekurzivni korak)

unsigned faktorijel(unsigned n) {

if (n == 0)

return 1;

else

return n*faktorijel(n-1);

3.2 Sumiranje niza

Sumiranje niza brojeva može biti izraženo (primitivno) rekurzivno.

Rekurzivna funkcija koja sumira elemente niza može se definisati na sljedeći način:

6
float suma(float a[], unsigned n) {

if (n == 0)

return 0.0f;

else

return suma(a, n-1) + a[n-1];

3.3 Stepenovanje

Stepenovanje broja može biti izraženo (primitivno) rekurzivno.

Vrijednost 𝑥𝑘 za nenegativne cjelobrojne izložioce može se jednostavno izračunati


sljedećom funkcijom:

float stepen_sporo(float x, unsigned k) {

if (k == 0)

return 1.0f;

else

return x * stepen_sporo(x, k - 1);

Iterativna varijanta prethodne funkcije je:

float stepen(float x, unsigned k) {

unsigned i; float m = 1.0f;

for(i=0; i<k; i++)

m *= x;

7
return m;

Sljedeće rekurzivno rješenje je znatno brže (zahtjeva mnogo manje množenja):

float stepen_brzo(float x, unsigned k) {

if (k == 0)

return 1.0f;

else if (k % 2 == 0)

return stepen_brzo(x * x, k / 2);

else

return x * stepen_brzo(x, k - 1);

3.4 Fibonačijev niz

Fibonačijev niz (0,1,1,2,3,5,8,13,...) može se definisati u vidu (generalne) rekurzivne funkcije 𝐹:

Funkcija za izračunavanje 𝑛-tog elementa Fibonačijevog niza može se definisati na sljedeći


način:

unsigned fib(unsigned n) {

if (n == 0 || n == 1)

return n;

else

return fib(n-1) + fib(n-2);

8
3.5 NZD (Euklidov algoritam)

Euklidov algoritam za izračunavanje najvećeg zajedničkog djelioca dva broja se može izračunati
narednom rekurzivnom funkcijom, pretpostavlja se da je a ≥ b.

unsigned nzd(unsigned a, unsigned b) {

if (b == 0)

return a;

else

return nzd(b, a % b);

3.6 Hanojske kule (kule Hanoja)

Problem kula Hanoja glasi ovako: date su tri kule i na prvoj od njih 𝑛 diskova opadajuće
veličine; zadatak je prebaciti sve diskove sa prve na treću kulu (koristeći i drugu) ali tako da
nikada nijedan disk ne stoji iznad manjeg.

Iterativno rješenje ovog problema je veoma kompleksno, a rekurzivno prilično jednostavno:


ukoliko je 𝑛 = 0, nema diskova koji treba da se prebacuju; inače: prebaci (rekurzivno) 𝑛−1
diskova sa polaznog na pomoćnu kulu (korištenjem dolazne kule kao pomoćne), prebaci najveći
disk sa polazne na dolaznu kulu i, konačno, prebaci (rekurzivno) 𝑛 − 1 diskova sa pomoćne na
dolaznu kulu (korištenjem polazne kule kao pomoćne). U nastavku je implementacija ovog
rješenja:

void kule(unsigned n, char polazna, char dolazna, char pomocna) {

if (n > 0) {

kule(n-1, polazna, pomocna, dolazna);

printf("Prebaci disk sa kule %c na kulu %c\n",start,finish);

9
kule(n-1, pomocna, dolazna, polazna);

}
}

3.7 Uzajamna rekurzija

U dosadašnjim primjerima, rekurzivne funkcije su pozivale same sebe direktno. Postoji i


mogućnost da se funkcije međusobno pozivaju i tako stvaraju uzajamnu rekurziju. Naredni
primjer ilustruje dvije funkcije koje se koriste u ispitivanju da li je parametar 𝑛 (𝑛 je
prirodan broj) paran broj. Funkcija paran rješava problem za vrijednost 𝑛 kada je 𝑛 > 0
primjenom funkcije 𝑛𝑒𝑝𝑎𝑟𝑎𝑛 za vrijednost 𝑛−1, pri čemu se za svođenje problema troše tri
vremenske jedinice. Funkcija 𝐵 za vrijednost parametra 𝑛 kada je 𝑛 > 0 poziva funkciju
paran za vrijednost 𝑛−1, pri čemu se za svođenje problema troše tri vremenske jedinice.

Za 𝑛 = 0, i funkcija paran i funkcija neparan troše dvije vremenske jedinice. Zadatak je


izračunati vrijeme izvršavanja funkcije paran.

int paran(int n) {

if (n==0)

return 1;

else

return neparan(n-1);

int neparan(int n) {

if (n==0)

return 0;

else

return paran(n-1);

10
4 DOBRE I LOŠE STRANE REKURZIJE

Dobre strane rekurzije su (obično) čitljiv i kratak kod, jednostavan za razumijevanje,


analizu, dokazivanje korektnosti i održavanje. Ipak, rekurzivna rješenja imaju i mana.

Cijena poziva. Prilikom svakog rekurzivnog poziva kreira se novi stek okvir i kopiraju se
argumenti funkcije. Kada rekurzivnih poziva ima mnogo, ovo može biti veoma memorijski i
vremenski zahtjevno, te je poželjno rekurzivno rješenje zamijeniti iterativnim.

Suvišna izračunavanja. U nekim slučajevima prilikom razlaganja problema na manje


potprobleme dolazi do preklapanja potproblema i do višestrukih rekurzivnih poziva za iste
potprobleme. Razmotrimo, na primjer, izvršavanje navedene funkcije koja izračunava
elemente Fibonačijevog niza za 𝑛 = 5. U okviru tog izvršavanja, funkcija fib je 3 puta
pozvana za 𝑛 = 0, 5 puta za 𝑛 = 1, itd. Naravno, na primjer, poziv fib(1) je svaki put
izvršavan iznova i nije korištena vrijednost koju je vratio prethodni takav poziv. Zbog
ovoga, izvršava se mnogo suvišnih izračunavanja i količina takvih izračunavanja u ovom
primjeru raste. Kako bi se izbegla suvišna izračunavanja moguće je koristiti tehniku
memorizacije, koja podrazumijeva da se u posebnoj strukturi podataka čuvaju svi rezultati
već završenih rekurzivnih poziva. Pri svakom ulasku u funkciju konsultuje se ova struktura
i, ako je rezultat već poznat, vraća se prethodno izračunat rezultat.

unsigned memo[MAX_FIB];

unsigned fib(unsigned n) {

if (memo[n]) return memo[n];

if (n == 0 || n == 1)

return memo[n] = n;

else

return memo[n] = fib(n-1) + fib(n-2);

11
}

Drugi pristup rješavanja problema suvišnih izračunavanja naziva se dinamičko programiranje.


Ključna ideja je da se, umjesto da se analizirajući problem on svodi na niže ka jednostavnijim
potproblemima, od jednostavnih potproblema naviše konstruiše rješenje glavnog problema.
Rješenja dinamičkim programiranjem obično ne uključuju rekurziju.

Na primjer, gore navedena funkcija fib može se zamijeniti iterativnom funkcijom koja od
početnih elemenata niza postepeno sintetiše sve dalje i dalje elemente niza. Primijetimo da za
izračunavanje 𝑛-tog elementa niza nije neophodno pamtiti sve elemente niza do indeksa n već
samo dva prethodna:
int fib(int n) {

int i, fpp, fp;

if (n == 0 || n == 1)

return n;

fpp = 0;

fp = 1;

for(i = 2; i <= n; i++) {

int f = fpp + fp;

fpp = fp;

fp = f;

return fp;

12
5 ZAKLJUČAK

Rekurzija je funkcija koja poziva sama sebe. Rekurzivno programiranje je često korištena
tehnika kojom je moguće implementirati razne algoritme. Rekurzija uvijek mora imati neko
ograničenje koje će je zaustaviti, jer bi se u suprotnom funkcija pozivala beskonačan broj puta, i
program se nikada ne bi izvršio.

• Algoritam rješavamo pomoću funkcije koja poziva samu sebe,

• Rekurzivni algoritam mora imati završetak,

• Koristi se struktura podataka stog (stackframe) za pohranu rezultata i povratak iz


rekurzije.

Rekurzija program čini elegantnim. Međutim, ako su performanse vitalne, umjesto toga bolje je
koristiti petlje jer je rekurzija obično puno sporija. Rekurzija je važan pojam, pomaže u
rješavanju problema i često se koristi u strukturi podataka i algoritmima. Na primjer, uobičajeno
je koristiti rekurziju u problemima kao što je prelazak stabla, putopisi, razvrstavanje i
pretraživanje i može se učinkovito koristiti gdje god je potrebno.

13
6 LITERATURA
Janičić, P. & Marić, F., 2017. Programiranje 2. Beograd: Matematički fakultet univerziteta u
Beogradu.

Manger, R. & Marušić, M., 2003. STRUKTURE PODATAKA I ALGORITMI. Zagreb: Sveučilište u
Zagrebu.

Živković, D., 2010. Uvod u algoritme i strukture podataka. Beograd: Univerzitet Singidunum.

14

You might also like