Lezione n°5 Algoritmi Avanzati a.a.2015/2016 Prof.ssa Rossella Petreschi 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). Ricordiamo che: 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 La tecnica del Accelerated Cascading Dato un problema P di dimensione n, siano A1 e A2 due algoritmi per risolvere il problema P che operano rispettivamente in tempo T1e T2 (T1> T2), si costruisce un nuovo algoritmo A nel seguente modo: 1. si applica a P l’algoritmo A1(A1lavora per fasi) fintanto che l’output delle fasi non produca una istanza P’ di P di dimensione minore di una soglia prefissata; 2. si applica a P’ l’algoritmo A2 E’ da notare che invece di un solo algoritmo di riduzione A1 si potrebbe avere una catena di algoritmi di questo tipo. In tal caso si considera la catena di algoritmi di riduzione ordinata dall’algoritmo più lento al più veloce e si costruisce il nuovo algoritmo sostituendo il passo1 con l’applicazione a P della catena così ordinata. Somma con la tecnica dell’accelerated cascading L’algoritmo di somma parallelo con la tecnica della prima metà non è ottimo perché costa O(n log n) (ovvero n processori per log n tempo),mentre il miglior algoritmo sequenziale richiede tempo O(n). La tecnica dell’accelerated cascading ci permette di ridurre il costo dell’algoritmo parallelo a O(n) e quindi a raggiungere valore dell’efficienza uguale ad 1. La tecnica consiste nel dividere l’algoritmo in due fasi: 1° fase: si applica l’algoritmo sequenziale su k sotto-istanze del problema di piccola dimensione h in modo da ridurre la dimensione totale dello intero problema. Tempo parallelo O(h). 2° fase: si applica l’algoritmo parallelo con p processori sui k risultati del passo precedente. Tempo parallelo O(log k) Costo = O(p (h + log k)) Algoritmo per la somma con la tecnica dell’Accelerated Cascading Si adoperano p processori SommaAC(A, n) begin k=p h = n/k for i = 0 to k-1 pardo Pi: bi = i * h for j = 1 to h -1 do if bi + j < n then A[ bi ] = A[ bi ] + A[ bi + j ] B[ i ] = A[ bi ] Somma(B, k) end // algoritmo di somma parallela standard Tempo parallelo O(h + log k) Esempio di somma con la tecnica dell’Accelerated Cascading n = 12 P0 P=4 P1 size = 3 P2 P3 24 32 10 11 7 16 9 45 2 19 31 5 1° fase sequenziale 2° fase parallela 24 11 9 19 56 18 54 50 66 34 56 55 122 89 211 Analisi dell’algoritmo SommaAC Per ottenere costo Cp=O(n) e quindi Eff=1, dobbiamo scegliere O(n/log n) processori, assegnando a ciascuno O(log n) elementi da sommare sequenzialmente. La 1° fase pertanto richiede tempo paralello O(log n) per generare O(n/log n) elementi. Con O(n/log n) processori si opera con l’algoritmo di somma parallelo su gli O(n/log n) elementi, impiegando un tempo parallelo pari a: log (n/log n) = log n – log log n = O(log n) Costo totale = O(n/log n (log n + log n)) = O(n) Somme prefisse con Accellerated Cascading Si adoperano k processori Per semplicità assumiamo n multiplo di k: n = h·k begin for i = 0 to k-1 pardo Pi: bi = i * h // inizio blocco i-esimo for j = 1 to h -1 do A[ bi + j ] = A[ bi + j ] + A[ bi + j-1 ] B[ i ] = A[ bi + h-1 ] // l’ultimo del blocco PrefixSum(B, k) for i = 1 to k-1 pardo Pi: for j = 0 to h -1 do A[ bi + j ] = A[ bi + j ] + B[ i-1 ] end Tempo parallelo O(h + log k)