Informatica 4
La ricorsione
Definizione di ricorsione
• Ricorsione è la proprietà di quei programmi che,
all’interno delle istruzioni che li compongono,
richiamano se stessi
• Per essere richiamato, un programma deve avere
un nome
• Usiamo la seguente notazione per dichiarare che
un programma ha nome “fatt”, prende un certo
valore “n” di tipo intero in input, e restituisce un
risultato anch’esso di tipo intero:
nome e tipo
dei parametri
int fatt(int n)
tipo risultato
restituito
nome
programma
in input
Ricorsione: il caso base
• Caso base: ogni programma ricorsivo deve
avere un caso base, ossia un particolare valore
dell’input per cui la soluzione del problema è
immediata e non c’è bisogno che il
programma richiami se stesso
• Il caso base è necessario perché altrimenti un
programma ricorsivo richiamerebbe se stesso
in ogni caso, e non terminerebbe mai
Il caso base: esempio
• Ad esempio, nel calcolo del fattoriale di un
intero (n! = n x (n-1) x (n-2) x … x 3 x 2 x 1), il
caso base si ha quando n = 0, perché 0! = 1
per definizione
• Nel programma:
if (n==0)
return 1;
Ricorsione: ipotesi ricorsiva
• Prima di affrontare il caso generale, bisogna
supporre di avere risolto un caso leggermente
più semplice
• Questa supposizione costituisce l’ipotesi
ricorsiva
• Nel caso del fattoriale, nel calcolare n!,
supponiamo di conoscere già (n-1)!
• Da notare che il caso leggermente più
semplice è anche più vicino al caso base
Ricorsione: passo
• Il passo è l’istruzione con cui si calcola il risultato
generale a partire dal risultato parziale dato dall’ipotesi
ricorsiva
• Nel caso del fattoriale, n! = n x (n-1)! (da notare che,
omettendo il primo fattore, il resto della definizione di
n! coincide con (n-1)! )
• Nel programma, è in questo caso che c’è il richiamo al
programma stesso, con un parametro diverso, non più
il caso generale con n, ma il caso più semplice on (n-1):
return n*fatt(n-1)
Il significato della ricorsione
• Se siamo nel caso base, bene: risultato
immediato
• Se non siamo nel caso base, siamo in un caso
generale: impostiamo il calcolo, il cui risultato
dipende dal caso un po’ più semplice
• Il caso un po’ più semplice richiama lo stesso
programma che si porrà di nuovo il quesito:
siamo nel caso base?
• E così via fino a quando il caso base è
effettivamente raggiunto e tutti i risultati parziali
e, infine, anche quello generale, si possono
calcolare
Fattoriale ricorsivo
int fatt(int n){
if (n==0)
return 1;
return n*fatt(n-1);
}
/*un else dopo il primo return sarebbe
superfluo perché return comunque conclude
l’esecuzione*/
Esempio di esecuzione
• fatt(3)
– non siamo nel caso base, quindi fatt(3) = 3*fatt(2)
• fatt(2)
– non siamo nel caso base, quindi fatt(2) = 2*fatt(1)
• fatt(1)
– non siamo nel caso base, quindi fatt(1) = 1*fatt(0)
• fatt(0)
– siamo nel caso base: return 1
• una volta che è restituito il risultato 1, si riescono a
calcolare tutti i risultati parziali, fino ad ottenere
fatt(3) = 3*2*1*1 = 6
Parametri formali e attuali
• Quando si scrive il codice di fatt, abbiamo
chiamato “n” il parametro di input, ma esso non
ha un vero valore: lo usiamo solo per scrivere il
codice
• “n” nel codice si chiama “parametro formale”
• Quando usiamo di fatto il codice di fatt per
eseguire un calcolo, scriviamo fatt(3)
• “3” nella chiamata a fatt si chiama “parametro
attuale” (da una traduzione errata da actual che
in inglese vuol dire reale, effettivo)
Ricorsione vs iterazione
• Non è obbligatorio risolvere i problemi
scrivendo programmi ricorsivi
• E’ sempre possibile trovare una soluzione
iterativa, ossia che consista in una ripetizione
di un certo numero di istruzioni, finché la
soluzione non viene trovata
• Tipicamente l’iterazione è realizzata con un
ciclo for
Fattoriale iterativo
int fatt(int n){
int i;
int risultato = 1;
for(i=1;i<=n; i++)
risultato = risultato * i;
return risultato
}
/*alla fine del ciclo for risultato contiene
1*1*2*3*…*(n-1)*n, ossia n!*/
Scarica

Informatica 4