1. 2 Una ricorsione si definisce non lineare quando si ha più di una chiamata ricorsiva per blocco Per capire bene come opera una ricorsione non lineare.

Slides:



Advertisements
Presentazioni simili
INFORMATICA Altre Istruzioni di I/O
Advertisements

Funzioni In C++ le funzioni sono caratterizzate da un nome, dal tipo della variabile ritornata e da una lista di parametri (opzionali) La lista dei parametri.
Sottoprogrammi: funzioni e procedure
Ricorsione Procedure e funzioni ricorsive. Definizioni Un oggetto si dice ricorsivo se è definito totalmente o parzialmente in termini di sé stesso La.
Procedure e funzioni ricorsive
Procedure e funzioni In linguaggio C.
ITIS LATTANZIO Unità Didattica Materia Informatica Funzioni in C++
1 Informatica Generale Susanna Pelagatti Ricevimento: Mercoledì ore presso Dipartimento di Informatica, Via Buonarroti,
Introduzione al linguaggio C
Le funzioni.
Alberi binari di ricerca
La ricorsione Simulazione. Il Main /* Programma che usa una funzione ricorsiva*/ #include #define MAX_N 8 main() int valore, dato; printf(Introduci n:
Introduzione agli algoritmi. Definizione Sistema di regole e procedure di calcolo ben definite che portano alla soluzione di un problema con un numero.
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati.
Indirizzi delle variabili A ogni variabile sono associati tre concetti fondamentali: il valore memorizzato; il tipo dati di appartenenza; lindirizzo. Il.
Capitolo 1 Unintroduzione informale agli algoritmi Algoritmi e Strutture Dati.
Capitolo 1 Unintroduzione informale agli algoritmi Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
Capitolo 1 Unintroduzione informale agli algoritmi Algoritmi e Strutture Dati.
Funzioni definite dall’utente
PROGRAMMI DI RICERCA E ORDINAMENTO
Esercizi su alberi binari
Esercizi su alberi binari di ricerca
Algoritmi e strutture Dati - Lezione 7
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
CORSO DI PROGRAMMAZIONE II
CORSO DI PROGRAMMAZIONE II Introduzione alla ricorsione
CORSO DI PROGRAMMAZIONE II
Alberi di Ricorrenza Gli alberi di ricorrenza rappresentano un modo conveniente per visualizzare i passi di sostitu- zione necessari per risolvere una.
Array Funzioni che operano su array. Funzioni Ricordiamo che una funzione è una parte di codice a sé stante che esegue un compito e/o ritorna un risultato.
LdL - LP1 - ver. 6 - lez aa Linguaggi di programmazione I La ricorsione Prof. Luigi Di Lascio Lezione 10.
Esercizi su alberi binari
Procedure e funzioni nei linguaggi di alto livello Lab Programmazione - turno /2006.
INFORMATICA Altre Istruzioni di I/O. © Piero Demichelis 2 Funzioni di I/O Per la lettura e la scrittura da videoterminale il C prevede numerose istruzioni.
Esercizi FUNZIONI Passaggio di parametri per valore, variabili e tipi locali e globali, prototipo.
Politecnico di Milano Esercizi Preparazione alla prima prova intermedia.
Algebra Lineare Esercizi assegnati.
La Programmazione Ricorsiva
Le funzioni.
FUNZIONI: IL MODELLO APPLICATIVO 1) Valutazione, nellenvironment corrente, del simbolo che denota il nome della funzione; 2) Valutazione, nellenvironment.
DEFINIZIONE DI NUOVE FUNZIONI & STRATEGIE DI COMPOSIZIONE La capacità di definire nuove funzioni permette: di definire nuove operazioni di introdurre variabili.
PUNTATORI Un puntatore è una variabile destinata a contenere lindirizzo di unaltra variabile Vincolo di tipo: un puntatore a T può contenere solo lindirizzo.
ITIS LATTANZIO Unità Didattica Materia Informatica Funzioni in C++
1 ListaDiElem Cancella( ListaDiElem lista, TipoElemento elem ) { ListaDiElem puntTemp; if( ! ListaVuota(lista) ) if( lista–>info == elem ) { puntTemp =
Il linguaggio C Le funzioni C Language Il passaggio dei parametri
BUONA VISIONE.
void binario(int n); …………………
Lo sviluppo top down Le funzioni
Esercizi Liste.
Esercizi su alberi binari di ricerca
Sistemi e Tecnologie Informatiche Ricorsione Umberto Ferraro Petrillo.
Vettori, indirizzi e puntatori Finora abbiamo usato gli indirizzi nel chiamare  la funzione scanf()  le altre funzioni per riferimento Tuttavia la vera.
Alberi Alberi radicati : alberi liberi in cui un vertice è stato scelto come radice. Alberi liberi : grafi non orientati connessi e senza cicli. Alberi.
Didattica e Fondamenti degli Algoritmi e della Calcolabilità Quarta giornata Risolvere efficientemente un problema in P: la sequenza di Fibonacci Guido.
Informatica 4 Funzioni. FUNZIONE: definizione MATEMATICA Relazione (o applicazione) binaria tra due insiemi A e B che associa a ogni elemento di A un.
Allievi Elettrici - AA Le funzioni ricorsive in C
1 FONDAMENTI DI INFORMATICA II Ingegneria Gestionale a.a ° Ciclo Alberi.
CORSO DI PROGRAMMAZIONE II
Procedure e funzioni In linguaggio C.
4/25/2015E. Giovannetti -- OI09.1 Olimpiadi di Informatica 2010 Giornate preparatorie Dipartimento di Informatica Università di Torino marzo –
Algoritmi e strutture Dati - Lezione 7 1 Algoritmi di ordinamento ottimali L’algoritmo Merge-Sort ha complessità O(n log(n))  Algoritmo di ordinamento.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Università degli.
L’ordinamento per Selezione seleziona l’elemento con valore maggiore e lo scambia con il primo elemento del vettore. Tra gli N-1 elementi rimanenti viene.
C++:Strutture di Controllo
Informatica 4 La ricorsione. Definizione di ricorsione Ricorsione è la proprietà di quei programmi che, all’interno delle istruzioni che li compongono,
Copyright © Istituto Italiano Edizioni Atlas
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE La Ricorsione Marco D. Santambrogio – Ver. aggiornata al 29 Maggio 2014.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Capitolo 1 Un’introduzione.
Suggerimenti [1d5] SE la prima lettera della matrice (in alto a sinistra, matrice[0,0]) è diversa dalla prima lettera della parola (parola[0]) ALLORA siamo.
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE La Ricorsione Marco D. Santambrogio – Ver. aggiornata al 18 Maggio 2016.
Lezione n. Parole chiave: Corso di Laurea: Insegnamento: Docente: A.A Salvatore Cuomo La ricorsione 15 Approccio ricorsivo, esercizi sulla.
Transcript della presentazione:

1

2 Una ricorsione si definisce non lineare quando si ha più di una chiamata ricorsiva per blocco Per capire bene come opera una ricorsione non lineare si può tracciare un albero che mostra la successione delle chiamate o un albero che mostra la storia dello stack. Una ricorsione si definisce lineare quando si ha al massimo una chiamata ricorsiva per blocco Ricorsione lineare e non lineare

3 Supponiamo di avere il seguente codice: // MAIN int main() { int N; cout<<"main chiama A"<<endl; problemaA(1); cout<<"A ritorna a main"<<endl; cout<<"main chiama C(1)"<<endl; problemaC(1); cout<<"C(1) ritorna a main"<<endl; cout<<"main chiama Fine"<<endl; cout<<" Fine"<<endl; system("pause"); } void problemaA(int N) { cout<<"A chiama B"<<endl; problemaB(N); cout<<"B ritorna A"<<endl;} void problemaB(int N) { cout<<"C("<<N<<") chiama C("<<N-1 <<") “<<endl; cout<<"B chiama C(1)"<<endl; problemaC(1); cout<<" C(1) ritorna a B )"<<endl;} void problemaC(int N){ if (N>0) {cout<<"C("<<N<<") chiama C("<<N-1<<")"<<endl; problemaC(N-1); cout<<" C("<<N-1<<") ritorna a C("<<N<<")"<<endl; } }

4 problema Aproblema Bproblema C(1) problema C(0) MAIN problema C(1) problema C(0) Possiamo rappresentarlo mediante il grafico di figura

5 main chiama A A chiama B B chiama C(1) C(1) chiama C(0) C(0) ritorna a C(1) C(1) ritorna a B B ritorna A A ritorna a main main chiama C(1) C(1) chiama C(0) C(0) ritorna a C(1) C(1) ritorna a main main chiama Fine Fine int main() { int N; cout<<"main chiama A"<<endl; problemaA(1); cout<<"A ritorna a main"<<endl; cout<<"main chiama C(1)"<<endl; problemaC(1); cout<<"C(1) ritorna a main"<<endl; cout<<"main chiama Fine"<<endl; cout<<" Fine"<<endl; system("pause"); } // DEFINIZIONI void problemaC(int N) { if (N>0) { cout<<" C("<<N<<") chiama C("<<N-1<<")"<<endl; problemaC(N-1); cout<<" C("<<N-1<<") ritorna a C("<<N<<")"<<endl; } void problemaB(int N) { cout<<" B chiama C(1)"<<endl; // cout<<" C("<<N<<") chiama C("<<N-1<<")"<<endl; problemaC(1); cout<<" C(1) ritorna a B )"<<endl; } void problemaA(int N) { cout<<" A chiama B"<<endl; problemaB(N); cout<<" B ritorna A"<<endl; } A sinistra è possibile leggere l’output generato dal codice posto a destra

6 main C(0) ritorna a C(1) C(1) ritorna a BB ritorna A A ritorna a main C(1) ritorna a mainC(0) ritorna a C(1) main chiama A A A chiama B B B chiama C(1) C(1) C(1) chiama C(0) C(0) Fine ritorna a main main chiama C(1) C(1) C(1) chiama C(0) C(0) main chiama Fine Rappresentiamo le chiamate alle varie function con il grafico di figura

7 C(0) main A B C(1) C(0) fine Allegato: La ricorsione non lineare Più sinteticamente rappresentato dall’albero seguente:

8 Intorno all’anno 1170 a Pisa un commerciante di nome Bonaccio Pisano ebbe un figlio che chiamò Leonardo. Per motivi di lavoro si trasferì presto a Algeri dove Leonardo crebbe, studiò e imparò l’arabo avendo dei precettori musulmani. Da costoro imparò la matematica indo-arabica, il sistema decimale con la notazione posizionale delle cifre e l’uso dello zero. Leonardo Pisano scrisse il primo libro di matematica occidentale il “Liber Abaci” introducendo le cifre arabe Affascinato dal mondo e dalla cultura araba dove ogni persona ha nel suo cognome anche il nome del padre aggiunse al suo nome di Leonardo il cognome Filius Bonacci trasformato per brevità in Fibonacci. La successione di Fibonacci

9 La copia del Liber Abaci posseduta dalla Biblioteca Nazionale di Napoli

10 Problema Nel 1228 messer Leonardo Pisano detto Fibonacci si pose il seguente problema: posta una coppia di conigli in un recinto supponiamo che questa coppia dopo due mesi generi un’altra coppia e successivamente ne generi una al mese. La seconda coppia a sua volta dopo altri due mesi ne genera un’altra e così via. Dopo un anno, cioè dopo 12 mesi quante coppie di conigli ci saranno nel recinto?

La successione di Fibonacci 10

12 La successione di numeri interi che così si ottiene è detta successione di Fibonacci. E’ possibile rinvenire tale successione in molti esempi presenti in natura. Nella diapositiva successiva è mostrato il caso delle spirali dei girasole e dei cammini che un ape può percorrere per andare dalla prima cella di un alveare alla n-esima, supponendo che tutte le celle siano di forma esagonale.

13 ALCUNI CASI STRANI L’ape può raggiungere la cella n seguendo FIB(n+1) percorsi diversi Girasole con 53 (FIB(10)) spirali antiorarie e 89 (FIB(11)) orarie

14 La funzione FIB(N) che calcola i numeri di Fibonacci relativa ad un intero N obbedisce ad una regola molto semplice che si può descrivere in modo ricorsivo: Se N=0 allora FIB(N)=0 Se N=1 allora FIB(N)=1 Se N>1 allora FIB(N)=FIB(N-1)+FIB(N-2) La funzione FIB(N) si presta ad essere subito tradotta nel seguente codice Osserviamo che in una stessa riga di codice abbiamo due chiamate ricorsive (FIB(N-1)+FIB(N-2)) int FIB(int N) { if (N==0) return 0; else if (N==1) return 1; else return FIB(N-1)+FIB(N-2); }

15 Siamo di fronte ad una ricorsione non lineare quando nell’ambito di uno stesso processo ricorsivo si ha più di una chiamata ricorsiva. Pur nella sua semplicità, l’algoritmo mostrato consuma molte risorse: per numeri grandi c’è il pericolo di uno stack overflow. Per mostrare questo aspetto, introduciamo una nuova variabile, chiama, che ci restituisca il numero di volte in cui la funzione richiama se stessa; tale variabile, passata per riferimento, viene aggiunta alla lista dei parametri formali. int FIB(int N, int &chiama) { chiama++; if (N==0) return 0; else if (N==1) return 1; else return FIB(N-1, chiama)+FIB(N-2, chiama); }

16 Come precedentemente introdotto è possibile rappresentare l’evoluzione di un processo ricorsivo mostrando l’albero delle successive chiamate ricorsive. Quest’albero è mostrato nel lucido che segue per FIB(4). Nel lucido succesivo è mostrato l’albero per FIB(6) e una corrispondente rappresentazione dello stack relativo ai processi ricorsivi man mano aperti.

17 main F(3) F(1) F(2) F(4) F(2) Fibonacci(4) F(1)F(0) F(1)F(0) main F(3) F(1)F(2) F(1)F(0) Fibonacci(3) int FIB(int N, int &chiama) { chiama++; if (N==0) return 0; else if (N==1) return 1; else return FIB(N-1, chiama)+FIB(N-2,chiama); } Albero delle chiamate ricorsive di FIB(3) e FIB(4)

18 F(6) F(5)F(4) + F(2) F(3) + F(2) F(1) + F(0)F(1) F(3) + F(0)F(1) F(3) F(1) F(2) F(4) F(5) F(3) F(6) F(1)F(2) F(3) F(4) F(2) F(0)F(1) F(0) F(1) F(0)F(1) F(0) F(1) int FIB(int N) { if (N==0) return 0; else if (N==1) return 1; else return FIB(N-1)+FIB(N-2); } Albero delle chiamate ricorsive di FIB(6) stack

19 Complessità dell’algoritmo per il calcolo dei numeri di Fibonacci E’ stato dimostrato che per N abbastanza grande la complessità di F(N), cioè il numero di chiamate ricorsive, è circa O(1.61 N ). N1,61^N30 MFLOP 1 TERAFLOP sec ,3sec ,2min anni E+26anni3E+23

20 // La successione di fibonacci ricorsiva #include using namespace std; // PROTOTIPI double FIB(int, double &); // MAIN int main() { double N;double Tot; do { cout<<"Assegna N "<<endl; cin>>N; Tot=0; cout<<" Il numero di fibonacci "<<N<<" = "<< FIB(N,Tot)<<"chiamate = "<< Tot <<endl; } while (N>=0); system("pause"); } // DEFINIZIONI double FIB(int N, double &chiama) { chiama++; if (N==1) return 1; else if (N==2) return 1; else return FIB(N-1,chiama)+FIB(N-2,chiama); } Il codice che segue mostra il calcolo della successione di Fibonacci per un preassegnato valore e il numero di chiamate ricorsive necessarie.

21 Fibonacci(5) = 5 chiamate= 9 Fibonacci(10) = 55 chiamate= 109 Fibonacci(15) = 610 chiamate= Fibonacci(20) = chiamate= Fibonacci(25) = chiamate= Fibonacci(30) = chiamate= Fibonacci(35) = chiamate= Fibonacci(40) = chiamate= Fibonacci(45) = chiamate= Output del codice precedente per diversi valori di N.

22 Per ridurre da O (1.61 N ) a O(N) la complessità di calcolo per i numeri di Fibonacci invece di chiamare ricorsivamente la procedura di calcolo per ogni nodo dell’albero dello stack depositiamo i risultati di ogni computazione in un array e li richiamiamo, senza più calcolarli ogni volta che ne abbiamo bisogno. Detto FibNos l’array in cui si depositano i numeri parziali possiamo costruire una procedura ricorsiva alla seguente maniera: caso base Quando N=2 allora poni FibNos[0]  0 FibNos[1]  1 FibNos[2]  1 CHIAMATA RICORSIVA FibNos[N]  FibNos[N-2] +FibNos[N-1]

23 void FibVett(int N, double FibNos[]) { if (N==2) { FibNos[0] =0; FibNos[1] =1; FibNos[2] =1; } else FibVett(N-1,FibNos); FibNos[N]=FibNos[N-2] + FibNos[N-1]; Svantaggio Siamo limitati dalla dimensione dell’array per determinare N massimo F(3) F(1) F(2) F(4) F(2) FibVetNo(4) FibVetNo(3) FibVetNo(2) FibNos[4]=3 FibNos[0]=0 FibNos[1]=1 FibNos[2]=1 FibNos[3]=2 Di seguito mostriamo il codice, l’albero ricorsivo, e lo stack delle chiamate fibonacci

24 double fibo(double N, double prec, double att, double i, double &chiama) { double temp; if (i<N) { temp=prec+att; prec=att; att=temp; chiama++; return fibo(N,prec,att,i+1,chiama); } else return att; } int main () { double Num; cout<<" Quale numero di fibonacci desideri ? "; cin>>Num; double i=2, p=1, chiama=0; cout<<" Il fibonacci di "<<Num<<" e' "<<fibo(Num,p,a,i,chiama)<<" passi “<<chiama << endl; system("pause"); } Un’altra soluzione sempre di complessità lineare sfrutta due variabili di appoggio ed è illustrata di seguito.

25 N=6, prec=1, att=1, i=2 N=6, prec=1, att=2, i=3 N=6, prec=2, att=3, i=4 Allegato: fibonacci Lo stack ricorsivo di fibo(6) double fibo(double N, double prec, double att, double i, double &chiama) { double temp; if (i<N) { temp=prec+att; prec=att; att=temp; chiama++; return fibo(N,prec,att,i+1,chiama); } else return att; } N=6, prec=3, att=5, i=5 N=6, prec=5, att=8, i=6att=8 +

26 ESERCIZIO Detto F n l’ennesimo numero di Fibonacci scrivere un programma che calcoli l’espressione: A=F n+1 * F n-1 - F 2 n Mostrare i valori di A per N=1,2,3,4,5,6,7,8,9,10 Allegato: fibondiff

27 Esercizio 1- Assegnato un intero N, calcolare il rapporto tra le coppie di numeri di Fibonacci F(n+1)/F(n) e per ogni coppia sottrarre a detto rapporto la radice positiva dell’equazione x 2 -x-1=0. 2- Calcolare il MCD tra la somma dei numeri di Fibonacci compresi tra 1 e 10 e quelli compresi tra 11 e 20 compresi tra 11 e 20 e quelli compresi tra 21 e 30 compresi tra 21 e 30 e quelli compresi tra 31 e 40 Testo consigliato da leggere MARIO LIVIO – La sezione aurea

28 Calcolare con una funzione ricorsiva l’N-esimo termine della successione seguente a1=3 a2=1 a3=-1 a n =a n-1 *a n-2 -(a n-3 +a n-1 +a n-2 ) Caso base -> S(1)=3, S(2)=1, S(3)=-1 Chiamata ricorsiva -> S(N)=S(N-1)*S(N-2)-(S(N-3)+S(N-1)+S(N-2)) ESERCIZIO Di seguito è riportato il codice corrispondente.

29 /*Calcolare con una funzione ricorsiva l’N-esimo termine della successione seguente a1=3 a2=1 a3=-1 an=an-1*an-2-(an-3+an-1+an-2) */ double succ(double N) { if (N==1) return 3; else if (N==2) return 1; else if (N==3) return -1; else return succ(N-1)*succ(N-2)-(succ(N-3)+succ(N-1)+succ(N-2));

30 Riscrivere la procedura precedente rendendola lineare