SlideShare uma empresa Scribd logo
1 de 46
Fondamenti di informatica 1
Funzioni ricorsive
Definizioni induttive
• Sono comuni in matematica nella definizione
di proprietà di sequenze numerabili
• Esempio: numeri pari
– 0 è un numero pari
– se n è un numero pari anche n+2 è un numero
pari

• Esempio: il fattoriale di un naturale N (N!)
– se N=0 il fattoriale N! è 1
– se N>0 il fattoriale N! è N * (N-1)!
Dimostrazioni per induzione
• Dimostriamo che (2 x n)2 = 4 x n2
(distributività del quadrato rispetto alla moltiplicazione)

1) se n=1 : vero (per verifica diretta)
2) suppongo sia vero per n'=k (ip. di induz.) e
lo dimostro per n=k+1:
(2n)2 = (2(k+1))2 = (2k+2)2 = (2k)2 + 8k + 4 =
(per ipotesi di induzione) 4k2 + 8k + 4 =
4(k2 + 2k + 1) = 4(k+1)2 = 4n2
1) è il caso base, 2) è il passo induttivo
Iterazione e ricorsione
• Sono i due concetti informatici che nascono dal
concetto di induzione: applicare un'azione un
insieme numerabile e finito di volte
• L'iterazione si realizza mediante la tecnica del
ciclo
• Per il calcolo del fattoriale:
– 0! = 1
– n! = n (n - 1)(n - 2)…. 1
– realizzo un ciclo che parte dal dato richiesto e applica
il passo di induzione fino a raggiungere il caso base
Fattoriale iterativo
int fattoriale(int n) {
int fatt = 1;
for (; n > 1; --n) // non serve contatore, uso n
fatt *= n;
return fatt;
}
Progettare con la ricorsione
• Esiste un CASO BASE, che rappresenta un sotto-problema
facilmente risolvibile
• Esempio: se N=0, so N! in modo "immediato" (vale 1)

• Esiste un PASSO INDUTTIVO che ci riconduce (prima o
poi) al caso base

• L'algoritmo ricorsivo esprime la soluzione al
problema (su dati di una "dimensione" generica)
in termini di operazioni semplici e della soluzione
allo stesso problema su dati "più piccoli" (che, su
dati sufficientemente elementari, si suppone
risolto per ipotesi)
• Esempio: per N generico esprimo N! in termini di N (che è un dato
direttamente accessibile) moltiplicato per (è una operazione
semplice) il valore di (N-1)! (che so calcolare per ipotesi induttiva)
Progettare con la ricorsione
• E' un nuovo esempio del procedimento divideet-impera che spezza un problema in sottoproblemi
• Con le funzioni non ricorsive abbiamo
spezzato un problema in tanti sotto-problemi
diversi più semplici
• Con la ricorsione spezziamo il problema in
tanti sotto-problemi identici applicati a dati
più semplici
Fattoriale con la ricorsione
• 1) n! = 1 se n = 0
• 2) n! = n * (n - 1)! se n > 0
– riduce il calcolo a un calcolo più semplice
– ha senso perché si basa sempre sul fattoriale del
numero più piccolo, che io conosco
– ha senso perché si arriva a un punto in cui non è
più necessario riusare la definizione 2) e invece si
usa la 1)
– 1) è il caso base, 2) è il passo induttivo (ricorsivo)
Ricorsione nei sottoprogrammi
• Dal latino re-currere
– ricorrere, fare ripetutamente la stessa azione

• Un sottoprogramma P invoca se stesso
– Direttamente

P

• P invoca P

– oppure

– Indirettamente
• P invoca Q che invoca P

P
Q
Fattoriale ricorsivo
int fattorialeRic(int n) {
if (n == 0)
return 1;
else
return n * fattorialeRic(n - 1);
}
Simulazione del calcolo di FattRic(3)

3 = 0? No calcola fattoriale di 2 e moltiplica per 3
2 = 0? No calcola fattoriale di 1 e moltiplica per 2
1 = 0? No calcola fattoriale di 0 e moltiplica per 1
0 = 0? Si fattoriale di 0 è 1
fattoriale di 1 è 1 per fattoriale di 0, cioè 1 1 = 1
fattoriale di 2 è 2 per fattoriale di 1, cioè 2 1 = 2
fattoriale di 3 è 3 per fattoriale di 2, cioè 3 2 = 6
Esecuzione di funzioni ricorsive
• In un certo istante possono essere in corso
diverse attivazioni dello stesso
sottoprogramma
– Ovviamente sono tutte sospese tranne una,
l'ultima invocata, all'interno della quale si sta
svolgendo il flusso di esecuzione

• Ogni attivazione esegue lo stesso codice ma
opera su copie distinte dei parametri e delle
variabili locali
Il modello a runtime:
esempio

int fattorialeRic(int n) {
if (n == 0)
return 1;
else {
int temp = n * fattorialeRic(n - 1);
return temp;
}
}
int main() {
int numero;
cin >> numero;
int ris = fattorialeRic(numero);
cout << "Fattoriale ricorsivo: "
<< ris << endl;
return 0;
}

assumiamo val = 3
13

val = 3
ris =
n=3
temp =
n=2
temp =
n=1
temp =
n=0
temp =

6
3* 2

2* 1
1* 1
?

temp: cella temporanea per
memorizzare il risultato della
funzione chiamata
Terminazione della ricorsione
• … se ogni volta la funzione richiama se stessa…
perché la catena di invocazioni non continua
all'infinito?
• Quando si può dire che una ricorsione è ben
definita?
• Informalmente:
– Se per ogni applicazione del passo induttivo ci si
avvicina alla situazione riconosciuta come caso base,
allora la definizione è ben formata e la catena di
invocazioni termina
Un altro esempio: la serie di Fibonacci
• Fibonacci (1202) partì dallo studio sullo
sviluppo di una colonia di conigli in circostanze
ideali
• Partiamo da una coppia di conigli
• I conigli possono riprodursi all'età di un mese
• Supponiamo che dal secondo mese di vita in poi, ogni
femmina produca una nuova coppia
• e inoltre che i conigli non muoiano mai…

