ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 ·...

21
Programmazione ricorsione e divide et impera Alessandra Raffaetà Università Ca’ Foscari Venezia Corso di Laurea in Informatica

Transcript of ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 ·...

Page 1: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Programmazionericorsione e divide et impera

Alessandra Raffaetà

Università Ca’ Foscari VeneziaCorso di Laurea in Informatica

Page 2: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Ricorsione

La ricorsione è un modo classico di affrontare un problema riducendolo a un insieme di sottoproblemi dello stesso tipo, ma più semplicida risolvere. In generale una procedura ricorsiva ha la forma:

void foo (tipoParametro p) {if (p == casoBase1)

comandoBase1else

……foo(p’) /* p’ è un’espressione di tipo

tipoParametro tale che p’ < p */……

}

Page 3: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Una procedura ricorsiva …

Chiama se stessa ricorsivamente.

Ha una o più condizioni di terminazione.

Ad ogni chiamata ci si avvicina alla condizione di terminazione.

Page 4: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Esercizio

Problema: Trovare il massimo elemento di un array non vuoto.

TipoElem massimo (TipoElem v[ ], int dim) {TipoElem max;if (dim == 1)

return v[0];

max = massimo(v, dim - 1);if (maggiore(max, v[dim - 1]))

return max; return v[dim - 1];

}Chiamata della funzione: ris = massimo(v, dim);

Page 5: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Seconda soluzione

TipoElem massimotl (TipoElem v[ ], int dim, TipoElem max) {if (dim == 0)

return max;

if (maggiore(v[dim - 1], max))max = v[dim - 1];

return massimotl(v, dim – 1, max);}

Chiamata della funzione: ris = massimotl(v, dim – 1, v[dim - 1]);

Page 6: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Ricerca lineare ricorsiva

int riclineareRic (TipoElem v[ ], int dim, TipoElem elem) { if (dim == 0) /*il vettore è vuoto */

return -1;

if (equal(elem, v[dim - 1]))return dim - 1; /*l’elemento è stato trovato */

return riclineareRic(v, dim – 1, elem);}

Restituisce lo stesso risultato della ricerca lineareiterativa?

Page 7: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Ricerca binariaAssunzione: l’array v è ordinato.

Ricerca binaria: Si confronta l’elemento x dacercare con l’elemento m in posizione centraledell’array v. □ Se x è uguale a m ho finito.□ Se x è minore di m, la ricerca prosegue nella metà

inferiore dell’array.□ Se x è maggiore di m, la ricerca prosegue nella metà

superiore dell’array.

Il processo prosegue fino al reperimento di x oppure fino all’esaurimento dell’array nel qualcaso x non è presente in v.

Page 8: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Algoritmo per la ricerca binaria

int ricercabinaria (TipoElem v[ ], TipoElem elem, int inf, int sup) { int med, indice;if (inf > sup) /*la parte del vettore fra inf e sup è vuota */

indice = -1;else {

med = (inf + sup)/2;if (equal(elem, v[med]))

indice = med; /*l’elemento è stato trovato */else

if (minore(elem, v[med])) /*cerca nella parte inferiore*/indice = ricercabinaria(v, elem, inf, med -1);

else /*cerca nella parte superiore*/ indice = ricercabinaria(v, elem, med+1, sup);

}return indice;

}Chiamata della funzione: risultato = ricercabinaria(v, elem, 0, dim–1);

Page 9: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Esempio di ricerca binaria

60555133257

0 1 2 3 4 5

3

med = 2

605551

3 4 5

51

60555133257

0 1 2 3 4 5

v

Dato il vettore v ordinato, determinare l’indice di 51 se occorre in v.

med = 4

med = 3

L’elemento è stato trovato e la sua posizione in v è 3.

Page 10: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Tecnica di progetto di algoritmi: divide et impera

Soluzione ricorsiva di un problema articolata in 3 passi:Divide: divisione del problema originale in sottoproblemianaloghi a quello originale, ma di dimensione inferiore.

Impera: soluzione ricorsiva dei sottoproblemi; se la dimensione dei sottoproblemi è sufficientemente piccola, questi possono essere risolti direttamente.

Combina: composizione delle soluzioni dei sottoproblemiper ottenere una soluzione del problema originale.

Page 11: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Esempio di tecnica divide et imperaProblema: Trovare il massimo elemento di un array non vuoto.

Divide: divide l’array A[inf..sup] in due sottoarray con metà elementi, A[inf..med] e A[med+1..sup] dove med = (inf+sup)/2.Impera: trova i max applicando la funzionericorsivamente ai sottoarray se il sottoarray ha almeno due elementi, altrimenti il max èl’elemento stesso.Combina: confronta i due max trovati di A[inf..med] e A[med+1..sup] e restituisce il max dei due al fine di ottenere il max di A[inf..sup].

