Ricorsione Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 1/1 Ricorsione • Un metodo ricorsivo è un metodo che direttamente o indirettamente richiama se stesso. • Una definizione ricorsiva è autoreferenziale e definisce un metodo in termini di istanze più semplici dello stesso problema: 1. si definisce come risolvere problemi analoghi a quello di partenza, ma che hanno dimensione ridotta e possono essere risolti in maniera estremamente semplice (detti casi base); 2. quindi si definisce come ottenere la soluzione del problema di partenza combinando la soluzione di uno o più problemi analoghi. Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 2/1 Ricorsione - Induzione La ricorsione è basata sul principio di induzione matematica: • se un asserto P vale per n = n0 (caso base) • e si può provare che, assumendola valida per n, allora vale per n + 1 (passo induttivo) • allora P vale per ogni n ≥ n0 Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 3/1 Ricorsione - Induzione Per dimostrare che un asserto P (n) dove n ∈ N vale per ogni n ∈ N possiamo utilizzare il principio di induzione come segue: Poniamo U = n ∈ N vale P (n) • Si dimostra che vale P (0), con 0 ∈ U vale P (n) • Si assume come ipotesi che l’asserto P (n) valga per un generico n∈U • Si dimostra che P (n) vale anche per P (n + 1), ovvero n∈U →n+1∈U • Si conclude che l’insieme U dei numeri n per cui vale P (n) coincide con N Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 4/1 Ricorsione - Induzione Un tipico esempio di induzione è dato dalla formula di Gauss n n(n+1) i = i=1 2 Dimostriamo che vale l’asserto ∀n ∈ N : 0 + 1 + 2 + 3 + ... + n = Quindi P (n) ≡ 0 + 1 + 2 + 3 + ... + n = n(n+1) 2 n(n+1) 2 Caso base : dimostriamo che P (n) vale per n = 0 0= 0·1 2 Passo induttivo : dimostriamo che P (n) ⇒ P (n + 1) Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 5/1 Ricorsione - Fattoriale Un ulteriore esempio di ricorsione è dato dal fattoriale di un numero intero. Immaginiamo di dover calcolare il fattoriale di un numero n: n! = n ∗ (n − 1) ∗ ... ∗ 3 ∗ 2 ∗ 1 Si ricordi che 0! = 1 e che il fattoriale non è definito per i numeri negativi. Come possiamo definire un algoritmo che calcoli la funzione fattoriale? Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 6/1 Ricorsione - Fattoriale Osserviamo che: n! = n ∗ (n − 1) ∗ (n − 2) ∗ ... ∗ 2 ∗ 1 = n ∗ (n − 1)! La versione ricorsiva sarà: 0! = 1 (caso base, per n = 0) n! = n ∗ (n − 1)! (se n > 0) f (x) = ⎧ ⎨1 x=0 ⎩ x ∗ f (x − 1) x > 0 Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 7/1 Ricorsione - Fattoriale int factorial (int n) { int result; if (n<0) result = -1; // Fattoriale non calcolabile else if (n == 0) result = 1; // 0! = 1 (caso base) else result = n * factorial(n-1); // ricorsione return (result); } Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 8/1 Ricorsione - Fattoriale Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 9/1 Ricorsione - Fattoriale Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 10/1 Ricorsione Tail - Ricerca in vettore Esercizio: cercare un elemento in un vettore di interi in modo ricorsivo. Procedimento: • Verificare l’eguaglianza tra il valore da cercare e il primo elemento del vettore: se l’eguaglianza è verificata, terminare restituendo la posizione dell’elemento nel vettore • Se l’eguaglianza non è verificata, ripetere il passo precedente verificando l’eguaglianza tra il valore e l’elemento successivo • Se l’eguaglianza non è verificata per alcun elemento del vettore, terminare restituendo -1 Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 11/1 Ricorsione Tail - Ricerca in vettore #include <stdio.h> int ricerca (int array[], int x, int a, int lenght) { if (x == array[a]) return a; if (a < lenght) return ricerca (array, x, a+1, lenght); return (-1); } int main(void) { int DIM; printf ("Dimensione vettore? "); scanf("%d",&DIM); int x; int lista[DIM]; int i; int lenght=DIM; int pos; for (i=0; i<DIM; i++) { printf ("Valore di posto %d: ",i); scanf ("%d",&lista[i]);} printf ("\nValore da cercare: "); scanf ("%d",&x); pos = ricerca (lista, x, 0, lenght-1); if (pos == -1) printf ("\nValore non trovato"); else printf ("\nValore trovato in posto %d", pos); return (0); } Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 12/1 Ricorsione - Considerazioni Non tutti gli algoritmi possono essere espressi in forma ricorsiva: esistono alcuni requisiti fondamentali affinché un algoritmo sia esprimibile in forma ricorsiva. • Il primo è la possibilità di formulare l’algoritmo in funzione di se stesso • Il secondo è che non si verifichi mai un ciclo infinito: deve esistere una condizione di terminazione, ovvero almeno una istanza del processo che non richiede di essere scomposta in ulteriori istanze più semplici. Nell’algoritmo del fattoriale è rappresentata da 1! o da 0!. Inoltre non deve esistere un valore di ingresso che renda impossibile la terminazione dell’esecuzione. Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 13/1 Ricorsione - Considerazioni Un approccio iterativo richiede di considerare la soluzione del problema unitariamente, mediante una sequenza di passi elementari; Un approccio ricorsivo richiede di esprimere il problema in termini dello stesso problema, decomposto in casi più semplici. Deve essere identificato un caso base che non richiede tale decomposizione, per consentire la terminazione. Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 14/1 Ricorsione - Vantaggi e svantaggi Vantaggi ricorsione: • Possibilità di risolvere problemi anche complessi con poche righe di codice; • Algoritmi facilmente interpretabili. Svantaggi ricorsione: • Talvolta eleganza e semplicità di una formulazione ricorsiva possono impattare negativamente sulla efficienza (risorse utilizzate, memoria utilizzata) Informatica Generale - Ricorsione v1.0, aa 2005-2006 – p. 15/1