Sei sulla pagina 1di 40

Metodologie di Programmazione: Ricorsione in Java

Roberto Navigli
Metodologie di Programmazione 28/04/2021 2
Roberto Navigli
La ricorsione

Frattali di Mandelbrot
Immagini ricorsive

Sogno ricorsivo!
http://www.youtube.com/watch?
v=M59vgZwGPe4
http://www.youtube.com/watch?
v=j0Gx0eCJoWU

Cibo frattale…
Mutua ricorsione
Spingitori di (spingitori di) cavalieri!
Metodologie di Programmazione 28/04/2021 3
Roberto Navigli
Metodologie di Programmazione 28/04/2021 4
Roberto Navigli
28/04/2021 5
Ricorsione vs. Iterazione

Metodologie di Programmazione 28/04/2021 6


Roberto Navigli
Pensare ricorsivamente

• Dato un problema, bisogna identificare:


– I casi base: problemi di dimensione minima e immediatamente
calcolabile
– Il passo ricorsivo: passo in cui si riduce l’attuale istanza del
problema in uno o più sottoproblemi di dimensione minore
combinati mediante una o più operazioni elementari

Metodologie di Programmazione 28/04/2021 7


Roberto Navigli
Esempio: Fattoriale di un numero intero

• Come si calcola il fattoriale n! di un intero n?


• Definizione iterativa:
– n! = n*(n-1)*(n-2)*…*2*1 n>0
– 0! = 1 n=0

• Definizione ricorsiva:
– n! = n*(n-1)! n>0
– 0! = 1 n=0

Metodologie di Programmazione 28/04/2021 8


Roberto Navigli
L’idea della ricorsione (1)

• Sequenza di chiamate ricorsive:

5!

5 * 4!

4 * 3!

3 * 2!

2 * 1!

1 * 0!

Metodologie di Programmazione 28/04/2021 9


Roberto Navigli
L’idea della ricorsione (2)

• Valori restituiti da ciascuna chiamata:

5!

5120
* 4!

4 24
* 3!

3 *62!

2 *21!

1 *1 0!

Metodologie di Programmazione 28/04/2021 10


Roberto Navigli
Come funziona la ricorsione? (1)

• Ogni volta che viene effettuata una chiamata a un


metodo viene creato un nuovo ambiente, detto record di
attivazione
• Un record di attivazione contiene la zona di memoria
per le variabili locali del metodo e l’indirizzo di ritorno al
metodo chiamante
– A ogni chiamata, il record corrispondente viene aggiunto sullo
stack
• Lo stack è la pila dei record di attivazione delle
chiamate annidate effettuate

Metodologie di Programmazione 28/04/2021 11


Roberto Navigli
Come funziona la ricorsione? (2)

• Che succede quando chiamiamo fattorialeRicorsivo(5)?


fatt(0)
n=0
int k = f.fatt(5) chiamante = Fattoriale.fatt
fatt(1)
n=1
5 * fatt(4)
chiamante = Fattoriale.fatt
fatt(2)
4 * fatt(3)
n=2
chiamante = Fattoriale.fatt
3 * fatt(2) fatt(3)
n=3
2 * fatt(1) chiamante = Fattoriale.fatt
fatt(4)
1 * fatt(0) n=4
chiamante = Fattoriale.fatt

1 fatt(5)
n=5
chiamante = Fattoriale.main
main(new String[] {})
args = new String[] {}
Fattoriale f = new Fattoriale()
chiamante = JVM

Metodologie di Programmazione 28/04/2021 12


Roberto Navigli
Come funziona la ricorsione? (3)

• Che succede quando chiamiamo fattorialeRicorsivo(5)?


fatt(0)
n=0
return 5*24
int k = f.fatt(5) chiamante = Fattoriale.fatt
fatt(1)
return 4*6 n=1
5 * fatt(4)
chiamante = Fattoriale.fatt

return 3*2 fatt(2)