Page 12: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Funzione massimo – divide et imperaTipoElem massimo (TipoElem v[ ], int inf, int sup) {/*Trova il massimo degli elementi del vettore v di indice compreso tra inf e sup */

int med;TipoElem m1, m2;if (inf == sup) /* impera: risolve direttamente */

return v[inf];

med = (inf + sup)/2; /* divide: il problema in 2 sottopb */m1 = massimo(v, inf, med); /*impera: trova max parte inferiore*/m2 = massimo(v, med + 1, sup); /*impera:trova max parte superiore*/

if (m1 > m2) /*combina: trova max del vettore*/return m1;

return m2;}

Page 13: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

EsercizioDato un vettore v contenente (n > 0) interi positivi, sideve stabilire se la somma di tutti gli elementi di v siapari o dispari senza eseguire addizioni.

int sommaPari (int v[ ], int inf, int sup) {

int med, ris1, ris2;if (inf == sup)

return (v[inf] % 2 == 0);

med = (inf + sup)/2; /* divide: il problema in 2 sottopb */ris1 = sommaPari(v, inf, med); /*impera: trova pari parte inf*/ris2 = sommaPari(v, med + 1, sup); /*impera:trova pari parte sup*/

return (ris1 == ris2); /*combina: trova max del vettore*/}

Page 14: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Algoritmo di ordinamento: mergesort

Algoritmo di tipo divide et impera.

Divide: divide l’array v[p..u] in due sottoarray con metà elementi, v[p..med] e v[med+1..u] dove med = (p+u)/2.

Impera: ordina ricorsivamente i sottoarrayusando il mergesort se il sottoarray ha almenodue elementi, altrimenti il sottoarray è ordinato.

Combina: fondi insieme i due sottoarray ordinativ[p..med] e v[med+1..u] al fine di ottenere un array ordinato v[p..u].

Page 15: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

La procedura mergesort

void mergesort (TipoElem v[ ], int iniziale, int finale) {/*Ordina gli elementi del vettore v di indice compreso tra iniziale e

finale */ int med;if (iniziale < finale) {

med = (iniziale + finale)/2;mergesort(v, iniziale, med);mergesort(v, med + 1, finale);merge(v, iniziale, med, finale);

}}

Page 16: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Esempio: le chiamate ricorsive di mergesort

0 1 2 3 4 5

94752381

6 7

0 1 2 3

2381

4 5

9475

6 7

0 1

81

2 3

23

4 5

75 94

6 7

0

1

1

8

2

3

3

2

4

5

5

7

6

4

7

9

Page 17: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

La procedura mergeIdea: Da due mazzetti ordinati di carte vogliamo farneun terzo mettendo di volta in volta la carta più piccolatra le due in cima ai due mazzetti. Questo si ripete finoa quando uno dei due mazzetti finisce. Le carte restantisono aggiunte in fondo.

void merge (TipoElem v [ ], int iniziale, int med, int finale) {/*Fonde i due sottoarray ordinati di v da iniziale a med e da med+1 a

finale in un unico sottoarray ordinato da iniziale a finale */

TipoElem buf [DIM]; /* vettore di appoggio */ int primo,secondo, appoggio,i;primo = iniziale;secondo = med +1;appoggio = iniziale;

Page 18: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

La procedura merge (cont.)

while (primo <= med && secondo <= finale) {if (minoreUguale(v [primo], v [secondo])) {

buf [appoggio] = v [primo];primo ++;

}else {

buf [appoggio] = v [secondo];secondo ++;

}appoggio++;

}

Page 19: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

La procedura merge (cont.)

if (secondo > finale) /* è finito prima il secondo sottoarray; copia da v in v stesso tutti gli elementifino a med, cominciando dal fondo */

for (i= med; i>=primo; i--) { v[finale] = v[i];finale --;

}/* copia tutti gli elementi da iniziale a appoggio – 1 da buf a v */

for (i = iniziale; i < appoggio; i++)v[i] = buf[i];

}

Page 20: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Come funziona la procedura merge

Primo ciclo: Finché le due sottosequenze sono non vuote preleva l’elemento più piccolo in cima allesottosequenze.Secondo ciclo: Disponi in posizione corretta glielementi rimasti nella prima sottosequenza se non vuota.□ Osservazione: se la sottosequenza vuota è la prima non

occorre fare alcuna operazione poiché gli elementisono già in posizione corretta in base all’ordinamento.

Terzo ciclo: Trasferisci gli elementi dal vettoredi appoggio nella posizione corrispondente nelvettore di partenza.

Page 21: ricorsione e divide et impera Alessandra Raffaetàprogram/lezioni/lezione7... · 2010-06-01 · Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray

Esempio di calcolo della procedura merge

0 1 2 3 4 5

98754321

6 7

0 1 2 3

83214 5

97546 7

0 1

81

2 3

32

4 5

75 94

6 7

0

1

1

8

2

3

3

2

4

5

5

7

6

4

7

9