curs 10

6
1 10. Recursivitatea În programare, recursivitatea se referă la proprietatea unei funcţii de a se putea apela pe ea însăşi. Cu alte cuvinte, spunem că o funcţie este recursivă dacă ea se autoapelează. Ea se poate autoapel a fie direct, fie indirect prin apelul altor funcţii. La apelurile recursive ale unei funcţii aceasta este apelată înainte de a reveni din ea. La fiecare reapel al funcţiei, parametrii şi variabilele ei se alocă pe stivă într-o zonă independentă. De aceea, aceste date au valori distincte la fiecare reapelare. Nu acelaşi lucru se întâmplă cu variabilele statice. Ele ocupă tot timpul aceeaşi zonă de memorie şi deci ele păstrează valoarea la reapel. Orice apel al unei funcţii conduce la o revenire din funcţia respectivă la punctul următor celui din care s-a realizat apelul. Atunci când se revine dintr-o funcţie se procedează la curăţirea stivei, adică stiva se reface la starea ei dinaintea apelului. De aceea, orice apel al unui apel recursiv va conduce şi la curăţirea stivei, la o revenire din funcţie, şi deci parametrii şi variabilele locale vor reveni la valorile lor dinaintea reapelului respectiv. Numai variabilele statice rămân neafectate la o astfel de revenire. Procesele recursive sunt strâns legate de procesele iterative. Secvenţa iterativă presupune execuţia repetată a unei secvenţe de instrucţiuni, până la îndeplinirea unei condiţii (while, do-while, sau for din C). Recursivitatea se referă la executarea repetată a unei secvenţe de instrucţiuni, însă pe parcursul execuţiei (nu la sfârşit, ca în cazul iteraţiei), se va verifica o condiţie a cărei valoare de neadevăr implică reluarea execuţiei secvenţei de instrucţiuni de la început, fără ca execuţia curentă să se fi terminat. În momentul îndeplinirii condiţiei, se va reveni în ordine inversă din secvenţa de apeluri, reluându-se şi finalizându-se apelurile suspendate. Funcţiile recursive se utilizează la definirea proceselor recursive de calcul. Un proces de calcul se spune că este recursiv, dacă el are o parte care se defineşte prin el însuşi. Un exemplu simplu de astfel de funcţie este funcţia de calcul al factorialului, care se poate defini astfel: factorial(n)=1, dacă n=0, altfel factorial(n)=n* factorial(n-1) Alternativa: factorial(n)=n* factorial(n-1) defineşte funcţia factorial(n) prin ea însăşi. Un proces recursiv trebuie mereu să conţină o parte care nu se defineşte prin el însuşi. În exemplul de mai sus, alternativa: factorial(n)=1, dacă n=0 este partea care este definită direct. Definiţia de mai sus se transmite imediat în limbajul C: double factorial(int n)

Upload: sorin-cosmin

Post on 26-Sep-2015

220 views

Category:

Documents


6 download

DESCRIPTION

curs 10

