Sommario della lezione:
Metodologie per il progetto di algoritmi:
La Tecnica Divide et Impera
Esempi di applicazione a:
Ricerca del massimo e minimo di una sequenza di numeri
Calcolo di potenze di numeri
Moltiplicazione di interi
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 1/25
Paradigma generale di algoritmi basati su D&I
Algoritmo D&I(x)
x é sufficientemente piccola o semplice then
return adhoc(x) (% ovvero risolvi x direttamente)
else decomponi x in ℓ istanze piú piccole x1 , x2 , . . . , xℓ
for i = 1 to ℓ do
yi ← D&I(xi ) (% ovvero risolvi ricorsivamente ciascuna xi )
componi le sottosoluzioni yi alle istanze xi per ottenere
una soluzione globale y alla istanza completa x
if
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 2/25
Primo esempio: Ricerca binaria
Problema: Dato un array ordinato A[1 . . . n], ed un elemento x
in A, trovare l’indice i per cui A[i] = x
%(trova x in A[ℓ . . . r])
if ℓ = r then return(ℓ)
% istanza “piccola”
S EARCH(A, x, ℓ, r)
else
m = ⌊(ℓ + r)/2⌋ % inizio fase “decomposizione”
if x ≤ A[m]
then return(S EARCH(A, x, ℓ, m))
% inizio “conquer”
else return(S EARCH(A, x, m + 1, r))
Posto T (n) il numero di confronti nel caso peggiore effettuati
da S EARCH(A, x, 1, n) abbiamo che T (n) = 0 se n = 1 e
T (n) = T (n/2) + 1 altrimenti, ⇒ T (n) = Θ(log n)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 3/25
Secondo esempio: M ERGE S ORT
Problema: Dato un array A[1 . . . n], ordinare i suoi elementi.
Adottando la tecnica Divide et Impera procederemo nel modo
seguente:
M ERGE S ORT(A)
1. Se n = 1, restituisci A.
2. Altrimenti, dividi A in due metá uguali X e Y .
3. Ordina X e Y , chiamando ricorsivamente M ERGE S ORT(X) e
M ERGE S ORT(Y )
4. Combina i due array ottenuti al passo 3. in un unico array
ordinato e restituiscilo
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 4/25
Esempio di esecuzione di MergeSort
83297154
8329
83
8
7154
29
3
9
2
38
54
71
7
29
1
5
45
17
2389
4
1457
12345789
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 5/25
Analisi di M ERGE S ORT
M ERGE S ORT(A[1 . . . n])
if
n = 1 then return A else
A1 ← M ERGE S ORT(A[1 . . . ⌊n/2⌋])
A2 ← M ERGE S ORT(A[⌊n/2⌋ + 1 . . . n])
return M ERGE (A1 , A2 )
Analisi:
T (1) = Θ(1)
T (n) = 2 · T (n/2)+Θ(n)
da cui T (n) = Θ(n log n)
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 6/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 7/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 8/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 9/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 10/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 11/25
Ricordiamo il paradigma generale di algoritmi basati su D&I
AlgoritmoD&I(x)
x é sufficientemente piccolo o semplice then
return adhoc(x)
else decomponi x in istanze piú piccole x1 , x2 , . . . , xℓ
for i = 1 to ℓ do yi ← D&I(xi )
componi le yi per ottenere una soluzione y ad x
return y
if
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 12/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 13/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 14/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 15/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 16/25
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
000000
011011
011011
Contando anche le addizioni, altre
O(n2 ), otteniamo che l’algoritmo elementare effettua O(n2 ) operazioni per
moltiplicare due numeri di n bit
10110110010
Possiamo fare meglio?
Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 17/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 18/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 19/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 20/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 21/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 22/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 23/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 24/25
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 Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2014/15 – p. 25/25