– Quante coppie ci sono dopo n mesi?
Definizione ricorsiva della serie
• I numeri di Fibonacci

F(3)

– Modello a base di molte dinamiche
evolutive delle popolazioni

• F = {f0, ..., fn}

F(1)

+
F(2)

F(1)

+
F(0)

1

1
1
– f0 = 1
casi base (due !)
– f1 = 1
1 passo induttivo
– Per n > 1, fn = fn–1 + fn–2

• Notazione "funzionale": F(i) = fi
Numeri di Fibonacci in C++
int fibo(int n) {
if (n == 0 || n == 1)
return 1;
else
return (fibo(n - 1) + fibo(n - 2));
}

Ovviamente supponiamo che n>=0
Un altro esempio: MCD à-la-Euclide
• Il MCD tra M e N (M, N naturali positivi)
– se M=N allora MCD è N
1 caso base
– se M>N allora esso è il MCD tra N e M-N
– se N>M allora esso è il MCD tra M e N-M
30

2 passi induttivi

18

12
12

18
6

6

6
MCD: iterativo & ricorsivo
int euclideIter(int m, int n)
{
while( m != n )
if ( m > n )
m = m – n;
else
n = n – m;
return m;
}

int euclideRic (int m, int n)
{
if ( m == n )
return n;
if ( m > n )
return euclideRic(m–n, n);
else
return euclideRic(m, n–m);
}
Funzione esponenziale (intera)
• Definizione iterativa:
– 1) xy = 1 se y = 0
– 2) xy = x * x * … x
(y volte) se y > 0

• Codice iterativo:
int esp (int x, int y) {
int e = 1;
for (int i = 1; i <= y; i++ )
e *= x;
return e;
}

• Codice ricorsivo:

• Definizione ricorsiva:
– 1) xy = 1 se y = 0
– 2) xy = x * x(y-1)
se y > 0

int esp (int x, int y) {
if ( y == 0 )
return 1;
else
return x * esp(x, y-1);
}
Ricorsione e passaggio per reference
(incrementare m volte una var del chiamante)
void incrementa(int &n, int m) {
if (m != 0) {
n++;
incrementa(n, m - 1);
}
}

int main() {
cout << "Inserire due numeri" << endl;
int numero, volte;
cin >> numero >> volte;
incrementa(numero, volte);
cout << numero;
return 0;
}

• n è un sinonimo della
variabile del chiamante… ciò
vale in modo ricorsivo ..
• Per cui n si riferisce sempre
alla variabile numero del
main()
Modello a run-time
num 2 / 4
/3
5
volte 3
n
m 3
n
m 2
n
m 1
n
m 0

22
Terminazione (ancora!)
• Attenzione al rischio di catene infinite di
chiamate
• Occorre che le chiamate siano soggette a una
condizione che prima o poi assicura che la
catena termini
• Occorre anche che l'argomento sia
"progressivamente ridotto" dal passo
induttivo, in modo da tendere prima o poi al
caso base
Costruzione di una stringa invertita
• Data un stringa s1 produrre una seconda
stringa s2 che contiene i caratteri in ordine
inverso
A

B

C

B

C

+
C

A

+

B

+

A
Costruzione di una stringa invertita
string inversione(string s) {
// caso base
if (s.size() == 1)
return s;
// passo induttivo
return
inversione(s.substr(1,s.size()-1))
+ s[0];
}
string substr (size_t pos = 0, size_t
len = npos) const;

• Restituisce una nuova stringa
costruita con len caratteri a
partire da pos
• http://www.cplusplus.com/ref
erence/string/string/substr/

int main() {
string s1 = "Hello world!!";
string s2;
s2 = inversione(s1);
cout << s2;
return 0;
}

• NB: Soluzione non ottimale
che crea una stringa
temporanea per ogni carattere
della stringa da invertire
Palindromi in versione ricorsiva
• Un palindromo è tale se:
• la parola è di lunghezza 0 o 1;
– oppure

Caso base

• il primo e l'ultimo carattere della parola sono
uguali e inoltre la sotto-parola che si ottiene
ignorando i caratteri estremi è a sua volta un
palindromo
Passo induttivo
• Il passo induttivo riduce la dimensione del
problema!
Progettazione
A

da

C

C

A

V

A

L

L

A

V

A

C

C

A

a
Codice
bool palindroma(string par, int da, int a) {
if (da >= a)
return true;
else
return (par[da] == par[a] &&
palindroma(par, da+1, a-1));
}

• Notare la regola del
cortocircuito
• Evita il passo ricorsivo se si
trovano due caratteri
discordi

int main() {
string parola;
cout << "Inserisci la parola" << endl;
cin >> parola;
bool risultato =
palindroma(parola,0,parola.size()-1);
if (risultato)
cout << "La parola " << parola
<< " è palindroma" << endl;
else
cout << "La parola " << parola
<< " NON è palindroma" << endl;
return 0;
}

