Ricorsione Strumento potente per definizioni matematiche Possibilità di definire insieme infinito di oggetti con regola finita possibilità di descrivere un insieme infinito di computazioni con un programma finito Ricorsione in matematica Le formule matematiche sono spesso espresse in termini ricorsivi Esempio: definizione di fattoriale 1!=1 N!=N * (N-1)! Metodi ricorsivi Contengono riferimenti espliciti a sé stessi direttamente ricorsivi Un metodo ne invoca un altro e l’esecuzione di quest’ultimo porta ad un certo punto ad invocare nuovamente (direttamente o indirettamente) il metodo originale indirettamente ricorsivi Ricorsione infinita Requisito fondamentale: chiamata ricorsiva subordinata ad una condizione che ad un certo istante deve divenire non soddisfatta Qualsiasi definizione ricorsiva deve avere una parte non ricorsiva, detta base della ricorsione, che permette alla ricorsione stessa di terminare Nell’esempio precedente del fattoriale la base è 1! che è posto uguale ad 1 Variabili in metodi ricorsivi Ogni invocazione genera un nuovo insieme di variabili locali Ogni parametro riceve un valore iniziale in base alla nuova invocazione Ogni volta che il metodo termina si ritorna al metodo che lo ha chiamato ( che potrebbe essere lo stesso) Numeri di Fibonacci Schema più complicato di composizione ricorsiva che potrebbe (e dovrebbe) essere tradotto in forma iterativa Definizione: fib0 = 0 fib1 = 1 fibn+1 = fibn + fibn-1 Implementazione ricorsiva int computeFib(int n) { if (n == 0) return 0; if (n == 1) return 1; return computeFib(n-1)+computeFib(n2); } Numero di invocazioni 5 4 3 2 3 2 1 1 2 1 1 0 0 Numero totale di invocazioni cresce esponenzialmente 1 0 Implementazione iterativa int computeFib(int n) { int i = 1, x = 1, y = 0; while (i < n) { i = i+1; x = x+ y; y = x -y; } return x; } Considerazioni Ricorsione deve essere evitata se esiste una soluzione iterativa ovvia Non vuol dire evitare la ricorsione a qualunque costo esistono molte buone applicazioni della ricorsione algoritmi per loro natura ricorsivi vanno implementati con metodi ricorsivi Le torri di Hanoi inventato nel 1880 da Lucas Tre aste (o torri) ed n dischi di dimensioni diverse (con buco per inserirli nelle aste) All’inizio tutti i dischi sono nell’asta 1 in ordine decrescente di grandezza Obiettivo: portarli nella torre 3 rispettando le regole seguenti nessun disco mai sopra uno più piccolo si può spostare un solo disco alla volta dischi sempre collocati su una torre (non a parte) solo disco in cima ad una torre può essere spostato Algoritmo ricorsivo Obiettivo: spostare k dischi da torre 1 a torre 3 Algoritmo: Spostare k-1 dischi da torre originale a torre temporanea Spostare 1 disco da torre originale a torre di destinazione Spostare k-1 dischi da torre temporanea a torre di destinazione Implementazione 1 void moveTowers(int k, int o,int d) { if (k > 0) { moveTowers(k-1, o, 6-o-d); System.out.println("Sposta da "+o+"a"+d); moveTowers(k-1,6-o-d,d); } }