35
PROGRAMIRANJE 2 VEŽBE REKURZIJA Staša Vujičić Stanković

PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

PROGRAMIRANJE 2

VEŽBE

REKURZIJA

Staša Vujičić Stanković

Page 2: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 3: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 4: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

#include <stdio.h>

int faktorijel(int n)

{

/* Izlaz iz rekurzije */

if(n == 0)

return 1;

/* Rekurzija */

return n * fact(n - 1);

}

4

Page 5: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 6: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 7: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

REKURZIVNI POZIVI I STEK

povratna vrednost

vrednost parametara

povratna adresa (gde nastaviti?)

lokalne promenljive funkcije

7

Page 8: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 9: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 10: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 11: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 12: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 13: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

REŠENJE

#include <stdio.h>

int stepen(int x, int k)

{

if(k==0)

return 1;

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

}

13

Page 14: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 15: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/*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

Page 16: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 17: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

ZADATAK 3.

Napisati repno-rekurzivnu funkciju koja

izračunava n!.

17

Page 18: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/* 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

Page 19: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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.

Page 20: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 21: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 22: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 23: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 24: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 25: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

int fibonacci(int n)

{

/* Izlaz iz rekurzije */

if(n < 2)

return 1;

/* Rekurzivni pozivi */

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

}

25

Page 26: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 27: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 28: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

ZADATAK 7.

Napisati rekurzivnu funkciju koja računa sumu

elemenata na parnim pozicijama u nizu celih

brojeva.

28

Page 29: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/* 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

Page 30: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/* 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

Page 31: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 32: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/* 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

Page 33: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

/* 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

Page 34: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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

Page 35: PROGRAMIRANJE 2ns2.math.rs/~stasa/P2/Cas 06.pdf · Dinamičko programiranje nam omogućava da samo jednom rešavamo potprobleme. Već izračunate članove niza čuvamo u statičkom

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