• Notare che il primo passo
richiede di inizializzare la
ricorsione con i valori
degli estremi di partenza
Ricerca Binaria
• Scrivere un programma che implementi
l’algoritmo di ricerca dicotomica in un vettore
ordinato in senso crescente, con procedimento
ricorsivo.
• Dato un valore val da trovare e un vettore array
con due indici low, high, che puntano
rispettivamente al primo e ultimo elemento;
– L’algoritmo di ricerca dicotomica prevede che se
l’elemento f non è al centro del vettore cioè in
posizione “m = (low+high)/2” allora deve essere
ricercato ricorsivamente soltanto in uno dei due sottovettori a destra o a sinistra dell’elemento centrale
Progettazione
• Se low > high, allora l’elemento cercato f non è
presente nel vettore
(caso base)
• Se (val == array [ (low+high) / 2 ]), allora f è
presente nel vettore. (caso base)
• Altrimenti (passo induttivo)
– Se (f > array[ (low+high) / 2 ]) la ricerca deve
continuare nel sottovettore individuato dagli elementi
con indici nell’intervallo *m +1, high+
– Se (f < array[ (low+high) / 2 ]) allora la ricerca deve
continuare nel sottovettore individuato dagli elementi
con indici nell’intervallo *low, m - 1]
Codice
bool BinarySearch(int array[], int
low, int high, int val) {
int m;
if (low > high)
return false;
else {
m = (low + high) / 2;
if (val == array[m])
return true;
else if (val > array[m])
return BinarySearch(array,
m + 1, high, val);
else
return BinarySearch(array,
low, m - 1, val);
}
}

int main() {
int sequenza[] =
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int valore;
cin >> valore;
bool trovato =
BinarySearch(sequenza,0,size-1,valore);
if (trovato)
cout << "Risultato trovato " << endl;
else
cout << "Risultato non presente"
<< endl;
return 0;
}
Le torri di Hanoi
Stampare le mosse necessarie per spostare tutta la torre da A a C
muovendo un cerchio alla volta e senza mai mettere un
cerchio più grosso su uno più piccolo

Torre di
n
dischi

A

B

C

FORMULAZIONE RICORSIVA?
Le torri di Hanoi

FORMULAZIONE RICORSIVA

Torre di
n-1
dischi

A
33

B

C
Le torri di Hanoi

A
34

B

C
Le torri di Hanoi

A
35

B

C
Le torri di Hanoi

A
36

B

C
Progettazione ricorsiva
• Spostare la torre di 1 elemento da non viola
mai le regole e si effettua con un passo
elementare (caso base)
• Per spostare la torre di N elementi, p.e. da A a
C
– sposto la torre di N-1 cerchi da A a B (ricorsione)
– sposto il cerchio restante in C
– sposto la torre di N-1 elementi da B a C
(ricorsione)
Prototipo della funzione
hanoi(int altezza, char da, char a, char usando)

Piolo di partenza
Altezza della
piramide da spostare

Piolo di arrivo

Piolo di transito
Algoritmo
• Se devi spostare una piramide alta N da x a y
transitando da z
• Sposta una piramide alta N-1 da x a z,
transitando per y
• Sposta il disco N-esimo da x a y
–  stampa la mossa

• Sposta una piramide alta N-1 da z a y,
transitando per x
Codice
void hanoi (int altezza, char da, char a,
char transito) {
if (altezza > 0) {
hanoi (altezza-1, da, transito, a);
cout << "Sposta cerchio da "
<< da << " a "<< a <<endl;
hanoi (altezza-1, transito, a, da);
}
}

int main() {
hanoi (3, 'A', 'C', 'B');
return 0;
}
Hanoi: soluzione iterativa
• Non è così evidente…
• Stabiliamo un "senso orario" tra i pioli: 1, 2, 3
e poi ancora 1, ecc.
• Per muovere la torre nel prossimo piolo in
senso orario bisogna ripetere questi due passi:
– sposta il disco più piccolo in senso orario
– fai l'unico altro spostamento possibile con un altro
disco
Ricorsione o iterazione?
•
•
•
•

Spesso le soluzioni ricorsive sono eleganti
Sono vicine alla definizione del problema
Però possono essere inefficienti
Chiamare un sottoprogramma significa
allocare memoria a run-time
N.B. è sempre possibile trovare un
corrispondente iterativo di un
programma ricorsivo
Calcolo numeri di fibonacci
int fibo(int n) {
if (n == 0 || n == 1)
return 1;
else
return (fibo(n - 1) + fibo(n - 2));
}

• Drammaticamente inefficiente!
• Calcola più volte l'i-esimo numero di
Fibonacci!
Soluzione con memoria di supporto
• La prima volta che calcolo un dato numero di
Fibonacci lo memorizzo in un array
• Dalla seconda volta in poi, anziché ricalcolarlo,
lo leggo direttamente dall'array
• Mi occorre un valore "sentinella" con cui
inizializzare l'array che mi indichi che il
numero di Fibonacci corrispondente non è
ancora stato calcolato
– Qui posso usare ad esempio 0
Codice
long fib(int n, long memo[]) {
if (memo[n] != 0)
return memo[n];
memo[n] = fib(n-1,memo) +
fib(n-2, memo);
return memo[n];
}

•
•
•
•

const int MAX = 10;
int main() {
int n;
long memo[MAX];
for (int i = 2; i < MAX; i++)
memo[i] = 0;
memo[0] = 1;
memo[1] = 1; // casi base
cout << "Inserire intero: " << endl;
cin >> n;
cout << "fibonacci di " << n
<< " = " << fib(n, memo);
return 0;
}

Drastica riduzione della complessità (aumento di efficienza)
Questa soluzione richiede un tempo lineare in n
La soluzione precedente richiede un tempo esponenziale in n
Il prezzo è il consumo di memoria in qtà proporzionale a N
Check this out
• http://stackoverflow.com/questions/360748/c
omputational-complexity-of-fibonaccisequence

Mais conteúdo relacionado

Mais procurados

Chapter 2 Operating System Structures.ppt
Chapter 2 Operating System Structures.pptChapter 2 Operating System Structures.ppt
Chapter 2 Operating System Structures.pptErenJeager20
 
Memory : operating system ( Btech cse )
Memory : operating system ( Btech cse )Memory : operating system ( Btech cse )
Memory : operating system ( Btech cse )HimanshuSharma1389
 
Understanding lattice Boltzmann boundary conditions through moments
Understanding lattice Boltzmann boundary conditions through momentsUnderstanding lattice Boltzmann boundary conditions through moments
Understanding lattice Boltzmann boundary conditions through momentsTim Reis
 
