Deo zbornika Učimo algoritme
Fibonačijev niz
Fibonačijev niz je niz brojeva u kome zbir prethodna dva daju vrednost narednog. Prva dva člana su 0 i 1. Niz je primećen u mnogim prirodnim pojavama, a nazvan je po italijanskom matematičaru Fibonačiju.
Fibonačijev niz u prirodi
Zanimljivi primeri Fibonačijevog niza nalaze se svuda u prirodi, možemo ga uočiti posmatrajući ljude, biljke, životinje. Posebno pravilan primer Fibonačijevog niza nalazi se u spiralnom obliku nautilus školjke.
Rekurzivna definicija
U matematičkom smislu, rekurzija predstavlja definisanje problema uz pomoć samog tog problema. U matematici postoje mnogi primeri rekurzije, a najpoznatiji su Fibonačijevi brojevi koji se definišu na sledeć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. Konačno, u slučaju da je n
jednako 1 ili 0, funkcija vraća n
.
Dakle, Fibonačijev niz (0,1,1,2,3,5,8,13,…) može se definisati u vidu rekurzivne funkcije F
:
- F (0) = 0 i F (1) = 1 (bazni slučaj)
- F (n) = F (n − 1) + F (n − 2) (rekurzivni korak)
Implementacija
Funkcija za izračunavanje n-tog elementa Fibonačijevog niza može se rekurzivno implementirati na sledeći način:
function fib(n) {
if (n == 0 || n == 1) return n
return fib(n-1) + fib(n-2)
}
console.log(fib(15))
Funkcija za traženje n-og fibonačijevog broja može biti i iterativno implementirana, pomoću petlje:
function fib(n) {
const niz = [0, 1]
for(let i = 2; i <= n; i++) {
niz.push(niz[i-1] + niz[i-2])
}
return niz[n]
}
console.log(fib(15))
U funkcionalnim programskim jezicima eliminacija repne rekurzije je redovno zagarantovana standardom jezika, što omogućuje korišćenje rekurzije umesto petlji.
Opasnost rekurzije
Iako je rekurzivna definicija, bez sumnje, elegantnija, u jezicima koji nemaju eliminaciju repne rekurzije (tail-call optimizaciju) je nesrazmerno neefikasnija.
Sledi poređenje broja koraka za nalaženje 40-og fibonačijevog broja iterativno i rekurzivno u Javascriptu:
let brojac = 0
function nadjiFibonaci(n) {
const niz = [0, 1]
for(let i = 2; i <= n; i++) {
brojac++
niz.push(niz[i-1] + niz[i-2])
}
return niz[n]
}
function nadjiRekurzivno(n) {
brojac++
if (n < 3) return 1
return nadjiRekurzivno(n-1) + nadjiRekurzivno(n-2)
}
console.log(`Iterativno: broj ${nadjiFibonaci(40)} nadjen u ${brojac} operacija.`)
brojac = 0
console.log(`Rekurzivno: broj ${nadjiRekurzivno(40)} nadjen u ${brojac} operacija.`)
U vreme pisanja ovog teksta, odnos broja koraka između iterativne i rekurzivne funkcije bio je 39 prema 204668309 (204 miliona) koraka! Iako jezik u budućnosti može biti optimizovan za rekurzivne pozive, svakako treba biti veoma oprezan.
Literatura
- Predrag Janičić i Filip Marić, PROGRAMIRANJE 2, Osnove programiranja kroz programski jezik C, Beograd, 2017.