4 * fatt(3)
n=2
chiamante = Fattoriale.fatt
3 * fatt(2) return 2*1
fatt(3)
n=3
2 * fatt(1) return 1*1 chiamante = Fattoriale.fatt
fatt(4)
1 * fatt(0) return 1 n=4
chiamante = Fattoriale.fatt

1 fatt(5)
n=5
chiamante = Fattoriale.main
main(new String[] {})
args = new String[] {}
Fattoriale f = new Fattoriale()
chiamante = JVM

Metodologie di Programmazione 28/04/2021 13


Roberto Navigli
Non prevedere un caso base

• Che succede se chiamo fattorialeRicorsivo(-1)?


• Si ha una ricorsione infinita!

• Con quale conseguenza?

Metodologie di Programmazione 28/04/2021 14


Roberto Navigli
Non prevedere un caso base

• Che succede se chiamo fattorialeRicorsivo(-1)?

StackOverflowError,
un java.lang.Error!

Metodologie di Programmazione 28/04/2021 15


Roberto Navigli
Fattoriale ricorsivo *FATTO BENE*

Un’eccezione con
un costruttore!

Metodologie di Programmazione 28/04/2021 16


Roberto Navigli
Esercizio: Riconoscere una stringa palindroma
ricorsivamente
• Scrivere un metodo ricorsivo che determini se una
stringa in ingresso è palindroma (ad esempio,
itopinonavevanonipoti o ailatiditalia)

Metodologie di Programmazione 28/04/2021 17


Roberto Navigli
Esercizio: Concatenazione ricorsiva di stringhe

• Si realizzi un metodo ricorsivo che, dato in input un array


di stringhe, ne restituisca la loro concatenazione
• Ad esempio, concatena(new String[] { “abc”, “de”, “f” })
restituisce “abcdef”

Metodologie di Programmazione 28/04/2021 18


Roberto Navigli
Esercizio: Ricerca binaria ricorsiva

• Scrivere un metodo ricorsivo per la ricerca binaria di un


elemento all’interno di un array ordinato di interi
– Restituendo -1 se l’elemento non è presente

Metodologie di Programmazione 28/04/2021 19


Roberto Navigli
Strutture dati ricorsive (1)

• Ad esempio, una lista “linkata” (ricordate?):

Metodologie di Programmazione 28/04/2021 20


Roberto Navigli
Strutture dati ricorsive (2)

• Ad esempio, un labirinto:

Metodologie di Programmazione 28/04/2021 21


Roberto Navigli
Esercizio: Visualizzare il contenuto di una cartella

• Si realizzi una classe Cartella, costruita con il percorso


di una cartella reale, dotata di un metodo toString() che
ne visualizzi ricorsivamente il contenuto
• Avete bisogno della classe java.io.File

Metodologie di Programmazione 28/04/2021 22


Roberto Navigli
Visualizzare il contenuto di una cartella: soluzione

Metodologie di Programmazione 28/04/2021 23


Roberto Navigli
Esercizio: Cercare file in una cartella

• Si realizzi una classe Cartella, costruita con il percorso


di una cartella reale, dotata dei seguenti metodi:
– Cerca un file all’interno di una cartella
– Cerca all’interno della cartella tutti i file con l’estensione
specificata
– Cerca all’interno della cartella tutti i file con le estensioni fornite
in input
• Si faccia uso al meglio dell’overloading

Metodologie di Programmazione 28/04/2021 24


Roberto Navigli
Esercizio: Somma ricorsiva di numeri

• Supponiamo di non possedere l’operatore di somma ma


solo l’incremento (++) e il decremento (--)
• Come si può implementare la somma?

• Quanto è efficiente?
• E nella versione iterativa?

Metodologie di Programmazione 28/04/2021 25


Roberto Navigli
La successione di Fibonacci

• Come è definita la successione di Fibonacci?


– fib(0) = 0
– fib(1) = 1
– fib(x) = fib(x-1)+fib(x-2) se x > 1
• E’ una definizione ricorsiva!

• E’ una implementazione efficiente?

Metodologie di Programmazione 28/04/2021 26


Roberto Navigli
Schema delle invocazioni del metodo ricorsivo fib