Discrete event simulation
Discrete event simulationDiscrete event simulation
Discrete event simulationssusera970cc
 
Os Swapping, Paging, Segmentation and Virtual Memory
Os Swapping, Paging, Segmentation and Virtual MemoryOs Swapping, Paging, Segmentation and Virtual Memory
Os Swapping, Paging, Segmentation and Virtual Memorysgpraju
 
Interprocess communication
Interprocess communicationInterprocess communication
Interprocess communicationSushil Singh
 
Ch7 Process Synchronization galvin
Ch7 Process Synchronization galvinCh7 Process Synchronization galvin
Ch7 Process Synchronization galvinShubham Singh
 
Operating system 23 process synchronization
Operating system 23 process synchronizationOperating system 23 process synchronization
Operating system 23 process synchronizationVaibhav Khanna
 
What is a single sample t test?
What is a single sample t test?What is a single sample t test?
What is a single sample t test?Ken Plummer
 
The Message Passing Interface (MPI) in Layman's Terms
The Message Passing Interface (MPI) in Layman's TermsThe Message Passing Interface (MPI) in Layman's Terms
The Message Passing Interface (MPI) in Layman's TermsJeff Squyres
 
Discrete And Continuous Simulation
Discrete And Continuous SimulationDiscrete And Continuous Simulation
Discrete And Continuous SimulationNguyen Chien
 
4-Unconstrained Single Variable Optimization-Methods and Application.pdf
4-Unconstrained Single Variable Optimization-Methods and Application.pdf4-Unconstrained Single Variable Optimization-Methods and Application.pdf
4-Unconstrained Single Variable Optimization-Methods and Application.pdfkhadijabutt34
 
Imputation techniques for missing data in clinical trials
Imputation techniques for missing data in clinical trialsImputation techniques for missing data in clinical trials
Imputation techniques for missing data in clinical trialsNitin George
 

Mais procurados (20)

Chapter 2 Operating System Structures.ppt
Chapter 2 Operating System Structures.pptChapter 2 Operating System Structures.ppt
Chapter 2 Operating System Structures.ppt
 
Memory : operating system ( Btech cse )
Memory : operating system ( Btech cse )Memory : operating system ( Btech cse )
Memory : operating system ( Btech cse )
 
Soft computing
Soft computingSoft computing
Soft computing
 
Reasoning in AI.pdf
Reasoning in AI.pdfReasoning in AI.pdf
Reasoning in AI.pdf
 
Understanding lattice Boltzmann boundary conditions through moments
Understanding lattice Boltzmann boundary conditions through momentsUnderstanding lattice Boltzmann boundary conditions through moments
Understanding lattice Boltzmann boundary conditions through moments
 
Linux Memory Management
Linux Memory ManagementLinux Memory Management
Linux Memory Management
 
Discrete event simulation
Discrete event simulationDiscrete event simulation
Discrete event simulation
 
Deadlock
DeadlockDeadlock
Deadlock
 
Os Swapping, Paging, Segmentation and Virtual Memory
Os Swapping, Paging, Segmentation and Virtual MemoryOs Swapping, Paging, Segmentation and Virtual Memory
Os Swapping, Paging, Segmentation and Virtual Memory
 
Ai 7
Ai 7Ai 7
Ai 7
 
Interprocess communication
Interprocess communicationInterprocess communication
Interprocess communication
 
Ch7 Process Synchronization galvin
Ch7 Process Synchronization galvinCh7 Process Synchronization galvin
Ch7 Process Synchronization galvin
 
Operating system 23 process synchronization
Operating system 23 process synchronizationOperating system 23 process synchronization
Operating system 23 process synchronization
 
What is a single sample t test?
What is a single sample t test?What is a single sample t test?
What is a single sample t test?
 
Operating System
Operating SystemOperating System
Operating System
 
The Message Passing Interface (MPI) in Layman's Terms
The Message Passing Interface (MPI) in Layman's TermsThe Message Passing Interface (MPI) in Layman's Terms
The Message Passing Interface (MPI) in Layman's Terms
 
Discrete And Continuous Simulation
Discrete And Continuous SimulationDiscrete And Continuous Simulation
Discrete And Continuous Simulation
 
4-Unconstrained Single Variable Optimization-Methods and Application.pdf
4-Unconstrained Single Variable Optimization-Methods and Application.pdf4-Unconstrained Single Variable Optimization-Methods and Application.pdf
4-Unconstrained Single Variable Optimization-Methods and Application.pdf
 
Imputation techniques for missing data in clinical trials
Imputation techniques for missing data in clinical trialsImputation techniques for missing data in clinical trials
Imputation techniques for missing data in clinical trials
 
Hypothesis
HypothesisHypothesis
Hypothesis
 

Semelhante a 07 2 ricorsione

Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di Analisi
Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di AnalisiAlgoritmi e Calcolo Parallelo 2012/2013 - Tecniche di Analisi
Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di AnalisiPier Luca Lanzi
 
Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)STELITANO
 
Calcolo Numerico - 2 - Rappresentazione Dei Numeri
Calcolo Numerico - 2 - Rappresentazione Dei NumeriCalcolo Numerico - 2 - Rappresentazione Dei Numeri
Calcolo Numerico - 2 - Rappresentazione Dei NumeriMajong DevJfu
 
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli Algoritmi
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli AlgoritmiAlgoritmi e Calcolo Parallelo 2012/2013 - Analisi degli Algoritmi
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli AlgoritmiPier Luca Lanzi
 
05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioniPiero Fraternali
 
Lezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteLezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteSTELITANO
 
Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)STELITANO
 
Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)STELITANO
 
2 Rappresentazione Dei Dati
2   Rappresentazione Dei Dati2   Rappresentazione Dei Dati
2 Rappresentazione Dei Datiguest60e9511
 