TRANSCRIPT

  • 1

    10. Recursivitatea

    n programare, recursivitatea se refer la proprietatea unei funcii de a se putea apela pe ea nsi.

    Cu alte cuvinte, spunem c o funcie este recursiv dac ea se autoapeleaz. Ea se poate autoapela fie

    direct, fie indirect prin apelul altor funcii.

    La apelurile recursive ale unei funcii aceasta este apelat nainte de a reveni din ea. La fiecare

    reapel al funciei, parametrii i variabilele ei se aloc pe stiv ntr-o zon independent. De aceea, aceste

    date au valori distincte la fiecare reapelare. Nu acelai lucru se ntmpl cu variabilele statice. Ele ocup

    tot timpul aceeai zon de memorie i deci ele pstreaz valoarea la reapel.

    Orice apel al unei funcii conduce la o revenire din funcia respectiv la punctul urmtor celui din

    care s-a realizat apelul. Atunci cnd se revine dintr-o funcie se procedeaz la curirea stivei, adic stiva

    se reface la starea ei dinaintea apelului. De aceea, orice apel al unui apel recursiv va conduce i la

    curirea stivei, la o revenire din funcie, i deci parametrii i variabilele locale vor reveni la valorile lor

    dinaintea reapelului respectiv. Numai variabilele statice rmn neafectate la o astfel de revenire.

    Procesele recursive sunt strns legate de procesele iterative. Secvena iterativ presupune

    execuia repetat a unei secvene de instruciuni, pn la ndeplinirea unei condiii (while, do-while, sau

    for din C). Recursivitatea se refer la executarea repetat a unei secvene de instruciuni, ns pe parcursul

    execuiei (nu la sfrit, ca n cazul iteraiei), se va verifica o condiie a crei valoare de neadevr implic

    reluarea execuiei secvenei de instruciuni de la nceput, fr ca execuia curent s se fi terminat. n

    momentul ndeplinirii condiiei, se va reveni n ordine invers din secvena de apeluri, relundu-se i

    finalizndu-se apelurile suspendate.

    Funciile recursive se utilizeaz la definirea proceselor recursive de calcul. Un proces de calcul

    se spune c este recursiv, dac el are o parte care se definete prin el nsui. Un exemplu simplu de astfel

    de funcie este funcia de calcul al factorialului, care se poate defini astfel:

    factorial(n)=1, dac n=0, altfel

    factorial(n)=n* factorial(n-1)

    Alternativa:

    factorial(n)=n* factorial(n-1) definete funcia factorial(n) prin ea nsi.

    Un proces recursiv trebuie mereu s conin o parte care nu se definete prin el nsui. n exemplul

    de mai sus, alternativa:

    factorial(n)=1, dac n=0

    este partea care este definit direct.

    Definiia de mai sus se transmite imediat n limbajul C:

    double factorial(int n)

  • 2

    {

    if (n==0)

    return 1.0;

    else

    return n*factorial(n-1);

    }

    S urmrim execuia acestei funcii pentru n=3. La apelul din funcia principal se construiete o

    zon pe stiv n care se pstreaz adresa de revenire n funcia principal, dup apel, precum i valoarea

    lui n, aa cum este prezentat n figura de mai jos:

    adr1

    3

    rev

    n

    figura 1

    adresa de revenire n funcia care a fcut apelul.

    Deoarece n>0, se execut alternativa else. Aceasta conine expresia:

    n*factorial(n-1);

    care autoapeleaz (direct) funcia factorial. Reapelarea funciei se realizeaz cu valoarea n-1=3-1=2.

    prin aceasta se construiete o zon nou pe stiv, care conine revenirea la evaluarea expresiei de mai

    sus, precum i valoarea nou a parametrului n, adic valoarea 2. Se obine starea din figura de mai jos:

    adr1

    adr2

    3

    2

    rev

    n

    rev

    n

    adresa de revenire la evaluarea expresiei n*factorial(n-1);

    figura 2

    La o nou reapelare a funciei factorial, n=2>0, deci din nou se execut alternativa else. Evaluarea

    expresiei conduce la o reapelare cu valoarea n-1=2-1=1. Se obine urmtoarea configuraie a stivei:

  • 3

    adr1

    adr2

    3

    2

    rev

    n

    rev

    n

    rev

    n

    adr3

    1

    adresa de revenire la evaluarea expresiei n*factorial(n-1);

    figura 3

    Iari n=1>0 i deci se face o nou reapelare i se obine configuraia:

    adr1

    adr2

    3

    2

    rev

    n

    rev

    n

    rev

    n

    adr2

    1

    adr2

    0

    adresa de revenire la evaluarea expresiei n*factorial(n-1); rev

    n

    figura 4

    n acest moment n=0, deci se revine din funcie cu valoarea 1. Se cur stiva i se revine la

    configuraia din figura 3. Adresa de revenire permite continuarea evalurii expresiei n*factorial(n-1). n

    acest moment n are valoarea 1 i cum s-a revenit tot cu valoarea 1, se realizeaz produsul:

    1*1=1

    Dup evaluarea expresiei n*factorial(n-1) se realizeaz o nou revenire tot cu valoarea 1. Dup

    curirea stivei se ajunge la configuraia din figura 2. n acest moment n are valoarea 2. Evalund expresia

    n*factorial(n-1) se realizeaz produsul:

    2*1=2

  • 4

    Apoi se revine din nou la funcia cu valoarea 2. Dup curirea stivei se ajunge la configuraia

    din prima figur. n acest moment n are valoare 3. Se evalueaz expresia n*factorial(n-1) i se obine:

    3*2=6

    Se revine din funcie cu valoarea 6 (3!) i conform adresei de revenire, se revine n funcia

    principal din care s-a apelat funcia factorial.

    Programul care calculeaz factorialul unui ntreg preluat de la tastatur este prezentat n

    continuare.

    Ex. S se realizeze un program care calculeaz factorialul unui ntreg preluat de la tastatur.

    #include

    main()

    {

    int n;

    printf("Introduceti intregul pentru care se va calcula factorialul:");

    scanf ("%d",&n);

    printf("fact(%d)=%d",n,factorial(n));

    }

    factorial(int n)

    {

    if(n==0)

    return 1;

    else

    return n*factorial(n-1);

    }

    Referitor la funciile recursive, trebuie specificat faptul c recursivitatea constituie un nou tip de

    mecanism de control al programului. Din acest motiv, fiecare funcie recursiv va avea o instruciune if

    care controleaz dac funcia se va apela pe ea nsi din nou sau dac va reveni n programul apelant.

    Fr o astfel de instruciune, o funcie recursiv va rula necontrolat, folosind toat memoria alocat

    stivei, conducnd n final la o blocare a calculatorului.

    Recursivitatea poate fi util n simplificarea anumitor algoritmi. Spre exemplu, algoritmul de

    sortare Quicksort este foarte dificil de implementat dac nu se folosete recursivitatea.

  • 5

    Ex. S se realizeze un program care afieaz descresctor (sau cresctor) numerele de la n la 0 (sau

    0 la n) folosind o funcie recursiv, unde n este un ntreg introdus de utilizator.

    #include

    main()

    {

    int n;

    printf("Introduceti n>");

    scanf("%d",&n);

    afisare(n);

    }

    afisare(int n)

    {

    if (n>=0)

    {

    printf("%d ",n);

    afisare(n-1);

    }

    }

    Pentru afiarea numerelor n ordine cresctoare, n corpul instruciunii if se va inversa ordinea

    celor dou instruciuni.

    Ex: Calculul cmmdc (a,b), unde a i b sunt dou numere ntregi introduse de utilizator.

    a=28, b=12

    1. Metoda restului:

    a=b

    b=a%b

    dac b=0 cmmdc=a

    a = 28 12 4

    b = 12 4 0

    dac b=0 afieaz cmmdc=a altfel

    calculeaz cmmdc (b, a%b)

    2. Metoda scderilor succesive:

    dac a=b cmmdc = a dac a>b se calculeaz cmmdc (a-b,b) dac b>a se calculeaz cmmdc (a,b-a)

  • 6

    a = 28 16 (28 - 12) 4 (16 - 12) 4 4

    b = 12 12 12 8 (12 - 4) 4 (8 - 4)

    dac a=b afieaz cmmdc=a altfel

    dac a>b calculeaz cmmdc (a-b, b) altfel

    calculeaz cmmdc (a, b-a)