fib(5)

fib(4) fib(3)

fib(3) fib(2) fib(2) fib(1)

fib(2) fib(1) fib(1) fib(0) fib(1) fib(0)

fib(1) fib(0)

Metodologie di Programmazione 28/04/2021 27


Roberto Navigli
Esercizio: Generazione di tutte le stringhe binarie

• Scrivere un metodo ricorsivo che stampi tutte le stringhe


binarie di lunghezza k
• Ad esempio, genera(3) stampa:
000
001
010
011
100
101
110
111
Metodologie di Programmazione 28/04/2021 28
Roberto Navigli
Esercizio: Generazione di tutte le stringhe con
determinati vincoli
• Scrivere una classe costruita con un insieme di caratteri e
dotata di un metodo genera che, dato in input un intero k,
restituisca l’elenco completo delle stringhe di lunghezza k
utilizzando caratteri nell'insieme in input
– ad esempio, per l'insieme {'a', 'b', 'c'}, genera(2) restituisce l’elenco
[“aa”, “ab”, “ac”, “ba”, “bb”, “bc”, “ca”, “cb”, “cc”]
• Come procedere? E’ necessario scomporre il problema in
sottoproblemi
• Iterare sui caratteri validi per ottenere il primo carattere della
stringa ed effettuare, per ciascuno di essi, una chiamata
ricorsiva genera(k-1), finché non si arriva alla stringa vuota
• Extra: creare una versione sovraccaricata di genera in cui si
passa un predicato che determina se la stringa deve essere
generata oppure no

Metodologie di Programmazione 28/04/2021 29


Roberto Navigli
Esercizio: Permutazioni di una stringa

• Scrivere una classe costruita con una stringa e dotata di


un metodo generaPermutazioni che restituisce l’elenco
completo delle permutazioni della stringa
– ad esempio, per la stringa “abc” genera l’elenco [“abc”, “acb”,
“bac”, “bca”, “cab”, “cba” ]
• Come procedere? E’ necessario scomporre il problema
in sottoproblemi
• Fissato un carattere della stringa iniziale, lo poniamo
all’inizio della stringa e permutiamo la sottostringa
rimanente
– stesso problema, ma su una sequenza di caratteri più piccola!

Metodologie di Programmazione 28/04/2021 30


Roberto Navigli
Permutazioni di una stringa: versione efficiente

• Provate a fare la versione che, invece di creare un


nuovo oggetto della stessa classe, faccia tutto all'interno
dello stesso oggetto con il metodo (i metodi)
getPermutazioni

Metodologie di Programmazione 28/04/2021 32


Roberto Navigli
Esercizio: Teseo, il Minotauro e il Labirinto di Creta

• Si realizzi una classe Labirinto dotata di un solo ingresso


a un corridoio
• Ciascun corridoio fornisce zero o più ingressi ad altri
corridoi
• Ogni corridoio è dotato di un metodo contieneMinotauro
che specifica se nel corridoio si trova il Minotauro
• La classe Labirinto è dotata di un metodo percorri() che,
partendo dal corridoio iniziale, cerca il Minotauro nel
labirinto
• Avanzato: Teseo, che fa il suo ingresso nel Labirinto,
una volta trovato il Minotauro, stampa il percorso a
ritroso mediante il “filo di Arianna”…

Metodologie di Programmazione 28/04/2021 33


Roberto Navigli
Esercizio: Teseo, il Minotauro e il Labirinto di Creta

• Si realizzi una classe Labirinto dotata di un solo ingresso


a un corridoio
• Ciascun corridoio fornisce zero o più ingressi ad altri
corridoi
• Ogni corridoio è dotato di un metodo contieneMinotauro
che specifica se nel corridoio si trova il Minotauro
• La classe Labirinto è dotata di un metodo percorri() che,
partendo dal corridoio iniziale, cerca il Minotauro nel
labirinto
• Avanzato: Teseo, che fa il suo ingresso nel Labirinto,
una volta trovato il Minotauro, stampa il percorso a
ritroso mediante il “filo di Arianna”…

