• Non ci sono risultati.

La ricorsione

N/A
N/A
Protected

Academic year: 2021

Condividi "La ricorsione"

Copied!
22
0
0

Testo completo

(1)

Corso di Fondamenti di Informatica

La ricorsione

(2)

La ricorsione

■ Si dice che un oggetto (una struttura dati, una funzione matematica, un concetto…) è ricorsivo se è possibile darne una definizione in termini di (varianti di) sè stesso

■ Esempio: la funzione matematica fattoriale – n!=n*(n-1)! Se n≥1

– 0!=1

■ Esempio: la definizione di sequenza

– Una sequenza è la sequenza vuota oppure è un elemento seguito da una sequenza:

– – –

s ≡ x1 U s’

s’ ≡ x2 U s’’

s’’ ≡ x3 U s’’’

. . – sn ≡<>

(3)

La ricorsione

■ Esempio: una filastrocca per bambini …

– C'era una volta un re seduto sul sofà che disse alla sua serva raccontami una storia e la serva incominciò: C'era una volta un re...

■ Esempio: in natura…

(4)

Il Principio dell'induzione

■ Il principio d'induzione è un enunciato sui numeri naturali che in matematica trova un ampio impiego nelle dimostrazioni:

■ In pratica:

– si dimostra che vale P(1) (o P(0)), cioè che la proprietà P vale per 1(o 0).

– si assume come ipotesi che l'asserto P(n) valga per un generico n e da tale assunzione si dimostra che vale anche P(n + 1)

(5)

Il Principio dell'induzione: un Esempio

■ Dimostriamo che vale l'asserto:

Vogliamo quindi dimostrare la seguente proprietà P

Base dell'induzione: l'asserto è vero per n=1:

Passo induttivo:

∀ n∈ℕ 1 2 3 . . 1

2

1 2 3 . . 1

2

(6)

Il Principio dell'induzione: un Esempio

Passo induttivo:

Possiamo facilmente asserire che

Da ciò, in pochi passi, è facile dimostrare l'asserto:

1 ⇒

2 1 1 1 1

2

1 1 2 3 . . 1 1

1 1

2 + 1 1 1 1

2

(7)

La Ricorsione

■ In programmazione la ricorsione è una tecnica potente che:

permette di suddividere un problema da risolvere in sottoproblemi simili a quello originale

■ la potenza della ricorsione nasce dalla possibilità di definire un insieme infinito di oggetti con regola finita

■ …allo stesso modo, un insieme infinito di computazioni può essere descritto con un programma ricorsivo finito.

■ La ricorsione si applica ad algoritmi ricorsivi (i quali a loro volta sono spesso basati su strutture dati ricorsive)

■ Per tali algoritmi la tecnica ricorsiva permette di scrivere programmi eleganti e sintetici, sarà tuttavia opportuno verificarne di volta in volta l'efficienza rispetto ad altri tipi di soluzioni

(8)

Il programma fattoriale

■ La versione ricorsiva del fattoriale è:

• int fatt(int x){

• int f;

• if (x<=1) f=1;

else f=x*fatt(x1);

return f;

• }

Mentre quella iterativa è:

• int fatt(const int x){

• int f=1,i;

for(i=1;i<=x;i++)

• f=f*i;

return f;

• }

passo BASE

Passo INDUTTIVO

(9)

Lo schema degli algoritmi ricorsivi

■ Un algoritmo ricorsivo è sempre codificato per mezzo di un programma che richiama se stesso:

P (T1 x) { ...

if (p(x)) F;

else C(S,P);

R }

int fatt(int x){

int f;

if (x<=1) f=1;

else f=x*fatt(x1);

return f;

}

Il predicato p(x) verifica il

caso BASE

(10)

Terminazione

■ La chiamata ricorsiva deve quindi essere subordinata ad una condizione che ad un certo istante risulti soddisfatta (il predicato p).

■ Il numero di chiamate necessarie viene detto profondità della ricorsione

(11)

Algoritmi ricorsivi

■ Esistono diversi tipi di algoritmi ricorsivi:

– Si parla di mutua ricorsione quando nell'algoritmo una funzione ne richiama un'altra che a sua volta richiama la prima, altrimenti si parla di ricorsione diretta

– Si parla di ricorsionelineare quando vi è solo una chiamata ricorsiva all'interno della funzione, e di ricorsione non lineare nel caso in cui le chiamate ricorsive siano più di una.

– La distinzione più importante ai fini pratici si ha fra ricorsione di coda (tail recursion) e ricorsione non di coda. Si parla di ricorsione di coda quando la chiamata ricorsiva è l'ultima istruzione eseguita nella funzione. Questo tipo di algoritmo ricorsivo è possibile trasformarlo semplicemente in una versione iterativa, che di solito è più efficiente,

(12)

Meccanismo interno di ricorsione

■ Un sottoprogramma ricorsivo è dunque

direttamente o indirettamente se stesso un sottoprogramma che richiama

■ Non tutti i linguaggi realizzano il meccanismo

realizzano possono utilizzare due tecniche: della ricorsione. Quelli che lo – gestione LIFO di più copie della stessa funzione, ciascuna con il proprio

