Ricorsione

di il
4 risposte

Ricorsione

Ciao ragazzi, sto studiando le funzioni ricorsive, ma non capisco.
so che si deve risolvere il caso base e poi scrivere il passo ricorsivo. Perchè non si scrive una funzione che risolva l'algoritmo comprendendo anche il caso base? Immagino perchè 0 ed 1 sono casi particolari che non rientrerebbero nella formula, dando come risultato 1. Giusto?

prendiamo una funzione "long factorial(long number)"
il corpo è il seguente:

if (number<=0)
return 1; /*caso base*/
else { return= numero*factorial(n-1);}/*chiamata ricorsiva*/

se dò in input per esempio il numero 5, come agisce la funzione, passo passo? scusate sono un disastro.

4 Risposte

  • Re: Ricorsione

    Prendiamo per esempio il fattoriale di 5. Cos'è il fattoriale di 5? il prodotto di tutti i numeri da 5 a 2 estremi compresi. Quindi 5! = 5 * (5 -1) * (5 - 2)* (5 - 3). Ma (5 -1) * (5 - 2)* (5 - 3) è il fattoriale di 4! Duque se al posto di 5 mettiamo n che indica un numero qualsiasi appartenente ad N,scopriamo il fattoriale di n = n * (n - 1) ! = n * ( n - 1) * (n - 2) ! e così via. Dunque ci basterà decrementare n,applicare la funzione fattoriale su n - 1 poi su n -2 etc finchè n - k > 1.
    
    uint_fast64_t fat(uint_fast64_t n)
    {
        if(n > 1)
       {
           return n * fat(n - 1);
       }
      else
      {
          return 1;
      }
    }
    
  • Re: Ricorsione

    La ricorsione è una bestia nera nella didattica, e molti giungono alla laurea (e dopo) senza averla mai pienamente compresa. Dunque non vi è da preoccuparsi se all'inizio vi sono difficoltà a comprenderla: essa è controintuitiva e innaturale per la maggior parte dei programmatori, come ho potuto constatare nella mia lunga esperienza di formatore.

    In ogni caso, senza caso base non vi sarebbe neppure ricorsione, come in un'automobile senza motore.

    Fortunatamente per certuni, la ricorsione ha comunque scenari di utilizzo estremamente specialistici e limitati nel mondo reale: inoltre, in linguaggi imperativi che non hanno un supporto all'ottimizzazione della tail recursion e usano meccanismi di chiamata C o Pascal basati su stack frame, tra i quali C e Python, l'uso della ricorsione è formalmente sconsigliato, quando non tassativamente proibito (es. MISRA/C e altre normative per l'uso del linguaggio C in sistemi embedded critici).
    Dalla MISRA/C 2012:
    Rule 17.2 Functions shall not call themselves, either directly or indirectly.
    Dunque per la maggioranza dei programmatori mainstream si tratta solo di una tassa scolastica, o poco più.
    Ovviamente esistono delle eccezioni alla regola generale, restando al mainstream: e altrettanto ovvialmente la loro natura è tale (alberi e foreste, file systems, funzioni non primitive ricorsive e ben poco altro) da rendere per definizione inutile discuterne con chi sta a malapena iniziando a familiarizzare con le tecniche ricorsive.

    Se è vero che la maggior parte degli algoritmi sono descritti in letteratura scientifica in forma ricorsiva, sostanzialmente a cagione della compattezza e dell'economia simbolica che usualmente ne derivano, è altrettanto vero che il suo impiego nella prassi implementativa è decisamente molto più limitato, a causa dei fattori prestazionali sopra richiamati.


    loopunrolling ha scritto:


    Prendiamo per esempio il fattoriale di 5. Cos'è il fattoriale di 5? il prodotto di tutti i numeri da 5 a 2 estremi compresi.
    A rigore, vale la pena di sottolineare che nella definizione anche l'elemento neutro partecipa al prodotto, considerato una e una sola volta (come d'altronde ogni altro suo successore presente, in maniera perfettamente omogenea e coerente). Questo consente, tra l'altro, di evitare una ingiustificata forzatura "per definizione" nel definire il fattoriale dell'unità, lasciando scoperta solo la reale singolarità dello zero fattoriale, che per nostra comodità e per coerenza algebrica si desume per derivazione inversa (ad consequentiam) dover essere pari a uno. In casi come 1! infatti l'appello alla "definizione" non ha alcun reale significato logico e assiomatico, ed è invece la medesima trappola nella quale cadono anche quanti invocano la "definizione" nel caso di zero alla zero, che invece è un normalissimo caso omogeneo se si definiscono i naturali come arietà di insiemi e l'elevamento a esponente naturale a^b come cardinalità del numero di applicazioni tra due insiemi - finiti - di arietà data rispettivamente a e b. Nel caso di due insiemi vuoti, tale cardinalità è ovviamente unitaria, poiché esiste un solo modo per scrivere 0 = 0, ed ecco così elegantemente eliminata una inutile e fastidiosa "per definizione..." che in realtà, se non viene spiegata, appare puramente arbitraria e incomprensibile.

    Purtroppo in Italia il generale e fastidioso sbilanciamento nella didattica verso una peculiare mentalità analitica e ancillare alla fisica comporta anche certi timori e sostanziali errori nella logica di definizioni che appartengono a pieno diritto solo al regno della matematica discreta.
    In casi come il presente, includere nella definizione l'unità non comporta alcuna "imprecisione" né tampoco evoca paventati scenari di arbitraria proliferazione infinita di elementi neutri nel prodotto, che invece sono sempre possibili in altri scenari e con tipi di definizione analitica: ciascun naturale predecessore del numero dato n, con la sola ovvia eccezione dello zero, viene nella definizione considerato una e una sola volta nella produttoria, che ciò venga verbalmente esplicitato o meno.

    E' invece chiaro che, operativamente e computazionalmente, è del tutto irrilevante effettuare la moltiplicazione per l'unità. Tuttavia, è sempre opportuno chiarire bene le definizioni, visto anche l'andazzo generale della didattica.
  • Re: Ricorsione

    Allora, spero di spiegarmi bene.
    tu all'inizio invochi fattoriale (5).
    fattoriale (5) = 5 * fattoriale (4).
    fattoriale (4) = 4 * fattoriale (3).
    fattoriale (3) = 3 * fattoriale (2).
    fattoriale (2) = 2 * fattoriale (1).
    ecco, fattoriale (1) smette di evocare altre funzioni e restituisce un valore ""diretto"" (chiedo scusa per l'espressione ma non mi veniva nulla di meglio), cioè senza stare a chiamare altre funzioni.
    fattoriale (1) infatti è uguale a 1.
    2 * 1 = 2, quindi fattoriale (2) = 2, e poi si risolve in cascata (dal basso verso l'alto):
    3 * 2 = 6 -> fattoriale (3)
    4 * 6 = 24 -> fattoriale (4)
    5 * 24 = 120 -> fattoriale (5).
    e, infine, 120 è il risultato che otterrai a video.

    la ricorsione penso sia una cosa piuttosto difficile da immaginarsi (come hai potuto vedere, ogni volta devi farti tutto lo schemino fino ad arrivare al caso base, per poi risalire risolvendo tutte le espressioni), e, almeno in C, la chiamata di tutte queste funzioni è spesso molto più costosa di una versione iterativa.
    nel 90% dei casi, la versione iterativa è molto più semplice da spiegare (tant'è che quando spiegano il fattoriale in c, ti fanno come primo esempio quello iterativo) e da comprendere.
    ritengo comunque che la ricorsione, per quanto complicata e costosa in termini di prestazioni essa sia, per alcuni algoritmi (vedi quelli di ordinamento, come il quicksort, o la ricerca binaria) sia molto efficace per ridurre notevolmente il codice scritto.
  • Re: Ricorsione

    Ciao Serenazoso i due amici ti hanno detto in maniera teorica e pratica quello che succede! C'è poco da aggiungere se non che un immagine per cercare di capire. Innanzitutto come diceva M.A.W. 1968, nel linguaggio devono essere previsti dei meccanismi che gestiscano la ricorsione. Immagina che come nell'esempio del numero 5, prendi una scatola contraddistinto dal numero 5 e la metti in uno scaffale, quello più basso di una scaffalatura ; poi prendi la scatola numero 4 e la metti nello scaffale sopra a quella precedente e cosi di seguito poni le scatole nei ripiani superiori fino a quella contraddistinta con il num 1 che è il passo base. A questo punto prendi il contenuto di questa scatola e lo metti in quella dello scaffale sotto (numero 2), i meccanismi di cui prima provvederanno a rimuovere la scatola num 1 e a buttarla che tradotto vorrá dire chiudere la sessione della tua funzione e liberare memoria! E così a scendere fino alla num 5 che conterrà tutti i contenuti di tutti le scatole! Speriamo di nn averti complicato la vita! Da notare che per grosse moli di dati, la scaffalatura deve essere...enorme cioè un copioso consumo di memoria! In un progetto d'esame all'uni dovevo ordinare alfabeticamente un parte del testo di un libro. A parte la quantità di testo che ho dovuto scegliere per nn andare in overflow, il tempo necessario per ordinarlo è stato piuttosto lungo!


    Inviato dal mio iPhone utilizzando Tapatalk
Devi accedere o registrarti per scrivere nel forum
4 risposte