Lezione 9 (14 marzo 2012)
Lezione 9 (14 marzo 2012)Lezione 9 (14 marzo 2012)
Lezione 9 (14 marzo 2012)STELITANO
 
Lezione 8 (12 marzo 2012)
Lezione 8 (12 marzo 2012)Lezione 8 (12 marzo 2012)
Lezione 8 (12 marzo 2012)STELITANO
 
Complessita' computazionale
Complessita' computazionaleComplessita' computazionale
Complessita' computazionaleSaraDiLuzio2
 
Schema Risolutivo Delle Serie
Schema Risolutivo Delle SerieSchema Risolutivo Delle Serie
Schema Risolutivo Delle SerieDinoIT
 
Python - Primi passi
Python - Primi passi Python - Primi passi
Python - Primi passi orestJump
 
Lezione 2 alberi e differenze finite
Lezione 2   alberi e differenze finiteLezione 2   alberi e differenze finite
Lezione 2 alberi e differenze finiteGiovanni Della Lunga
 
Complessita computazionalee
Complessita computazionaleeComplessita computazionalee
Complessita computazionaleeSimoneAielli1
 

Semelhante a 07 2 ricorsione (20)

Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di Analisi
Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di AnalisiAlgoritmi e Calcolo Parallelo 2012/2013 - Tecniche di Analisi
Algoritmi e Calcolo Parallelo 2012/2013 - Tecniche di Analisi
 
Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)Lezione 23 (9 maggio 2012)
Lezione 23 (9 maggio 2012)
 
Dirichlet L-Series
Dirichlet L-SeriesDirichlet L-Series
Dirichlet L-Series
 
Calcolo Numerico - 2 - Rappresentazione Dei Numeri
Calcolo Numerico - 2 - Rappresentazione Dei NumeriCalcolo Numerico - 2 - Rappresentazione Dei Numeri
Calcolo Numerico - 2 - Rappresentazione Dei Numeri
 
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli Algoritmi
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli AlgoritmiAlgoritmi e Calcolo Parallelo 2012/2013 - Analisi degli Algoritmi
Algoritmi e Calcolo Parallelo 2012/2013 - Analisi degli Algoritmi
 
05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni05 3 istruzioni-selezione-iterazione-condizioni
05 3 istruzioni-selezione-iterazione-condizioni
 
Lezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parteLezione 20 (2 maggio 2012) prima parte
Lezione 20 (2 maggio 2012) prima parte
 
Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)
 
Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)Lezione 10 (21 marzo 2012)
Lezione 10 (21 marzo 2012)
 
Reti Logic
Reti LogicReti Logic
Reti Logic
 
2 Rappresentazione Dei Dati
2   Rappresentazione Dei Dati2   Rappresentazione Dei Dati
2 Rappresentazione Dei Dati
 
Lezione 9 (14 marzo 2012)
Lezione 9 (14 marzo 2012)Lezione 9 (14 marzo 2012)
Lezione 9 (14 marzo 2012)
 
Lezione 8 (12 marzo 2012)
Lezione 8 (12 marzo 2012)Lezione 8 (12 marzo 2012)
Lezione 8 (12 marzo 2012)
 
Metodo di Newton
Metodo di NewtonMetodo di Newton
Metodo di Newton
 
Complessita' computazionale
Complessita' computazionaleComplessita' computazionale
Complessita' computazionale
 
Schema Risolutivo Delle Serie
Schema Risolutivo Delle SerieSchema Risolutivo Delle Serie
Schema Risolutivo Delle Serie
 
Python - Primi passi
Python - Primi passi Python - Primi passi
Python - Primi passi
 
Lezione 2 alberi e differenze finite
Lezione 2   alberi e differenze finiteLezione 2   alberi e differenze finite
Lezione 2 alberi e differenze finite
 
Complessita computazionalee
Complessita computazionaleeComplessita computazionalee
Complessita computazionalee
 
2008 python
2008 python2008 python
2008 python
 

Mais de Piero Fraternali

Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Piero Fraternali
 
presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015Piero Fraternali
 
Fraternali concertation june25bruxelles
Fraternali concertation june25bruxellesFraternali concertation june25bruxelles
Fraternali concertation june25bruxellesPiero Fraternali
 
Crowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaCrowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaPiero Fraternali
 
06 1 array_stringhe_typedef
06 1 array_stringhe_typedef06 1 array_stringhe_typedef
06 1 array_stringhe_typedefPiero Fraternali
 
05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusionePiero Fraternali
 
Human computation and participatory systems
Human computation and participatory systems Human computation and participatory systems
Human computation and participatory systems Piero Fraternali
 
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Piero Fraternali
 
Human and social computation
Human and social computation Human and social computation
Human and social computation Piero Fraternali
 
Web technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringWeb technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringPiero Fraternali
 

Mais de Piero Fraternali (20)

Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016Multimedia on the mountaintop: presentation at ACM MM2016
Multimedia on the mountaintop: presentation at ACM MM2016
 
presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015presentation at European Utility Week, Vienna, Nov. 2015
presentation at European Utility Week, Vienna, Nov. 2015
 
Fraternali concertation june25bruxelles
Fraternali concertation june25bruxellesFraternali concertation june25bruxelles
Fraternali concertation june25bruxelles
 
Crowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della modaCrowsourcing (anche) per le aziende del settore tessile e della moda
Crowsourcing (anche) per le aziende del settore tessile e della moda
 
07 1 funzioni
07 1 funzioni07 1 funzioni
07 1 funzioni
 
06 2 vector_matrici
06 2 vector_matrici06 2 vector_matrici
06 2 vector_matrici
 
06 1 array_stringhe_typedef
06 1 array_stringhe_typedef06 1 array_stringhe_typedef
06 1 array_stringhe_typedef
 
