Ricerca di Massimo e Minimo di un Array Problema. Trova l’elemento di valore massimo e quello di valore minimo in un array S[1 . . . n]. Quanti confronti tra elementi di S occorrono? Algoritmo semplice. max = S[1] for i = 2 to n do if S[i] > max then max = S[i] min = S[1] for i = 2 to n do if S[i] < min then min = S[i] (# di confronti tra elementi di S) = 2(n − 1) (si puó migliorare a 2n − 3) Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 1/18 Uso di Divide et Impera nel calcolo del max e min 1. Se l’array ha ≤ 2 elementi, risolvi il problema direttamente, altrimenti dividi l’array in metá 2. Trova il massimo ed il minimo in ciascuna metá ricorsivamente 3. Ritorna il massimo dei due massimi ed il minimo dei due minimi trovati al passo 2. M AX M IN(x, y) % ritorna il max e min in S[x . . . y] if y − x ≤ 1 then return(max(S[x], S[y]), min(S[x], S[y])) else (max 1, min 1)=(M AX M IN(x, ⌊(x + y)/2⌋) (max 2, min 2)=(M AX M IN((x + y)/2⌋ + 1, y ) return(max(max 1, max 2), min(min 1, min 2)) Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 2/18 Analisi di MaxMin Assumiamo che n sia potenza di 2 e sia T (n) il numero di confronti effuato da MaxMin MaxMin chiama se stessa 2 volte su array di taglia n/2, eseguendo quindi 2T (n/2) confronti MaxMin effettua un altro confronto per calcolare il max a partire da (max 1, max 2) ed un altro per calcolare il min a partire da (min 1, min 2). In totale T (n) = 1 se n =2 2T (n/2) + 2 altrimenti Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 3/18 T (n) =? (ricordando che n = 2i ) T (n) = 2T (n/2) + 2 = 2(2T (n/4) + 2) + 2 = 4T (n/4) + 4 + 2 = 8T (n/8) + 8 + 4 + 2 = 2i−1 T (n/2i−1 ) + i−1 X 2j j=1 log n−1 = 2log n−1 T (2) + X 2j j=1 = n/2 + (2log n − 2) = n/2 + n − 2 = 1.5n − 2 Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 4/18 Nota Sia nel caso del M ERGE S ORT che dell’algoritmo M AX M IN abbiamo diviso il problema originale in due sottoproblemi di taglia uguale. É stato un caso? NO. In generale, la tecnica Divide et Impera produce algoritmi efficienti quando riusciamo a dividere il problema originale in sottoproblemi di taglia piú o meno simile, bilanciando quindi il lavoro da fare tra le varie chiamate ricorsive dell’algoritmo sui rispettivi sottoproblemi. Inoltre, in generale, nella tecnica D&I si ricorre su sottoproblemi di taglia sempre minore, fin quando essi non abbiano una taglia talmente piccola da essere risolti direttamente, ovvero attraverso una qualche semplice procedura non ricorsiva. Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 5/18 Applicazione di D&I al Calcolo di Potenze Problema: Dato un numero a ed un intero positivo n, calcolare an usando il minor numero di moltiplicazioni. Algoritmo semplice S LOW P OWER(a, n) x=a for i = 2 to n do x=x·a return x (# di moltiplicazioni effettuate da S LOW P OWER(a, n))=n − 1 Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 6/18 Applicazione di D&I al Calcolo di Potenze Per applicare D&I osserviamo che an = a⌊n/2⌋ · a⌈n/2⌉ Inoltre, a⌈n/2⌉ = a⌊n/2⌋ se n é pari, e a⌈n/2⌉ = a · a⌊n/2⌋ se n é dispari. Algoritmo veloce FAST P OWER(a, n) if n = 1 return a else x = FAST P OWER(a, ⌊n/2⌋) if n é pari return x · x if n é dispari return x · x · a Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 7/18 Complessitá di FAST P OWER(a, n) FAST P OWER(a, n) if n = 1 return a else x = FAST P OWER(a, ⌊n/2⌋) if n é pari return x · x if n é dispari return x · x · a T (1) = 0. In generale T (n) ≤ T (n/2) +2. Assumendo n = 2k , abbiamo T (n) ≤ T (n/2) + 2 ≤ T (n/4) + 2 + 2 = T (n/4) + 4 ≤ . . . ≤ T (n/2k ) + 2k = 2 log n Quindi con D&I possiamo calcolare an usando O(log n) moltiplicazioni, contro le n − 1 dell’algoritmo semplice. Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 8/18 Applicazione di D&I alla Moltiplicazione di Interi Come si moltiplicano due numeri? abbiamo appreso che si fà così: In seconda elementare 27× 32 = 54 81 864 Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 9/18 e vale anche per numeri in binario! Qual è la complessità dell’algoritmo che 011011× moltiplica, nel modo “elementare”, due 110110 = numeri di n bits ciascuno? 000000 Vi sono n moltiplicazioni parziali, in ciascuna delle quali si moltiplicano n bits, in 011011 2 totale O(n ) moltiplicazioni di bit 011011 Contando anche le addizioni, altre O(n2 ), otteniamo che l’algoritmo elementare effettua O(n2 ) operazioni per moltiplicare due numeri di n bit 000000 011011 011011 10110110010 Possiamo fare meglio? Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 10/18 Applicazione di D&I alla Moltiplicazione di Numeri Ricordiamo il paradigma centrale della tecnica D&I: “dividi il problema in sottoproblemi, risolvi i sottoproblemi, e combina le relative soluzioni in una soluzione per il problema globale”. In ossequio a tale paradigma, dato un numero di n bit x iniziamo a dividerlo in bit di “ordine alto” e bit di “ordine basso”, ovvero x = x1 · 2n/2 + x0 . Esempio: x = 11001110 | {z } 00111001 | {z } = |1100111000000000 {z } + |0000000000111001 {z } x1 x0 x1 ·28 x0 Facciamo lo stesso con l’altro numero y da moltiplicare, scrivendo y = y1 · 2n/2 + y0 Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 11/18 avendo scritto x = x1 · 2n/2 + x0 e y = y1 · 2n/2 + y0 otteniamo x · y = (x1 · 2n/2 + x0 ) · (y1 · 2n/2 + y0 ) = x1 · y1 · 2n +(x1 · y0 + x0 · y1 ) · 2n/2 +x0 · y0 (1) L’ uguaglianza (1) riduce il problema di calcolare un singolo prodotto x · y di due numeri di n bit ciascuno, nel problema di calcolare quattro prodotti (x1 · y1 , x1 · y0 , x0 · y1 , x0 · y0 ) di numeri di n/2 bit ciascuno. Abbiamo quindi un abbozzo di algoritmo D&I: “calcola ricorsivamente ciascuno dei 4 sottoproblemi, di dimensione n/2 ciascuno, e combina i risultati ottenuti usando l’equazione (1)”. Domanda: usando la tecnica D&I, di quanto abbiamo “stracciato” l’agoritmo appreso in seconda elementare? Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 12/18 Vediamo, Dobbiamo calcolare... x · y = x1 · y1 · 2n +(x1 · y0 + x0 · y1 ) · 2n/2 +x0 · y0 Detto T (n) il numero di operazioni che il nostro algoritmo impiega per calcolare il prodotto di due numeri di n bit, avremo che T (n) = 4T (n/2) + dn (ricordiamo: abbiamo 4 sottoproblemi di taglia n/2 ciascuno) Questa è una equazione del tipo T (n) = aT (n/c) + dn, con a = 4 > 2 = c, che risolta con i metodi visti nella lezione scorsa dà... T (n) = O(nlogc a ) = O(n2 )!!! cioè la stessa complessità dell’algoritmo elementare. Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 13/18 Dove abbiamo fallito? Abbiamo tentato di risolvere un problema di taglia n mediante 4 chiamate ricorsive a risoluzioni di problemi di taglia n/2 Possiamo diminuire il numero di chiamate ricorsive per calcolare x · y = x1 · y1 · 2n +(x1 · y0 + x0 · y1 ) · 2n/2 +x0 · y0 ? Vediamo... Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 14/18 Occorre calcolare x · y = x1 · y1 · 2n +(x1 · y0 + x0 · y1 ) · 2n/2 +x0 · y0 x1 y1 + x1 y0 + x0 y1 + x0 y0 = (x1 + x0 ) · (y1 + y0 ) che ci possiamo calcolare con una singola chiamata ricorsiva al prodotto del numero (x1 + x0 ) per (y1 + y0 ). Partiamo da Se ora ci calcoliamo x1 y1 (con una chiamata ricorsiva) e x0 y0 (con un’altra chiamata ricorsiva), ci possiamo calcolare x1 y0 + x0 y1 = (x1 + x0 ) · (y1 + y0 ) − x1 y1 − x0 y0 semplicemente sottraendo x1 y1 e x0 y0 da (x1 + x0 ) · (y1 + y0 ). Quindi, con solo 3 chiamate ricorsive ci siamo calcolati tutti i termini che compaiono nel prodotto x · y = x1 · y1 · 2n +(x1 · y0 + x0 · y1 ) · 2n/2 +x0 · y0 e possiamo calcolarci tranquillamente x · y Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 15/18 Algoritmo D&I per il calcolo di prodotti: Recursive-Multiply(x, y) Scrivi x = x1 · 2n/2 + x0 y = y1 · 2n/2 + y0 Calcola x1 + x0 e y1 + y0 p = Recursive-Multiply(x1 + x0 , y1 + y0 ) x1 y1 = Recursive-Multiply(x1 , y1 ) x0 y0 = Recursive-Multiply(x0 , y0 ) return x1 y1 · 2n + (p − x1 y1 − x0 y0 ) · 2n/2 + x0 y0 Detta T (n) la complessità di Recursive-Multiply(x, y) per numeri x e y n bits, avremo T (n) = 3T (n/2) + dn Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 16/18 Risolviamo T (n) = 3T (n/2) + dn Ricordiamo che equazioni del tipo T (n) = aT (n/c) + dn, con a > c hanno come soluzione T (n) = O(nlogc a ), il che, nel nostro caso ci dice T (n) = O(nlogc a ) = O(nlog2 3 ) = O(n1.59 ) migliorando, finalmente, un algoritmo da seconda elementare... Per avere una idea del miglioramento, osserviamo che n2 ≈ 17 × nlog2 3 per n = 1000, n2 ≈ 309 × nlog2 3 per n = 1000000, e n2 ≈ 5436 × nlog2 3 per n = 1000000000. Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 17/18 più seriamente... Idee simili possono essere usate per ottenere algoritmi efficienti per calcolare (rapidamente) la convoluzione f ∗ g di due funzioni f e g, P∞ definita come (f ∗ g)[n] = m=−∞ f [m]g[n − m]. Tale operazione é cruciale in molti campi dell’Analisi dei Segnali ed ha applicazioni pratiche importantissime (TAC, riconoscimento vocale, compressione di immagini JPEG, riconoscimento automatico di immagini, trattamento di segnali musicali, astronomia, ...) Un algoritmo per il calcolo della convoluzione funzioni basato sulle idee che abbiamo presentato ha rivoluzionato il campo dell’Analisi dei Segnali. Tale algoritmo, chiamato Fast Fourier Transform (FFT), fu scoperto da Cooley e Tukey nel 1965 (sebbene Gauss nel 1805 ne avesse pensato uno simile). L’algoritmo FFT é entrato nella lista “Top Ten Algorithms of the Century”, compilata dalla rivista Computing in Science and Engineering nel Gennaio 2000. Universitá degli Studi di Salerno – Corso di Introduzione agli Algoritmi e Strutture Dati – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 18/18