Metodologie di Programmazione 28/04/2021 34


Roberto Navigli
Variante: Versione con Labirinto a matrice

• Si realizzi una classe LabirintoMatrice in cui il labirinto è


rappresentato mediante una matrice NxM (due valori in
input al costruttore)
• Ogni cella della matrice è un’istanza di una classe Cella
– Una cella o è una stanza o è un muro
– Una stanza può o meno contenere il minotauro
• La classe LabirintoMatrice è dotata di un metodo
percorri() che, partendo dalla cella (x,y) specificata in
ingresso al metodo, cerca il Minotauro nel labirinto
• Avanzato: stampare il percorso a ritroso

Metodologie di Programmazione 28/04/2021 35


Roberto Navigli
Mutua ricorsione

• A volte può verificarsi il caso in cui il metodo a richiami b


e b, a sua volta, richiami a
• Questo meccanismo di reciproca chiamata dei metodi è
detto di mutua ricorsione
• Ovviamente è sempre necessario stabilire uno o più
casi base, onde evitare un ciclo infinito di chiamate tra a
eb

Metodologie di Programmazione 28/04/2021 36


Roberto Navigli
Esempio: controlla numeri pari e dispari

• Scrivere una classe che, data una lista di interi, controlli


ricorsivamente se ciascun numero in posizione pari sia
dispari e ciascun numero in posizione dispari sia pari

Metodologie di Programmazione 28/04/2021 37


Roberto Navigli
Esercizio: Somma e sottrazione di valori

• Scrivere una classe SommaSottrai che, costruita con


una lista di interi, espone un metodo ricorsivo
sommaSottrai che restituisce la somma dei valori della
lista in posizione dispari da cui vengono sottratti i valori
in posizione pari
– Ad esempio, data la lista [2, 5, 3, 7, 11, 1] il metodo restituisce
2-5+3-7+11-1=3

Metodologie di Programmazione 28/04/2021 38


Roberto Navigli
Esercizio: And tra espressioni in una lista

• Si progetti un’interfaccia Valutabile che espone un


metodo valuta() il quale calcola il valore dell’oggetto e
restituisce un booleano
• Si progetti quindi una classe ValutaValutabili i cui
oggetti sono costruiti con un array di oggetti “valutabili”
• La classe espone un metodo valutaInAnd il quale
restituisce il valore booleano dell’and tra tutti i valori
degli oggetti nell’array
• Analogamente, realizzare un metodo valutaInOr che
mette in or i valori degli oggetti nell’array

Metodologie di Programmazione 28/04/2021 40


Roberto Navigli
La Torre di Hanoi

• In un tempio dell’estremo oriente alcuni monaci devono


spostare una pila di dischi d’oro da un piolo di diamante a
un altro
• La pila iniziale comprende 64 dischi, tutti infililati nello
stesso piolo dal basso verso l’alto in ordine di grandezza
decrescente
• I monaci devono spostare l’intera pila su un altro piolo,
rispettando il vincolo di muovere un solo disco per volta e
non ponendo mai un disco più grande sopra uno più
piccolo
• Sono disponibili 3 pioli e uno può essere usato come
supporto temporaneo
• Si realizzi la classe TorreDiHanoi che risolva il problema
Metodologie di Programmazione 28/04/2021 41
Roberto Navigli
La Torre di Hanoi: suggerimenti

• Tenete bene a mente che per risolvere un problema


ricorsivamente è necessario:
1) ridurlo a un problema più piccolo
2) identificare il caso base
• Posso riformulare il problema di spostare n dischi nel
problema in cui:
– Sposto n-1 dischi dal piolo 1 al piolo 2 (usando il piolo 3 come
supporto temporaneo)
– Sposto l’ultimo disco (il più grande) dal piolo 1 al piolo 3
– Sposto n-1 dischi dal piolo 2 al piolo 3, usando il piolo 1 come
supporto temporaneo

Metodologie di Programmazione 28/04/2021 42


Roberto Navigli