Lezione n°5
Algoritmi Avanzati
a.a.2014/2015
Prof.ssa Rossella Petreschi
Circuiti di ordinamento
Utilizziamo il comparatore, ossia un circuito di confronto con due ingressi e due
uscite il cui valore è la coppia di valori in input ordinati in modo ascendente.
x
min(x,y)
y
max(x,y)
I circuiti formati da comparatori, se opportunamente combinati, costituiscono
un'architettura in grado di ordinare n valori di input.
9
5
2
2
5
9
7
5
5
2
2
5
7
7
7
7
9
9
Insertion Sort
Due architetture per implementare l'insertion sort (tra 8 valori in input):
Questa macchina utilizza una
versione seriale dell'algoritmo di
complessità O(n2) (indicata dalla
profondità del circuito, ossia il
numero di porte che vengono
attraversate in tempi differenti).
È
possibile
ottenere
una
complessità O(n) ottimizzando lo
scheduling dei comparatori e
parallelizzando l'algoritmo.
Sulla monotonicità
Se un circuito di ordinamento trasforma la sequenza di input a = (a1, a2,
…, an) nella sequenza di output b = (b1, b2, …, bn), allora per ogni
funzione monotona crescente f, il circuito trasforma la sequenza di input
f(a) = (f(a1), f(a2), …, f(an)) nella sequenza di output f(b)=(f(b1), f(b2), …,
f(bn)).
Tale proprietà è facilmente verificata da un singolo comparatore e per
induzione la si può provare per un intero circuito di ordinamento.
x
min(x,y)
f(x)
min(f(x), f(y))) = f(min(x,y))
y
max(x,y)
f(y)
max(f(x), f(y))) = f(max(x,y))
Principio 0/1
Teorema (principio 0/1): se un circuito combinatorio di ordinamento lavora
correttamente per qualunque input costruito sull'alfabeto {0,1} allora lavora
correttamente per qualunque input costruito su di un qualsiasi alfabeto finito A.
Dim: supponiamo per assurdo che il circuito ordini tutte le sequenze costruite
sull'alfabeto {0,1} correttamente, ma che esista una sequenza di input di
numeri arbitrari a = (a1, a2, …, an) contenente elementi ai e aj tali che ai < aj
mentre il circuito pone aj prima di ai nella sequenza di output. Definiamo una
funzione f monotona crescente come:
•f(x) = 0 se x <= ai
•f(x) = 1 se x > ai
dal lemma precedente segue che il circuito sistema f(aj) prima di f(ai) nella
sequenza di output quando f(a) è l'input. Ma poiché f(aj) = 1 mentre f(ai) = 0,
neghiamo l'ipotesi giungendo ad un assurdo.
Sequenze bitoniche
Una Sequenza Bitonica è una sequenza che può essere divisa in due sottosequenze
monotone, una crescente e l'altra decrescente o viceversa.
Sono bitoniche le due sequenze:
•m(S) = (min{s1,sn+1}, min{s2,sn+2}, …, min{sn,s2n})
•M(S) = (max{s1,sn+1}, max{s2,sn+2}, …, max{sn,s2n})
ottenute dalla sequenza bitonica S = s1, s2, …, s2n
Sfruttando la definizione di m(S) ed M(S) e le relative proprietà si può ottenere una
definizione ricorsiva per le sequenze bitoniche che ci permette di realizzare un primo
algoritmo di ordinamento che opera ricorsivamente secondo lo schema:
S=13896554
S1 = m(S) = 1 3 5 4
S2 = M(S) = 6 5 8 9
S3 = m(S1) = 1 3 S4 = M(S1) = 5 4
S5 = m(S2) = 6 5 S6 = M(S2) = 8 9
13
45
56
89
Complessità O(log(n)).
Sequenze pulite
Una sequenza binaria si dice pulita se è composta interamente da 0 o da 1.
Se S è bitonica almeno una delle due sottosequenze bitoniche m(S) e M(S) è pulita
(diretta conseguenza del fatto che la cardinalità di {0,1} è due).
In figura è presentato un circuito di ordinamento, di profondità logaritmica, per
sequenze 0/1 bitoniche (ad ogni passo rendiamo pulita metà della sequenza).
0
0
1
1
1
0
0
0
0
0
0
0
1
0
1
1
0
0
0
0
1
0
1
1
0
0
0
0
0
1
1
1
Circuito di fusione
Il circuito di fusione fonde due sequenze ordinate costruite sullo stesso alfabeto
sfruttando il fatto che, date due sequenze ordinate entrambe crescenti (o decrescenti) x
e y, la sequenza che si ottiene concatenando x con z=“y rovesciata” è bitonica
(l’inversione della stringa y si realizza semplicemente variando le connessioni).
0
0
0
1
0
0
1
1
0
0
0
0
1
0
1
1
0
0
0
0
1
0
1
1
0
0
0
0
0
1
1
1
La profondità del circuito è logaritmica e il numero di comparatori ad ogni passo è n/2.
Circuito di ordinamento
In figura è riportato un circuito di ordinamento che realizza l’ordinamento connettendo
iterativamente diversi circuiti di fusione (la base di questa costruzione sta nel fatto che
ogni sequenza di due elementi è bitonica).
1
0
0
0
0
1
1
0
0
1
0
0
0
1
0
1
0
0
1
0
0
0
1
1
0
0
0
1
0
0
1
1
0
0
0
0
1
0
1
1
0
0
0
0
1
0
1
1
0
0
0
0
0
1
1
1
Poiché concateniamo un numero logaritmico di circuiti di fusione, otteniamo una
profondità del circuito O(log2 n).
Applicando il teorema di Brent
Il circuito di ordinamento, O, è un circuito combinatorico di
profondità d = O(log2 n), di dimensione pari al numero di
comparatori c = O(nlog2n), fan in e fan out limitati. Per il
Teorema di Brent, l’algoritmo di ordinamento che lavora su O
può essere simulato da una algoritmo che lavora su una PRAM
EREW con p processori in tempo O(c / p + d),
ovvero O((n log2 n) / p + log2 n).
Quando p = O(n) si ha che la complessità temporale
dell'ordinamento su una PRAM EREW è
O(log2 n) e il costo O(n log2 n).
Trasportabilità fra P-RAM con diverso numero
di processori
Teorema (p' < p): ogni algoritmo A che lavora in tempo parallelo O(t) su
una PRAM con p processori può essere simulato da un algoritmo A' che
lavora su una PRAM con p' processori (p' < p) in tempo O(t p / p').
Dimostrazione: durante ognuno dei t passi dell’esecuzione di A, i p
processori lavorano parallelamente in tempo O(1). Durante ogni passo
della esecuzione di A', ciascuno dei p'<p processori eseguirà un blocco
seriale di p/p' operazioni in tempo O(p/p'). Pertanto il tempo parallelo
relativo alla esecuzione di A' sarà O(tp/p').
Il costo dei due algoritmi A e A' rimane pari a O(tp).
Numero di processori limitato (1)
Vogliamo sommare n numeri, con la tecnica della prima metà, avendo a disposizione un
numero fissato a priori di p processori (p < n).
Ricordiamo come si lavora con n processori
Begin
for i = 1 to log n do
for j = 0 to n/2i -1 pardo
Pj: A[ j ] = A[ j ] + A[ j+n/2i ]
return A[ 0 ]
end
P1 P 2 P3 P4
4 2 7 4 1 6 3 8
5 8 10 12
15 20
35
Numero di processori limitato (2)
p = 2 P1 P2
Vediamo cosa cambia con p <n processori
Begin
for i = 1 to log n do
for s = 0 to (n/2i) / p -1 do
for j = 0 to min(n/2i, p) -1 pardo
if (j+s*p < n/2i) then
Pj: A[ j+s*p ] = A[ j+s*p ] + A[ j+s*p +n/2i ]
return A[ 0 ]
end
i=1
4 2 7 4 1 6 3 8
s=0
5 8 7 4 1 6 3 8
s=1
i=2 5 8 10 12
s=0
i=3 15 20
s=0
35