insieme di variabili locali

– Gestione mediante record di attivazione; un’ unica copia del codice del sottoprogramma ma ad ogni chiamata è associato un record di attivazione (variabili locali e punto di ritorno). In questo caso diversi record di attivazione dello stesso sottoprogramma possono essere contemporaneamente presenti nello stack

(13)

Schema ricorsivo

m ain ...

...

..fatt(3)

fat t (2) if (x< = 1)

f= 1;

else

f= x* fat t (x-1);

ret urn f;

fat t (1) if (x< = 1)

f= 1;

else

f= x* fat t(x-1) ; ret urn f;

2 1 6

Fase ascendente Fase discenden te

fat t(3) if (x< = 1)

f= 1;

else

f= x* fat t (x-1) ;

ret urn f;

(14)

Dalla forma ricorsiva a quella iterativa

Un problema ricorsivo può sempre essere risolto in termini iterativi (il viceversa non è vero).

Nel caso in cui la ricorsione è in coda, la trasformazione è molto semplice. Ad esempio:

while !p(x) S;

F

;

Quando invece la ricorsione non è l'ultima istruzione della procedura si può optare per una soluzione che preveda l'utilizzo di uno stack di supporto che permette di salvare i valori delle chiamate ricorsive e tramite un ciclo while simulare ciò che fa lo stack di sistema.

P (T1 x) { ...

if p(x) F;

else {S;P(x);}

return }

(15)

Dalla forma ricorsiva a quella iterativa: il fattoriale

i n t f a t t ( c o n s t i n t f ;

i f ( x < = 1 ) f = 1 ;

e l s e f = x * f a t t ( x - 1 ) ; r e t u r n f ;

}

i n t x ) { i n t f a t t ( c o n s t i n t x ) { i nt f =1, i ;

f or ( i =1; i < =x ; i ++) f = f * i ;

r e t u r n f ; }

Forma ricorsiva Forma iterativa

(16)

Alcuni esempi

■ Esponenziale

■ Calcolo lunghezza di un stringa

■ Ricerca del minimo.

■ Ricerca di un valore

■ Stringa palindrome

■ Inversione di un stringa

(17)

Esponenziale

float exp( float x, int n) {

if (n == 1) return x;

else return x*exp(n1);

}

ricorsione in coda

NOTA

Questa versione tiene conto solo di esponenti positivi. Provare a scrivere la

stessa funzione (ricorsiva) tenendo conto anche del caso n < 0

(18)

Calcolo Lunghezza di una Stringa

ricorsione in coda void strlen( char *str)

{

if (str[0] == '\0') return 0;

else return 1 + strlen(&str[1]);

}

Posso anche scrivere:

*str == '\0'

(19)

Funzione di Ricerca del Minimo ricorsiva

int ric_min( int n, int v[]) {

int m;

if (n==1)

return v[0];

m = ric_min(n1, &v[1]);

if (m < v[0]) return m;

else return v[0];

(20)

Funzione di Ricerca di valore ricorsiva

bool check_val( int n, int v[], int {

if (v[0] == val) return true;

else

if (n > 1)

val)

return check_val(n1, v+1, val);

}

Uso dell'aritmetica dei puntatori

(21)

Palindrome

■ Stringa palindrome:

– uguale se letta nei due versi: anna, radar, asorrosa, abbbcbbba

■ Una stringa è palindrome se:

– ha un solo carattere, oppure

– è composta da un primo ed ultimo carattere uguali che racchiudono una stringa ancora palindrome

(22)

Palindrome

bool palindrome(char str[], int first, int last) {

if (str[last] == str[first]){

if ((last – first) <= 1) return true;

else return palindrome(str, first+1, } else return false;

last1);

}

NOTAProvare a scrivere la versione iterativa

Riferimenti

Documenti correlati

Si scriva per ognuna di tali funzioni un programma in C che verifichi il corretto funzionamento di tale funzione, Si leggano dall’input i valori dell’array ed eventuali altri

Da allora, i monaci del tempio si alternano per spostare i dischi dal piolo originario ad un altro piolo, seguendo le rigide regole di Brahma: i dischi vanno ma- neggiati uno

• anche in questo caso nel corpo della funzione si attiva una sola chiamata ricorsiva, dato che le due chiamate sono alternative l’una all’altra; quindi la successione delle

Analogamente, una funzione (o una procedura) viene detta ricorsiva quando all’interno del corpo della funzione vi è una chiamata alla funzione (o procedura)

 Nei casi in cui la ricorsione deve continuare, si deve inserire la chiamata alla stessa funzione con gli opportuni parametri attuali..  Attenzione: se la funzione torna un valore

• Scrivere una funzione ricorsiva (in C) che, avendo in input un array di n interi di interi positivi, dia in output TRUE se tutti gli elementi sono maggiori di 10, FALSE altrimenti..

/* calcola la somma di tutti gli elementi della sequenza, usando il metodo sommaRic */. public int

  Individuare, in un gruppo di palline l’unica pallina di peso maggiore delle altre facendo uso di una bilancia “a basculla” (Per semplicità: il numero di palline sia una