05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione05 2 integrali-conversioni-costanti-preproc-inclusione
05 2 integrali-conversioni-costanti-preproc-inclusione
 
05 1 intro-struttura
05 1 intro-struttura05 1 intro-struttura
05 1 intro-struttura
 
03 2 arit_bin
03 2 arit_bin03 2 arit_bin
03 2 arit_bin
 
03 1 alg_bool
03 1 alg_bool03 1 alg_bool
03 1 alg_bool
 
02 algo programmi
02 algo programmi02 algo programmi
02 algo programmi
 
06 3 struct
06 3 struct06 3 struct
06 3 struct
 
Human computation and participatory systems
Human computation and participatory systems Human computation and participatory systems
Human computation and participatory systems
 
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
Better society: Meet us at #ICT2013eu for #trustedsocialmedia http://bit.ly/1...
 
So human presentation
So human presentationSo human presentation
So human presentation
 
Human and social computation
Human and social computation Human and social computation
Human and social computation
 
Web technologies: Model Driven Engineering
Web technologies: Model Driven EngineeringWeb technologies: Model Driven Engineering
Web technologies: Model Driven Engineering
 
Common Gateway Interface
Common Gateway InterfaceCommon Gateway Interface
Common Gateway Interface
 
Web technologies: HTTP
Web technologies: HTTPWeb technologies: HTTP
Web technologies: HTTP
 

