Fondamenti di Informatica II
Ingegneria Informatica
Prof. M.T. PAZIENZA
a.a. 2003-2004 – 3° ciclo
Ricorsione
Per ricorsione si intende la tecnica di risoluzione di un
problema in termini della soluzione ad una versione
più piccola (situazione fondamentale) dello stesso
problema.
Caratteristiche della ricorsione:
• ciascun passo ricorsivo semplifica la risoluzione
generale del problema
• l’ultimo passo (passo base) del problema viene
risolto direttamente
• Identificazione di una condizione di uscita dalla
ricorsione (coincide con il caso base)
Ricorsione
Punti su cui riflettere:
• Concetto di ricorsione
• Relazioni tra ricorsione ed iterazione
• Quali problemi devono essere risolti con metodi
ricorsivi e quali con metodi iterativi?
• Ricorsione come approccio mentale
• Scrivere funzioni ricorsive aiuta la comprensione delle
stesse
• Capire quando usare la ricorsione determina
l’efficienza di un algoritmo
Risorsione
Approccio:
• Identifica qual è l’aspetto fondamentale del problema
• Risolvilo
• Struttura il problema più complesso in termini di quello
fondamentale
• Problema generale come una cascata di problemi minori
ciascuno risolto dalla stessa funzione che affronta
sottoproblemi sempre più piccoli finché raggiunge quello
fondamentale e lo elabora senza problemi.
• L’importante è identificare la struttura base dell’algoritmo
Ricorsione (esempio1)
Permutazione
Si consideri una funzione che produca tutte le permutazione
di una stringa.
Esempio di permutazioni di una stringa di lettere:
"eat" "eta" "aet" "ate" "tea" "tae"
Se una stringa ha n lettere, allora il numero delle
permutazioni è dato dalla funzione fattoriale:
n! = 1 x 2 x 3 x . . . x n
Ovvero
n! = (n - 1)! x n
Ricorsione (esempio1)
Per calcolare il valore di n! si può usare un ciclo, oppure
ricorrere alla soluzione ricorsiva
n! = (n - 1)! x n
con l’assunzione fondamentale (passo base) che:
1! = 1
0! = 1
Quindi si può implementare una funzione fattoriale del tipo:
int factorial(int n)
{
if (n == 0) return 1;
int smaller_factorial = factorial(n-1);
int result = smaller_factorial * n;
return result;
}
Ricorsione (esempio1)
Una funzione che generi tutte le permutazioni di
una parola è data da
vector<string>
generate_permutations(string word);
Tutte le permutazioni della stringa "eat“ sono date
da:
vector<string> v= generate_permutations("eat");
for(int i = 0; i < v.size(); i++)
cout << v[i] << "\n";
Ricorsione (esempio1)
Per generare tutte le permutazioni ricorsivamente, si
generano tutte le permutazioni che cominciano
con la lettera 'e', poi quelle che cominciano con la
lettera a, quindi quelle che cominciano con la
lettera t.
Oppure applichiamo la permutazione (con un
approccio ricorsivo) su stringhe da due ottenendo
"at" "ta“, "et" "te“, "ae" "ea"
Aggiungiamo le prime lettere (lasciate inizialmente
da parte) per trovare tutte le permutazioni
"eat" "eta“, "aet" "ate“, "tae" "tea"
Ricorsione (esempio2)
Una parola si dice palindroma se è costituita da una stringa
che è uguale e se stessa quando la si consideri dal verso
opposto (es. anna, alla, ici, …)
Step 1:
Semplificare l’input in modo che lo stesso metodo risolutivo
possa essere applicato alla componente più semplice del
problema; esempi:
Eliminare il primo carattere
Eliminare l’ultimo carattere
Eliminare insieme il primo e l’ultimo carattere
Eliminare un carattere dal centro della parola
Tagliare la stringa in due metà.
Ricorsione (esempio2)
Step 2:
Si combinano le soluzioni relative agli input più semplici con la
soluzione del problema originale
La complessità di ciascuna soluzione parziale non deve preoccupare,
viene lasciata al passo successivo (che sarà risolto da qualcun
altro)
• Si consideri l’ipotesi di tagliare a metà la parola: nel caso di
“rotor” non si ottengono risultati incoraggianti
• Si consideri l’ipotesi (più interessante) di eliminare il primo e
l’ultimo carattere : con la parola “rotor” si ottiene "oto“. Verificata
che è palindroma, sarebbe verificata che anche la stringa iniziale è
palindroma, quindi (definiz. ricorsiva)
Una parola è palindroma se
il primo e l’ultimo carattere coincidono, (passo base)
la parola ottenuta eliminando il primo e l’ultimo carattere è
palindroma
Ricorsione (esempio2)
Step 3:
Trovare la soluzione per il caso base (più semplice). La
ricorsione si ferma con il caso base
Nel caso delle parole palindrome il caso base coincide con:
• stringhe di due caratteri
• stringhe con un solo carattere
• stringa vuota (senza caratteri)
La stringa di due caratteri non richiede una elaborazione
speciale: basta toglierle il primo e l’ultimo carattere e
diventa una stringa vuota.
1. Una stringa di un solo carattere è palindroma per
definizione
2. Una stringa vuota è palindroma per definizione
Ricorsione (esempio2)
Step 4:
La soluzione finale si ottiene combinando la
soluzione del caso semplice con quella del caso
base
Talvolta risulta più facile trovare soluzioni ricorsive
se si cambia leggermente il problema originario
Talvolta capita che funzioni cooperanti si chiamino
vicendevolmente in un approccio ricorsivo
(ricorsione reciproca) (esempio: calcolo delle
espressioni aritmetiche)
Ricorsione (esempio3)
Ricorsione reciproca
Per una espressione aritmetica si può affermare che:
• Un’espressione è un termine, o una somma di
termini, o una differenza di termini
• Un termine è o un fattore, o un prodotto di fattori,
o un quoziente di fattori
• Un fattore è o un numero (caso base) o una
espressione racchiusa tra parentesi
Efficienza della ricorsione
Sebbene la ricorsione costituisca un approccio molto
potente per la risoluzione di algoritmi complessi,
l’efficienza delle implementazioni può non essere
ottimale richiedendo un tempo incredibile per arrivare
alla soluzione finale
Molto spesso la soluzione iterativa e quella ricorsiva hanno
la stessa prestazione; solo pochi problemi hanno una
soluzione ricorsiva più veloce di quella iterativa. Le
soluzioni ricorsive risultano più facili da capire ed
implementare correttamente
Le soluzioni ricorsive sono molto più efficienti
nell’interfaccia utente e richiedono uno sforzo di analisi
più che di programmazione