Upload
others
View
4
Download
0
Embed Size (px)
Citation preview
PROGRAMIRANJE 2
VEŽBE
REKURZIJA
Staša Vujičić Stanković
REKURZIJA
Rekurzija je programerska tehnika pri kojoj:
F-ja u svojoj definiciji direktno poziva samu sebe.
F-ja indirektno poziva samu sebe,
npr. fja f poziva fju g, koja poziva fju f.
Ovakav pristup programiranju se često koristi
prilikom rešavanja problema koji imaju po
prirodi rekurzivnu definiciju.
To su problemi kod koji se problem dimenzije n
jednostavno svodi na problem dimenzije n-1
ili još manje. 2
REKURZIJA
Kako se definiše faktorijel?
Kako bi izgledala iterativna, a kako rekurzivna
verzija funkcije int faktorijel( int n)?
n! = n * (n-1)!
0! = 1
3
#include <stdio.h>
int faktorijel(int n)
{
/* Izlaz iz rekurzije */
if(n == 0)
return 1;
/* Rekurzija */
return n * fact(n - 1);
}
4
STEK
Stek je struktura podataka u koju se novi podaci
uvek dodaju na vrh, a prilikom uklanjanja se
prvo skida onaj koji je poslednji dodat.
Zato se kaže da je stek LIFO struktura.
(Last In First Out).
5
REKURZIVNI POZIVI I STEK
Prilikom poziva funkcije,
na stek se prvo stavljaju argumenti funkcije,
onda adresa povratka,
nakon čega je prostor za lokalne promenljive
pozvane funkcije (što čini stek element).
Svi rekurzivni pozivi se čuvaju na steku.
Kada se pozove funkcija sa trivijalnim slučajem,
izvrši se, a onda se naredbom return stek element
za taj poziv skida sa steka i ide na adresu
povratka,
tj. na instrukciju iz ranijeg poziva iz kojeg je
pozvana.
6
REKURZIVNI POZIVI I STEK
povratna vrednost
vrednost parametara
povratna adresa (gde nastaviti?)
lokalne promenljive funkcije
7
VAŽNO!
Kada pišemo rekurzivnu funkciju moramo da
obezbedimo:
Izlazak iz rekurzije, obično trivijalnim slučajem.
Rekurzivni poziv kojim se rešava problem manje
dimenzije.
Rekurzija nam omogućava pisanje elegantnijih
rešenja.
Rekurzivne funkcije troše mnogo više memorije nego
iterativne koje rešavaju isti problem.
8
ZADATAK 1.
1. Napisati rekurzivnu funkciju koja štampa
brojeve između 0 i n.
2. Napisati rekurzivnu funkciju koja štampa
brojeve između n i 0.
9
REŠENJE
#include <stdio.h>
void izbroj(int n)
{
/* Izlaz iz rekurzije */
if ( n < 0 )
return;
/* rekurzivni poziv */
izbroj(n-1);
printf("%d ",n);
} 10
void odbroj(int n)
{ if ( n < 0 )
return;
printf("%d ",n);
odbroj(n-1);
}
int main()
{
int n;
printf("Unesite prirodan broj: ");
scanf("%d",&n);
izbroj(n);
printf("\n");
odbroj(n);
return 0;
}
11
ZADATAK 2.
Napisati rekurzivnu funkciju koja izračunava xk,
za dati realni broj x i prirodan broj k.
Kako izgleda iterativno rešenje?
Linearno rešenje se zasniva na činjenici:
xk = x * xk-1
Logaritamsko rešenje je zasnovano na činjenicama:
xk = x * (x2)k/2 , za neparno k
xk = (x2)k/2 , za parno k
Prirodno, logaritamskom rešenju će biti potrebno
manje rekurzivnih poziva da bi došlo do rešenja,
i stoga je efikasnije.
Broj uzastonih rekurzivnih poziva se naziva
dubina rekurzije. 12
REŠENJE
#include <stdio.h>
int stepen(int x, int k)
{
if(k==0)
return 1;
return x * stepen(x, k-1);
}
13
int stepen2(int x, int k)
{
if( k == 1)
return x;
/*Ako je stepen paran*/
if((k % 2) == 0)
return stepen2(x*x, k/2);
return x*stepen2(x*x, k/2);
} 14
/*Iterativna verzija funkcije*/
int stepen_iterativno(int x,int k)
{
int i;
int s=1;
for(i=0; i<k; i++)
s*=x;
return s;
}
15
REPNA REKURZIJA (TAIL RECURSION)
Repno-rekurzivna funkcija je ona funkcija čije se
telo završava rekurzivnim pozivom,
pri čemu taj rekurzivni poziv ne učestvuje u
nekom izrazu.
Ovakve funkcije se mogu lako zameniti
odgovarajućom iterativnom funkcijom.
16
ZADATAK 3.
Napisati repno-rekurzivnu funkciju koja
izračunava n!.
17
/* Pomocna funkcija koja izracunava n! * result.
Koristi repnu rekurziju */
int faktorijelRepna(int n, int result) {
if (n == 0) return result;
return faktorijelRepna(n - 1, n * result);
}
/* Funkcija izracunava n! */
int faktorijel(int n)
{
return faktorijelRepna(n, 1);
}
18
ZADATAK 4.
Paskalov trougao se dobija tako što mu je svako polje
(izuzev 0-te vrste i 0-kolone)
zbir jednog polja iznad levo i jednog polja iznad desno.
red 0 1
red 1 1 1
red 2 1 2 1
red 3 1 3 3 1
red 4 1 4 6 4 1
red 5 1 5 10 10 5 1
red 6 1 6 15 20 15 6 1
red 7 1 7 21 35 35 21 7 1
red 8 1 8 28 56 70 56 28 8 1 19
n.-ti red sadrži brojeve C(n, k)
za k = 0,...,n.
a) Napisati rekurzivnu funkciju koja izračunava dn
kao sumu elemenata n-te hipotenuze pravouglog
trougla.
b) Napisati rekurzivnu funkciju koja izračunava
vrednost polja (i, j) – binomni koeficijent.
20
int sumaElemenataHipotenuze(int n)
{
return n > 0 ? 2 * sumaElemenataHipotenuze( n-1) : 1;
}
int C( int n, int m ) {
if( (n == 0) || (m == n) ) return 1;
else
return C( n-1, m-1) + C(n-1, m );
} 21
ZADATAK 5.
Napisati rekurzivnu funkciju koja sabira niz celih
brojeva zadatih kao niz od n elemenata:
a[0], a[1], … , a[n-2], a[n-1].
1. način:
za n = 0, suma = 0
za n > 0, suma = suma_prvih_n-1_elemenata + a[n-1]
2. način:
za n = 0, suma = 0
za n > 0, suma = a[0] + suma_ostalih_n-1_elemenata 22
int sumaNiza(int *a, int n)
{
if(n<=0 )
return 0;
return a[n-1] + sumaNiza(a,n-1);
}
int sumaNiza2(int *a, int n)
{
if(n<=0)
return 0;
return a[0] + sumaNiza2(a+1,n-1);
}
23
ZADATAK 6.
Napisati rekurzivnu funkciju koja računa n-ti
element u Fibonačijevom nizu.
F(0) = F(1) = 1
F( n) = F(n-1) + F(n-2)
24
int fibonacci(int n)
{
/* Izlaz iz rekurzije */
if(n < 2)
return 1;
/* Rekurzivni pozivi */
return fibonacci(n - 1) + fibonacci(n - 2);
}
25
Popraviti funkciju tako da se problemi manje
dimenzije rešavaju samo jedan put.
Dinamičko programiranje nam omogućava da
samo jednom rešavamo potprobleme.
Već izračunate članove niza čuvamo u statičkom
nizu celih brojeva, jer taj niz onda neće biti
smešten na stek već u statičkoj memoriji odakle
će biti dostupan svim pozivima rekurzivne
funkcije.
26
int fibonacciNapredna(int n)
{ /* Niz koji cuva resenja podproblema.
Kompajler inicijalizuje statičke promenljive na podrazumevane
vrednosti. Stoga elemente celobrojnog niza inicijalizuje na 0 */
static int f[50];
/* Ako je podproblem već rešen, uzimamo gotovo rešenje! */
if(f[n] != 0) return f[n];
/* Izlaz iz rekurzije */
if(n < 2)
return f[n] = 1;
/* Rekurzivni pozivi */
return f[n] =
fibonacciNapredna(n - 1) + fibonacciNapredna(n - 2);
} 27
ZADATAK 7.
Napisati rekurzivnu funkciju koja računa sumu
elemenata na parnim pozicijama u nizu celih
brojeva.
28
/* NAPOMENA: Primer ilustruje uzajamnu (posrednu) rekurziju. */
/* Deklaracija funkcije suma_neparnih_pozicija mora da bude
navedena jer se ta funkcija koristi u telu funkcije
suma_parnih_pozicija, tj. koristi se pre svoje definicije
Funkcija je mogla biti deklarisana i u telu funkcije suma_parnih
pozicija */
int suma_neparnih_pozicija(int a[], int n);
/* Funkcija racuna sumu elemenata niza na pozicijama 0, 2, 4, ... */
int suma_parnih_pozicija(int a[], int n)
{
if(n == 0) return 0;
return a[0] + suma_neparnih_pozicija(a + 1, n - 1);
}
29
/* Funkcija racuna sumu elemenata niza na
pozicijama 1, 3, 5, ... */
int suma_neparnih_pozicija(int a[], int n)
{
if(n == 0)
return 0;
return suma_parnih_pozicija(a + 1, n - 1);
}
30
ZADATAK 8.
Napisati rekurzivnu funkciju koja prikazuje sve
permutacije skupa {1, 2, ... ,n}.
Za n=3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
31
/* Funkcija proverava da li se x vec
nalazi u permutaciji na prethodnih 1...n mesta*/
int koriscen(int a[], int n, int x){
int i;
for(i=1; i<=n; i++)
if(a[i] == x) return 1;
return 0;
}
32
/* F-ja koja ispisuje sve permutacije skupa {1,2,...,n}
a[] je niz u koji smešta permutacije
m - označava da se na m-tu poziciju u
permutaciji smešta jedan od preostalih
celih brojeva
n- je veličina skupa koji se permutuje
Funkciju pozivamo sa argumentom m=1 jer krećemo
da formiramo permutaciju od prve pozicije
*/
33
void permutacija(int a[], int m, int n){
int i;
/* ako je pozicija na koju treba smestiti broj
premašila veličinu skupa,
onda se svi brojevi već nalaze u
permutaciji i ispisujemo permutaciju. */
if(m>n){
ispisiNiz(a,n);
return; }
34
for(i=1;i<=n;i++){
/* ako se broj i nije do sada pojavio u permutaciji od
1 do m-1 pozicije, onda ga stavljamo na poziciju m i
pozivamo funkciju da napravi permutaciju za jedan
veće dužine, tj. m+1. Inače nastavljamo dalje,
tražeći broj koji se nije pojavio do sada u
permutaciji */
if(! koriscen(a,m-1,i)){
a[m]=i;
permutacija(a,m+1,n);
}
}
}
35