Di cosa parlamo oggi: Problema della Selezione Input: Array A[1 . . . n] di n numeri distinti ed un intero i, con i ∈ {1, . . . , n}. Output: l’elemento di rango i in A Ricordiamo: rango di un elemento x = numero di elementi che sono ≤ di x nel vettore A Esempi: Elemento di valore minimo in A = elemento di rango 1 Elemento di valore massimo in A = elemento di rango n Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 1/34 Problema della Selezione: soluzione "semplice" Input: Array A[1 . . . n] di n numeri distinti ed un intero i, con i ∈ {1, . . . , n}. Output: l’elemento di rango i in A A LGORITMO -S EMPLICE(A, i) 1. Ordina il vettore A 2. Output A[i] Complessitá O(n log n) Si puó far meglio? Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 2/34 Vediamo qualche caso particolare... Ricerca del Minimo (elemento di rango 1) di A[1 . . . n] min = A[1] for i = 2 to n do if A[i] < min then min = A[i] return min Complessitá dell’algoritmo = O(n) Stessa storia per il calcolo del massimo in A (elemento di rango n) Per fortuna vale un risultato simile per la ricerca dell’elemento di di rango i in A, qualunque sia i. Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 3/34 Un pó di intuizione... Prima ancora di chiederci come trovare l’elemento di rango k nell’array A[1...n], chiediamoci: come stabilire che un dato elemento a di A é (o non é) l’elemento di rango k di A? Un modo semplice potrebbe essere il seguente: dividiamo l’array A in A1 =tutti x < a a A2 = tutti x > a come facevamo in Q UICKSORT. Se |A1 | = k − 1, l’elemento a é proprio l’elemento di rango k che cercavamo, se |A1 | = 6 k − 1 l’elemento a non é l’elemento di rango k che cercavamo (tale test riusciamo a realizzarlo in tempo Θ(n)). La cosa interessante è che il test ci dice molto di più del solo fatto che l’elemento a é o non é l’elemento di rango k di A. Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 4/34 Possibile algoritmo basato su Divide et Impera Partiamo dall’array A A= e vogliamo trovare l’elemento di rango k in A ⇓ scegliamo un elemento a in A e dividiamo A in: A1 =tutti x < a a A2 = tutti x > a dove stá l’elemento di rango k ? Se |A1 | = k − 1, allora l’elemento di rango k é proprio a. Se |A1 | ≥ k , allora l’elemento di rango k stá in A1 Se |A1 | < k − 1, allora l’elemento di rango k stá in A2 , e sará l’elemento di rango k − |A1 | − 1 di A2 Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 5/34 Algoritmo generale basato su Divide et Impera S ELECT(A, k) % trova l’elemento di rango k in A if |A| = 1 then return(A[1]) else scegli un elemento a da A e DIVIDI A in A1 e A2 , con A1 = {x ∈ A : x < a}, e A2 = {x ∈ A : x > a}, calcola j = |A1 | if k = j + 1 then return(a) else if k ≤ j then return(S ELECT(A1 , k)) else return(S ELECT(A2 , k − j − 1)) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 6/34 Esempio A = [15 13 11 19 18 12 14 17], k = 7 15 13 13 11 11 12 19 14 18 15 12 17 14 18 17 19 a = 15 Quindi A1 = [13 11 12 14], |A1 | = j = 4 e poichè k = 7 > j = 4, ricorriamo nella parte destra dell’array, ovvero in A2 = [17 18 19] e lì occore cercare l’elemento di rango k − j − 1 = 7 − 4 − 1 = 2 che è 18, che è anche l’elemento di rango 7 dell’intero array A • Osservazione: È ovvio che la partizione di A in (A1 , a, A2 ) si può effettuare in tempo Θ(n), e che la si può effettuare senza usare un array ausiliare, usando le stesse idee illustrate in Quicksort. Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 7/34 Analisi della complessità di S ELECT S ELECT(A, k) % trova l’elemento di rango k in A if |A| = 1 then return(A[1]) else scegli un elemento a da A e DIVIDI A in A1 = {x ∈ A : x < a}, e A2 = {x ∈ A : x > a}, calcola j = |A1 | if k = j + 1 then return(a) else if k ≤ j then return(S ELECT(A1 , k)) else return(S ELECT(A2 , k − j − 1)) Sia T (n, k) = complessità di S ELECT(A, k). Detto j + 1 = rango dell’elemento a scelto, avremo •T (n, k) = Θ(n) + Θ(1) se k = j + 1 •T (n, k) = Θ(n) + T (j, k) se k ≤ j (ricorriamo infatti in A1 ) •T (n, k) = Θ(n) + T (n − j − 1, k − j − 1) se k > j (ricorriamo infatti in A2 ) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 8/34 Complessità di S ELECT nel caso peggiore Ricordando l’analisi effettuata durante lo studio di Quicksort, abbiamo che la procedura che partiziona A in A1 , a, e A2 , puó in linea di principio restituire una partizione di A con |A1 | = n − 1, e |A2 | = 0. Ció accadrebbe, ad esempio, se avessimo scelto a come elemento massimo di A. Potremmo avere quindi la seguente equazione di ricorrenza nel caso di k = 1 T (n, 1) = T (n − 1, 1) + Θ(n) che ha ovvia soluzione T (n, 1) = Θ(n2 ) , Quindi l’algoritmo S ELECT puó anche essere peggiore dell’algoritmo semplice prima visto! Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 9/34 Peró... Se avessimo scelto un elemento a per partizionare l’array A in A1 = {x ∈ A : x < a}, a, e A2 = {x ∈ A : x > a}, per cui valesse |A1 | = n/c e |A2 | = n(1 − 1/c) − 1, per un qualche c > 1, avremmo avuto la seguente equazione di ricorrenza •T (n, k) = Θ(n) + Θ(1) se k = n/c + 1 •T (n, k) = Θ(n) + T (n/c, k) se k ≤ n/c (ricorriamo in A1 ) •T (n, k) = Θ(n) + T (n(1 − 1/c) − 1, k − n(1 − 1/c) − 1) se k > n/c (ricorriamo in A2 ) Non é difficile vedere che tali equazioni hanno soluzioni T (n, k) = Θ(n) (dal Teorema generale visto nella Lezione 4) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 10/34 Cosa succede quindi? • Esistono delle situazioni "sfortunate" in cui l’algoritmo S ELECT ha complessitá Θ(n2 ) • Esistono delle situazioni "favorevoli" in cui lo stesso algoritmo S ELECT ha complessitá Θ(n) Come possiamo evitare le situazioni "sfavorevoli"? Come in Quicksort decidiamo di scegliere l’elemento a, in accordo a cui partizionamo l’array A in A1 ed A2 , equiprobabilmente a caso! Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 11/34 In altri termini... Visto che le situazioni "favorevoli" sembrano essere "molte" e quelle "sfavorevoli" sembrano essere poche, se scegliessimo a caso l’elemento a in accordo a cui partizioniamo l’array A, allora ragionevolmente con probabilitá "grande" andremmo in una situazione favorevole, e con probabilitá "bassa" andremmo a ricadere in una situazione "sfavorevole" Ovvero, in "media" dovremmo capitare in una situazione favorevole Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 12/34 Procediamo quindi a caso S ELECT(A, k) % trova l’elemento di rango k in A if |A| = 1 then return(A[1]) else scegli a caso a e DIVIDI A in A1 = {x ∈ A : x < a}, e A2 = {x ∈ A : x > a}, calcola j = |A1 | if k = j + 1 then return(a) else if k ≤ j then return(S ELECT(A1 , k)) else return(S ELECT(A2 , k − j − 1)) Detta T (n, k) la complessità di S ELECT(A, k), e j + 1 il rango di a, questo algoritmo impiegherá • tempo T (n, k) = Θ(n) + Θ(1) (se abbiamo scelto a di rango j + 1 = k) • tempo T (n, k) = Θ(n) + T (j, k) (se abbiamo scelto a di rango j ≥ k) •T (n, k) = Θ(n) + T (n − j − 1, k − j − 1) (se abbiamo scelto a di rango j < k) Ciascuno di questi eventi occorrerá con probabilitá 1/n, pari alla probabilitá di scegliere un certo a di rango j + 1 ∈ {1, . . . , n} Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 13/34 e quindi... Ci troviamo di nuovo di fronte ad una una quantitá (il tempo di esecuzione di S ELECT) che assume differenti valori con certe probabilitá. Di nuovo non ha senso parlare di tempo di esecuzione nel caso peggiore, ma occorrerá valutare il tempo di esecuzione nel caso medio. Ricordiamo ancora una volta che se abbiamo una "quantitá" T che puó assumere differenti valori t1 , t2 , . . . , tn , con rispettive probabilitá P r{T = ti } = pi , per i = 1, . . . , n, allora il Valore Medio di T é E[T ] = n X ti × pi . i=1 Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 14/34 ritornando a S ELECT... Occorre quindi valutare la seguente quantitá per S ELECT X T (n, k) = (tempo di esecuzione)×P r{di avere quel tempo di esec.} tempo di esec. che rappresenterá il Tempo Medio di esecuzione di S ELECT Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 15/34 Valutiamola quindi Per quanto detto prima, il tempo di esecuzione T (n, k) di S ELECT può assumere differenti valori pari a: •Θ(n) + Θ(1) (se abbiamo scelto a di rango j + 1 = k) • T (n, k) = Θ(n) + T (j, k) (se abbiamo scelto a di rango j ≥ k) •T (n, k) = Θ(n) + T (n − j − 1, k − j − 1) (se abbiamo scelto a di rango j < k) Pertanto, il valor medio di T (n, k) sarà dato dalla somma di tali quantitá moltiplicate per la probabilitá di avere tali quantitá. Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 16/34 Equazione di ricorrenza per T (n, k) Sia T (n, k) il tempo medio di esecuzione di S ELECT(n, k) Abbiamo la seguente ricorsione per T (n, k) k−2 1 X T (n, k) = T (n − j − 1, k − j − 1) n j=0 + n−1 X (ricorsione , k > j + 1) T (j, k) (ricorsione , k ≤ j) j=k +Θ(1) +Θ(n) (terminazione , k = j + 1) (tempo per partizionare l’array) Proveremo che ∃c > 0 tale che T (n, k) < cn Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 17/34 Prova che T (n, k) < cn Per induzione su n. Sappiamo che T (n, k) = k−2 X 1 n T (n − j − 1, k − j − 1) + j=0 n−1 X j=k T (j, k) + Θ(1) + Θ(n) ovvero, che ∃b > 0 tale che T (n, k) ≤ k−2 X 1 n T (n − j − 1, k − j − 1) + j=0 n−1 X T (j, k) + bn j=k Assumiamo induttivamente che T (s, k) < cs, ∀s < n, e proviamolo per s = n Abbiamo quindi, dall’ipotesi induttiva k−2 n−1 j=0 j=k X 1 X c(n − j − 1) + T (n, k) ≤ cj + bn n Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 18/34 continuiamo T (n, k) ≤ k−2 X 1 n c(n − j − 1) + j=0 n−1 X j=k cj + bn k−2 n−1 k−1 j=0 j=1 j=1 X X X 1 cj + bn cj − (j + 1) + cn(k − 1) − c = n 1 n(n − 1) k(k − 1) k(k − 1) = +c −c cnk − cn − c + bn n 2 2 2 2 n n 1 cnk − cn − ck 2 + ck + c − c + bn = n 2 2 1 n2 3 = c − c n + (cn + c)k − ck 2 + bn n 2 2 Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 19/34 Studiamo f (k) = n2 c2 − c 32 n + (cn + c)k − ck 2 f ′ (k) = cn + c − 2ck, f ′′ (k) = −2c < 0 con f ′ (k) = cn + c − 2ck = 0 per k = Quindi cn+c 2c = n+1 2 n+1 2 é un punto di massimo per f (k), ovvero 3 2 c n+1 = cn − cn + ∀k f (k) ≤ f 2 4 4 Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 20/34 Ritorniamo all’equazione di ricorrenza per T (n, k) Avevamo 1 n2 3 c − c n + (cn + c)k − ck 2 + bn T (n, k) ≤ n 2 2 1 = f (k) + bn n 1 n+1 ≤ f + bn n 2 c 1 3 2 + bn cn − cn + = n 4 4 3 c = cn − c + + bn 4 c 4n c = cn − n+c− + bn 4 4n Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 21/34 Passo CRUCIALE e FINALE (!) Abbiamo visto che c n+c− T (n, k) ≤ cn − + bn 4 4n c Osserviamo ora che per costante c tale che c > 4b, vale che c c − n+c− + bn < 0 4 4n ovvero esiste una costante c > 0 tale che c c T (n, k) ≤ cn − n+c− + bn < cn 4 4n che é quanto volevamo dimostrare... Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 22/34 Pausa di riflessione Cosa abbiamo fatto? Abbiamo preso un algoritmo (Select) che ha un tempo di esecuzione O(n2 ) nel caso peggiore, e introducendo scelte casuali al suo interno, lo abbiamo trasformato in un algoritmo con tempo di esecuzione medio O(n) Ciò è potuto accadere in quanto la scelta casuale dell’elemento a (mediante il quale effettuiamo la partizione dell’array A) fà si che la probabilità di avere "situazioni" che portano a complessità Θ(n2 ) diventa "piccola", ed è "grande" invece la probabilità di ricadere in situazioni che portano a complessità O(n) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 23/34 Insisto: voglio un algoritmo non randomizzato! Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 24/34 Ricordiamo l’algoritmo generale basato su Divide et Impera S ELECT(A, k) % trova l’elemento di rango k in A if |A| = 1 then return(A[1]) else scegli un elemento a da A e DIVIDI A in A1 e A2 , con A1 = {x ∈ A : x < a}, e A2 = {x ∈ A : x > a}, calcola j = |A1 | if k = j + 1 then return(a) else if k ≤ j then return(S ELECT(A1 , k)) else return(S ELECT(A2 , k − j − 1)) e la sua complessitá T (n, k) Detto j + 1 = rango dell’elemento a scelto, avremo •T (n, k) = Θ(n) + Θ(1) se k = j + 1 •T (n, k) = Θ(n) + T (j, k) se k ≤ j (ricorriamo infatti in A1 ) •T (n, k) = Θ(n) + T (n − j − 1, k − j − 1) se k > j (ricorriamo infatti in A2 ) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 25/34 Di conseguenza... Abbiamo visto che se si sceglie un elemento a di rango “piccolo", si possono avere equazioni di ricorrenze del tipo T (n, 1) = T (n − 1, 1) + Θ(n) con soluzione T (n, 1) = Θ(n2 ) , Abbiamo altresí visto che se si sceglie un elemento a di rango n/c, per qualche costante c > 1, si hanno equazioni di ricorrenze del tipo T (n, k) = Θ(n) + T (n/c, k) con soluzione T (n, k) = Θ(n) , Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 26/34 Quindi per avere un algoritmo per il problema della selezione dell’elemento di rango k che abbia complessità O(n) nel caso peggiore occorrerebbe scegliere l’elemento a in accordo al quale partizionamo l’array A in modo tale che a non sia nè "troppo grande", nè "troppo piccolo" (infatti, se lo sceglievamo sempre "troppo grande", ad esempio pari al massimo di A, ottenevamo un algoritmo di complessità Θ(n2 ) nel caso peggiore) L’ideale sarebbe scegliere per a un elemento di rango n/2, detto in gergo mediana di A , che è peró altrettanto difficile quanto il problema di partenza che vogliamo risolvere! Come fare ad uscire da questa specie di "circolo vizioso"? Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 27/34 Idea (e complessitá) dell’algoritmo Divide et Impera 1. Dividi gli n elementi in input in ⌊n/5⌋ gruppi di 5 elementi ciascuno, ed al piú un gruppo addizionale di n mod 5 elementi Tempo= Θ(n) 2. Trova la mediana di ciascuno degli ⌈n/5⌉ gruppi (ad es., ordinando ciascun gruppo con InsertionSort e prendendo l’elemento di mezzo) Tempo= Θ(n) 3. Usa ricorsivamente l’algoritmo per trovare la mediana x delle ⌈n/5⌉ mediane trovate al passo 2. Tempo= T (⌈n/5⌉) 4. Partiziona l’array input A in (A1 , x, A2 ). Sia |A1 | = j, |A2 | = n − j − 1. Tempo= Θ(n) 5. Ricorri per cercare l’elemento di rango k in A1 se k ≤ j, ritorna x se k = j + 1, ricorri in A2 per cercarvi l’elemento di rango k − (j + 1) se k >j+1 Tempo≤ T (max(|A1 |, |A2 |) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 28/34 abbiamo quindi Detta T (n) la complessitá dell’algoritmo nel caso peggiore, essa si puó scrivere come T (n) = Θ(n) (per la divisione di A in n/5 gruppi) +Θ(n) (per il calcolo della mediana di ciascun gruppo di 5 elementi) +T (⌈n/5⌉) (per la ricorsione sulle ⌈n/5⌉ mediane prima trovate) +Θ(n) +T (max(|A1 |, |A2 |) (per la partizione di A) (per la ricorsione sul lato piú grande della part.) = Θ(n) + Θ(n) + T (⌈n/5⌉) + Θ(n) + T (max(|A1 |, |A2 |) = T (max(|A1 |, |A2 |)) + T (⌈n/5⌉) + Θ(n) Ci serve sapere quant’é max(|A1 |, |A2 |) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 29/34 Ricordiamo che A1 = { tutti gli elementi di A che sono < di x} A2 = { tutti gli elementi di A che sono > di x} Cerchiamo di valutare |A1 | e |A2 | Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 30/34 Idea dell’analisi x I punti rappresentano gli elementi (nel nostro caso 33). Ogni gruppo di 5 elementi é rappresentato da una colonna. Le mediane di ogni gruppo sono bianche. La mediana delle mediane é l’elemento etichettato con x. Le frecce vanno da elementi piú grandi a piú piccoli. In ogni gruppo (tranne l’ultimo) ci sono almeno 3 elementi > di x (sono quelli a destra di x racchiusi nella linea rossa) In ogni gruppo a sinistra di x (racchiusi nella linea blu) ci sono almeno 3 elementi < x. 3n 1 n Quindi |A2 | ≥ 3 2 5 − 2 ≥ 10 − 6 ⇒ |A1 | ≤ 7n/10 + 6 3n 1 n |A1 | ≥ 3 2 5 − 2 ≥ 10 − 6 ⇒ |A2 | ≤ 7n/10 + 6 Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 31/34 Ricapitolando: T (n) = T (max(|A1 |, |A2 |)) + T (⌈n/5⌉) + Θ(n) Abbiamo poi scoperto che |A1 |, |A2 | ≤ 7n/10 + 6 ⇒ max(|A1 |, |A2 |) ≤ 7n/10 + 6 abbiamo quindi la ricorsione T (n) ≤ T (7n/10 + 6) + T (⌈n/5⌉) + Θ(n) Proveremo che essa ha soluzione T (n) ≤ cn, per qualche costante c opportuna, per induzione su n ovvero assumeremo che per qualche costante c, e ∀k < n vale T (k) ≤ ck , indi proveremo che T (n) ≤ cn Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 32/34 Procediamo T (n) ≤ T (7n/10 + 6) + T (⌈n/5⌉) + Θ(n) ≤ T (7n/10 + 6) + T (⌈n/5⌉) + an (per definizione di Θ(n)) ≤ c(7n/10 + 6) + c(⌈n/5⌉) + an (per ipotesi induttiva) ≤ 7cn/10 + 6c + cn/5 + c + an (in quanto ⌈x⌉ ≤ x + 1) = 9cn/10 + 7c + an = cn + (−cn/10 + 7c + an) ≤ cn (per n sufficientemente grande e purché c > 10a) Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 33/34 Qualche osservazione • Abbiamo provato che é possibile determinare l’elemento di rango k in un array A[1 . . . n], 1 ≤ k ≤ n in tempo Θ(n) • Potremmo ottenere da questo risultato, una versione di Quicksort che ordina n numeri in tempo Θ(n log n) nel caso peggiore! Basta infatti usare l’algoritmo S ELECT(n, n/2) prima di partizionare l’array in due parti, usando l’output di S ELECT(n, n/2) come l’elemento a mediante il quale si partiziona A. In questo caso la partizione risulterá essere perfettamente bilanciata (n/2 elementi a sinistra ed altrettanti a destra), portando ad una equazione di ricorrenza sul tempo di Quicksort del tipo T (n) = 2T (n/2) + Θ(n), che sappiamo ha soluzione T (n) = Θ(n log n) In pratica, peró, l’algoritmo randomizzato che abbiamo visto nella lezione scorsa risulta essere di gran lunga preferibile. Universitá degli Studi di Salerno – Corso di Algoritmi – Prof. Ugo Vaccaro – Anno Acc. 2009/10 – p. 34/34