07 2 ricorsione

  • 1. Fondamenti di informatica 1 Funzioni ricorsive
  • 2. Definizioni induttive • Sono comuni in matematica nella definizione di proprietà di sequenze numerabili • Esempio: numeri pari – 0 è un numero pari – se n è un numero pari anche n+2 è un numero pari • Esempio: il fattoriale di un naturale N (N!) – se N=0 il fattoriale N! è 1 – se N>0 il fattoriale N! è N * (N-1)!
  • 3. Dimostrazioni per induzione • Dimostriamo che (2 x n)2 = 4 x n2 (distributività del quadrato rispetto alla moltiplicazione) 1) se n=1 : vero (per verifica diretta) 2) suppongo sia vero per n'=k (ip. di induz.) e lo dimostro per n=k+1: (2n)2 = (2(k+1))2 = (2k+2)2 = (2k)2 + 8k + 4 = (per ipotesi di induzione) 4k2 + 8k + 4 = 4(k2 + 2k + 1) = 4(k+1)2 = 4n2 1) è il caso base, 2) è il passo induttivo
  • 4. Iterazione e ricorsione • Sono i due concetti informatici che nascono dal concetto di induzione: applicare un'azione un insieme numerabile e finito di volte • L'iterazione si realizza mediante la tecnica del ciclo • Per il calcolo del fattoriale: – 0! = 1 – n! = n (n - 1)(n - 2)…. 1 – realizzo un ciclo che parte dal dato richiesto e applica il passo di induzione fino a raggiungere il caso base
  • 5. Fattoriale iterativo int fattoriale(int n) { int fatt = 1; for (; n > 1; --n) // non serve contatore, uso n fatt *= n; return fatt; }
  • 6. Progettare con la ricorsione • Esiste un CASO BASE, che rappresenta un sotto-problema facilmente risolvibile • Esempio: se N=0, so N! in modo "immediato" (vale 1) • Esiste un PASSO INDUTTIVO che ci riconduce (prima o poi) al caso base • L'algoritmo ricorsivo esprime la soluzione al problema (su dati di una "dimensione" generica) in termini di operazioni semplici e della soluzione allo stesso problema su dati "più piccoli" (che, su dati sufficientemente elementari, si suppone risolto per ipotesi) • Esempio: per N generico esprimo N! in termini di N (che è un dato direttamente accessibile) moltiplicato per (è una operazione semplice) il valore di (N-1)! (che so calcolare per ipotesi induttiva)
  • 7. Progettare con la ricorsione • E' un nuovo esempio del procedimento divideet-impera che spezza un problema in sottoproblemi • Con le funzioni non ricorsive abbiamo spezzato un problema in tanti sotto-problemi diversi più semplici • Con la ricorsione spezziamo il problema in tanti sotto-problemi identici applicati a dati più semplici
  • 8. Fattoriale con la ricorsione • 1) n! = 1 se n = 0 • 2) n! = n * (n - 1)! se n > 0 – riduce il calcolo a un calcolo più semplice – ha senso perché si basa sempre sul fattoriale del numero più piccolo, che io conosco – ha senso perché si arriva a un punto in cui non è più necessario riusare la definizione 2) e invece si usa la 1) – 1) è il caso base, 2) è il passo induttivo (ricorsivo)
  • 9. Ricorsione nei sottoprogrammi • Dal latino re-currere – ricorrere, fare ripetutamente la stessa azione • Un sottoprogramma P invoca se stesso – Direttamente P • P invoca P – oppure – Indirettamente • P invoca Q che invoca P P Q
  • 10. Fattoriale ricorsivo int fattorialeRic(int n) { if (n == 0) return 1; else return n * fattorialeRic(n - 1); }
  • 11. Simulazione del calcolo di FattRic(3) 3 = 0? No calcola fattoriale di 2 e moltiplica per 3 2 = 0? No calcola fattoriale di 1 e moltiplica per 2 1 = 0? No calcola fattoriale di 0 e moltiplica per 1 0 = 0? Si fattoriale di 0 è 1 fattoriale di 1 è 1 per fattoriale di 0, cioè 1 1 = 1 fattoriale di 2 è 2 per fattoriale di 1, cioè 2 1 = 2 fattoriale di 3 è 3 per fattoriale di 2, cioè 3 2 = 6
  • 12. Esecuzione di funzioni ricorsive • In un certo istante possono essere in corso diverse attivazioni dello stesso sottoprogramma – Ovviamente sono tutte sospese tranne una, l'ultima invocata, all'interno della quale si sta svolgendo il flusso di esecuzione • Ogni attivazione esegue lo stesso codice ma opera su copie distinte dei parametri e delle variabili locali
  • 13. Il modello a runtime: esempio int fattorialeRic(int n) { if (n == 0) return 1; else { int temp = n * fattorialeRic(n - 1); return temp; } } int main() { int numero; cin >> numero; int ris = fattorialeRic(numero); cout << "Fattoriale ricorsivo: " << ris << endl; return 0; } assumiamo val = 3 13 val = 3 ris = n=3 temp = n=2 temp = n=1 temp = n=0 temp = 6 3* 2 2* 1 1* 1 ? temp: cella temporanea per memorizzare il risultato della funzione chiamata
  • 14. Terminazione della ricorsione • … se ogni volta la funzione richiama se stessa… perché la catena di invocazioni non continua all'infinito? • Quando si può dire che una ricorsione è ben definita? • Informalmente: – Se per ogni applicazione del passo induttivo ci si avvicina alla situazione riconosciuta come caso base, allora la definizione è ben formata e la catena di invocazioni termina
  • 15. Un altro esempio: la serie di Fibonacci • Fibonacci (1202) partì dallo studio sullo sviluppo di una colonia di conigli in circostanze ideali • Partiamo da una coppia di conigli • I conigli possono riprodursi all'età di un mese • Supponiamo che dal secondo mese di vita in poi, ogni femmina produca una nuova coppia • e inoltre che i conigli non muoiano mai… – Quante coppie ci sono dopo n mesi?
  • 16. Definizione ricorsiva della serie • I numeri di Fibonacci F(3) – Modello a base di molte dinamiche evolutive delle popolazioni • F = {f0, ..., fn} F(1) + F(2) F(1) + F(0) 1 1 1 – f0 = 1 casi base (due !) – f1 = 1 1 passo induttivo – Per n > 1, fn = fn–1 + fn–2 • Notazione "funzionale": F(i) = fi
  • 17. Numeri di Fibonacci in C++ int fibo(int n) { if (n == 0 || n == 1) return 1; else return (fibo(n - 1) + fibo(n - 2)); } Ovviamente supponiamo che n>=0
  • 18. Un altro esempio: MCD à-la-Euclide • Il MCD tra M e N (M, N naturali positivi) – se M=N allora MCD è N 1 caso base – se M>N allora esso è il MCD tra N e M-N – se N>M allora esso è il MCD tra M e N-M 30 2 passi induttivi 18 12 12 18 6 6 6
  • 19. MCD: iterativo & ricorsivo int euclideIter(int m, int n) { while( m != n ) if ( m > n ) m = m – n; else n = n – m; return m; } int euclideRic (int m, int n) { if ( m == n ) return n; if ( m > n ) return euclideRic(m–n, n); else return euclideRic(m, n–m); }
  • 20. Funzione esponenziale (intera) • Definizione iterativa: – 1) xy = 1 se y = 0 – 2) xy = x * x * … x (y volte) se y > 0 • Codice iterativo: int esp (int x, int y) { int e = 1; for (int i = 1; i <= y; i++ ) e *= x; return e; } • Codice ricorsivo: • Definizione ricorsiva: – 1) xy = 1 se y = 0 – 2) xy = x * x(y-1) se y > 0 int esp (int x, int y) { if ( y == 0 ) return 1; else return x * esp(x, y-1); }
  • 21. Ricorsione e passaggio per reference (incrementare m volte una var del chiamante) void incrementa(int &n, int m) { if (m != 0) { n++; incrementa(n, m - 1); } } int main() { cout << "Inserire due numeri" << endl; int numero, volte; cin >> numero >> volte; incrementa(numero, volte); cout << numero; return 0; } • n è un sinonimo della variabile del chiamante… ciò vale in modo ricorsivo .. • Per cui n si riferisce sempre alla variabile numero del main()
  • 22. Modello a run-time num 2 / 4 /3 5 volte 3 n m 3 n m 2 n m 1 n m 0 22
  • 23. Terminazione (ancora!) • Attenzione al rischio di catene infinite di chiamate • Occorre che le chiamate siano soggette a una condizione che prima o poi assicura che la catena termini • Occorre anche che l'argomento sia "progressivamente ridotto" dal passo induttivo, in modo da tendere prima o poi al caso base
  • 24. Costruzione di una stringa invertita • Data un stringa s1 produrre una seconda stringa s2 che contiene i caratteri in ordine inverso A B C B C + C A + B + A
  • 25. Costruzione di una stringa invertita string inversione(string s) { // caso base if (s.size() == 1) return s; // passo induttivo return inversione(s.substr(1,s.size()-1)) + s[0]; } string substr (size_t pos = 0, size_t len = npos) const; • Restituisce una nuova stringa costruita con len caratteri a partire da pos • http://www.cplusplus.com/ref erence/string/string/substr/ int main() { string s1 = "Hello world!!"; string s2; s2 = inversione(s1); cout << s2; return 0; } • NB: Soluzione non ottimale che crea una stringa temporanea per ogni carattere della stringa da invertire
  • 26. Palindromi in versione ricorsiva • Un palindromo è tale se: • la parola è di lunghezza 0 o 1; – oppure Caso base • il primo e l'ultimo carattere della parola sono uguali e inoltre la sotto-parola che si ottiene ignorando i caratteri estremi è a sua volta un palindromo Passo induttivo • Il passo induttivo riduce la dimensione del problema!
  • 28. Codice bool palindroma(string par, int da, int a) { if (da >= a) return true; else return (par[da] == par[a] && palindroma(par, da+1, a-1)); } • Notare la regola del cortocircuito • Evita il passo ricorsivo se si trovano due caratteri discordi int main() { string parola; cout << "Inserisci la parola" << endl; cin >> parola; bool risultato = palindroma(parola,0,parola.size()-1); if (risultato) cout << "La parola " << parola << " è palindroma" << endl; else cout << "La parola " << parola << " NON è palindroma" << endl; return 0; } • Notare che il primo passo richiede di inizializzare la ricorsione con i valori degli estremi di partenza
  • 29. Ricerca Binaria • Scrivere un programma che implementi l’algoritmo di ricerca dicotomica in un vettore ordinato in senso crescente, con procedimento ricorsivo. • Dato un valore val da trovare e un vettore array con due indici low, high, che puntano rispettivamente al primo e ultimo elemento; – L’algoritmo di ricerca dicotomica prevede che se l’elemento f non è al centro del vettore cioè in posizione “m = (low+high)/2” allora deve essere ricercato ricorsivamente soltanto in uno dei due sottovettori a destra o a sinistra dell’elemento centrale
  • 30. Progettazione • Se low > high, allora l’elemento cercato f non è presente nel vettore (caso base) • Se (val == array [ (low+high) / 2 ]), allora f è presente nel vettore. (caso base) • Altrimenti (passo induttivo) – Se (f > array[ (low+high) / 2 ]) la ricerca deve continuare nel sottovettore individuato dagli elementi con indici nell’intervallo *m +1, high+ – Se (f < array[ (low+high) / 2 ]) allora la ricerca deve continuare nel sottovettore individuato dagli elementi con indici nell’intervallo *low, m - 1]
  • 31. Codice bool BinarySearch(int array[], int low, int high, int val) { int m; if (low > high) return false; else { m = (low + high) / 2; if (val == array[m]) return true; else if (val > array[m]) return BinarySearch(array, m + 1, high, val); else return BinarySearch(array, low, m - 1, val); } } int main() { int sequenza[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int valore; cin >> valore; bool trovato = BinarySearch(sequenza,0,size-1,valore); if (trovato) cout << "Risultato trovato " << endl; else cout << "Risultato non presente" << endl; return 0; }
  • 32. Le torri di Hanoi Stampare le mosse necessarie per spostare tutta la torre da A a C muovendo un cerchio alla volta e senza mai mettere un cerchio più grosso su uno più piccolo Torre di n dischi A B C FORMULAZIONE RICORSIVA?
  • 33. Le torri di Hanoi FORMULAZIONE RICORSIVA Torre di n-1 dischi A 33 B C
  • 34. Le torri di Hanoi A 34 B C
  • 35. Le torri di Hanoi A 35 B C
  • 36. Le torri di Hanoi A 36 B C
  • 37. Progettazione ricorsiva • Spostare la torre di 1 elemento da non viola mai le regole e si effettua con un passo elementare (caso base) • Per spostare la torre di N elementi, p.e. da A a C – sposto la torre di N-1 cerchi da A a B (ricorsione) – sposto il cerchio restante in C – sposto la torre di N-1 elementi da B a C (ricorsione)
  • 38. Prototipo della funzione hanoi(int altezza, char da, char a, char usando) Piolo di partenza Altezza della piramide da spostare Piolo di arrivo Piolo di transito
  • 39. Algoritmo • Se devi spostare una piramide alta N da x a y transitando da z • Sposta una piramide alta N-1 da x a z, transitando per y • Sposta il disco N-esimo da x a y –  stampa la mossa • Sposta una piramide alta N-1 da z a y, transitando per x
  • 40. Codice void hanoi (int altezza, char da, char a, char transito) { if (altezza > 0) { hanoi (altezza-1, da, transito, a); cout << "Sposta cerchio da " << da << " a "<< a <<endl; hanoi (altezza-1, transito, a, da); } } int main() { hanoi (3, 'A', 'C', 'B'); return 0; }
  • 41. Hanoi: soluzione iterativa • Non è così evidente… • Stabiliamo un "senso orario" tra i pioli: 1, 2, 3 e poi ancora 1, ecc. • Per muovere la torre nel prossimo piolo in senso orario bisogna ripetere questi due passi: – sposta il disco più piccolo in senso orario – fai l'unico altro spostamento possibile con un altro disco
  • 42. Ricorsione o iterazione? • • • • Spesso le soluzioni ricorsive sono eleganti Sono vicine alla definizione del problema Però possono essere inefficienti Chiamare un sottoprogramma significa allocare memoria a run-time N.B. è sempre possibile trovare un corrispondente iterativo di un programma ricorsivo
  • 43. Calcolo numeri di fibonacci int fibo(int n) { if (n == 0 || n == 1) return 1; else return (fibo(n - 1) + fibo(n - 2)); } • Drammaticamente inefficiente! • Calcola più volte l'i-esimo numero di Fibonacci!
  • 44. Soluzione con memoria di supporto • La prima volta che calcolo un dato numero di Fibonacci lo memorizzo in un array • Dalla seconda volta in poi, anziché ricalcolarlo, lo leggo direttamente dall'array • Mi occorre un valore "sentinella" con cui inizializzare l'array che mi indichi che il numero di Fibonacci corrispondente non è ancora stato calcolato – Qui posso usare ad esempio 0
  • 45. Codice long fib(int n, long memo[]) { if (memo[n] != 0) return memo[n]; memo[n] = fib(n-1,memo) + fib(n-2, memo); return memo[n]; } • • • • const int MAX = 10; int main() { int n; long memo[MAX]; for (int i = 2; i < MAX; i++) memo[i] = 0; memo[0] = 1; memo[1] = 1; // casi base cout << "Inserire intero: " << endl; cin >> n; cout << "fibonacci di " << n << " = " << fib(n, memo); return 0; } Drastica riduzione della complessità (aumento di efficienza) Questa soluzione richiede un tempo lineare in n La soluzione precedente richiede un tempo esponenziale in n Il prezzo è il consumo di memoria in qtà proporzionale a N
  • 46. Check this out • http://stackoverflow.com/questions/360748/c omputational-complexity-of-fibonaccisequence