Ordinamento Continuiamo a discutere il problema dell’ordinamento: Ordinamento (Sorting) INPUT: Sequenza di n numeri <a1,a2,… … an> OUTPUT: Permutazione π<a1, a2 , … …, an> = <a1’,a2’, … … , an’> tale che a1’ a2’ … … an’ Algoritmo di Insertion-Sort problema dell’ordinamento risolve il La complessità temporale (tempo di esecuzione dell’algoritmo) dell’ Insertion-Sort verifica: T(n) = (n2) nel caso peggiore e nel caso medio T(n) = (n) nel caso migliore Oltre alla complessità temporale possiamo studiare la complessita’ spaziale di un algoritmo: Complessità spaziale = spazio di memoria necessario per ospitare le strutture di dati utilizzate dall’algoritmo. La complessità spaziale dell’insertion sort è (n) 1 L’algoritmo di Insertion Sort è l’ unico algoritmo possibile per risolvere il problema dell’ordinamento? Ovviamente NO! L’algoritmo di Selection Sort è definito dalla seguente strategia: • Al primo passo, si seleziona l’elemento più piccolo tra gli n elementi della sequenza, e lo si pone nella prima posizione (scambiandolo con l’elemento A[1] ); • Al secondo passo, si seleziona l’elemento più piccolo tra i rimanenti n-1 elementi di A, e lo si pone nella seconda posizione (scambiandolo con A[2] ); . . . • Al k-esimo passo, si seleziona l’elemento più piccolo tra i rimanenti n-(k-1) elementi di A, e lo si pone nella k-esima posizione (scambiandolo con A[k]); . . . • All’(n-1)-esimo passo, si seleziona l’elemento più piccolo tra gli ultimi 2 elementi di A, e lo si 2 pone nella penultima posizione (scambiandolo con A[n-1]). Notare La strategia descritta prevede ad ogni step la risoluzione dello stesso problema (ricerca del minimo di una sequenza). => Abbiamo risolto il problema dell’ordinamento riconducendo tale problema al più semplice problema di ricerca del minimo. Algoritmo per la ricerca dell’elemento minimo di una sequenza Min_elemento(A) ind_min 1 For i 2 to length(A) do if (A[i] < A[ind_min ]) then ind_min i Return ind_min Qual è la complessità spaziale di questo algoritmo? S(n) = (n) Qual è la complessità temporale di questo algoritmo? T(n) = (n) per tutti gli input 3 Algoritmo Selection Sort Selection-Sort(A) For j1 to (length(A)-1) do ind_min j For i j+1 to length(A) do if (A[i] < A[ind_min ]) then ind_min i k A[ind_min] A[ind_min] A[j] A[j] k Notiamo che l’algoritmo è corretto. Infatti: - L ’algoritmo termina sempre; - Per ogni istanza di input l’output è corretto. La correttezza dell’ algoritmo può essere mostrata ragionando per induzione. Devo mostrare che la seguente affermazione è vera per j=1,2,3,……n-1: “Al j-esimo step l’algoritmo produce una sequenza ordinata non decrescente A[1],……,A[j], dove A[1] è il più piccolo elemento, A[2] è il secondo elemento piu’ piccolo , …………, A[j] è il j-esimo elemento più piccolo della sequenza iniziale.” Verifico la validità dell’affermazione per j=1; Assumo che la affermazione sia valida per un generico valore di j=h; Mostro che da ciò segue che la affermazione è valida per j=h+1. 4 Analisi del Selection Sort Selection-Sort(A) For j1 to (length(A)-1) do ind_min j For i j+1 to length(A) do if (A[i] < A[ind_min ]) then ind_min i k A[ind_min] A[ind_min] A[j] A[j] k (*) Complessità temporale del Selection Sort - Qual è la linea (o il blocco di linee di codice) che viene eseguito più volte (operazione dominante)? if (A[i] < A[ind_min ]) - operazione di confronto - Quante volte viene eseguita? n2 n j (n2 ) 2 j1 n 1 - Il numero di volte che questa linea viene eseguita dipende dall’ input? No! La compl. temp. del selection sort è T(n)=(n2) (indip. dall’input). 5 Complessità intrinseca di un problema Abbiamo introdotto 2 algoritmi (IS, SS) che risolvono il problema dell’ordinamento: SS – T(n) = Θ(n2) IS - T(n) = Θ(n2) Ci chiediamo – Si può fare di meglio? Più precisamente – E’ possibile risolvere il problema dell’ ordinamento mediante algoritmi aventi un comportamento asintotico migliore? Notare – Per rispondere alla domanda, dobbiamo “alzare” il livello della nostra analisi. Dobbiamo discutere le proprietà generali del problema, indipendentemente dagli algoritmi specifici utilizzati per risolverlo. 6 Prima di continuare …… Quando forniamo limiti asintotici superiori/inferiori alla complessità di un algoritmo senza specificare le proprietà dell’input, ci riferiamo implicitamente al caso peggiore: T(n) = O(f(n)) Per tutte le istanze di input T(n) = O(f(n)) Tworst(n) = O(f(n)), ma anche Tbest(n) = O(f(n)) T(n) = (f(n)) Esiste almeno un’istanza di input per cui T(n)= (f(n)) Tworst (n) = (f(n)), ma non è detto che Tbest(n) = Ω(f(n)) 7 Complessità intrinseca di un problema Complessità computazionale di un algoritmo Un problema computazionale ha delimitazione superiore alla complessità O(f(n)) (upper bound) se esiste un algoritmo per la sua risoluzione con delimitazione superiore O(f(n)). Un problema computazionale ha delimitazione inferiore alla complessita’ (f(n)) (lower bound) se tutti gli algoritmi per la sua risoluzione hanno delimitazione inferiore (f(n)). Se dimostro che un problema ha delimitazione inferiore (f(n)) e trovo un algoritmo avente complesssità O(f(n)) allora… …a meno di costanti, ho un algoritmo ottimale per risolvere il problema!!! Esempio di algoritmo ottimale Algoritmo per la ricerca del minimo in un insieme non ordinato, avente complessità O(n), Infatti, ogni algoritmo dovrà almeno leggere l’input, e quindi avrà complessità (n). 8 Problema dell’ordinamento Sappiamo per ora che: Lower bound - (n) Upper bound – O(n2) (banale, dimensione dell’input) IS, BS Abbiamo un gap lineare tra upper bound e lower bound. Possiamo fare meglio ….…… 9 Lower Bound per il problema dell’ordinamento Ordinamento per confronti Dati due elementi ai ed aj, per determinarne l’ordinamento relativo effettuiamo una delle seguenti operazioni di confronto: ai aj ; ai aj ; ai aj ; ai aj ; ai a j Non si possono esaminare i valori degli elementi o ottenere informazioni sul loro ordine in altro modo. Notare – Tutti gli algoritmi di ordinamento considerati fino ad ora sono algoritmi di ordinamento per confronto. 10 Gli algoritmi di ordinamento per confronto possono essere descritti in modo astratto in termini di ALBERI DI DECISIONE. Un generico algoritmo di ordinamento per confronto lavora nel modo seguente: -Confronta due elementi ai ed aj (ad esempio effettua il test ai aj); - A seconda del risultato – riordina e/o decide il confronto successivo da eseguire. Albero di decisione - Descrive i confronti che l’algoritmo esegue quando opera su un input di una determinata dimensione. I movimenti dei dati e tutti gli altri aspetti dell’algoritmo vengono ignorati Albero di decisione dell’algoritmo Insertion Sort a1:a2 a2:a3 a1:a3 a1:a3 <a1,a2,a3> <a1,a3,a2> <a2,a1,a3> <a3,a1,a2> a2:a3 <a2,a3,a1> <a3,a2,a1> 11 Prima di discutere i dettagli, alcune definizioni …… radice a1:a2 Sotto-albero sinistro Sotto-albero destro a2:a3 a1:a3 <a1,a3,a2> a1:a3 <a1,a2,a3> nodo <a2,a1,a3> a2:a3 cammino <a3,a1,a2> <a2,a3,a1> <a3,a2,a1> foglia Profondità di un nodo = lunghezza del cammino che lo congiunge alla radice. Altezza di un albero = valore massimo della profondità dei nodi. Notare - (Numero di Foglie) 2h h = altezza dell’albero 12 Torniamo al problema dell’ordinamento Insertion-Sort a1:a2 a2:a3 a1:a3 a1:a3 <a1,a2,a3> <a1,a3,a2> <a2,a1,a3> <a3,a1,a2> a2:a3 <a2,a3,a1> <a3,a2,a1> - Ogni foglia è etichettata con una permutazione della sequenza iniziale; - L’esecuzione dell’ algoritmo corrisponde a tracciare un cammino dalla radice ad una foglia; - L’algoritmo segue un cammino diverso a seconda delle caratteristiche dell’input caso migliore: cammino più breve caso peggiore: cammino più lungo - L’altezza dell’albero fornisce il numero di confronti che l’algoritmo esegue nel caso peggiore. 13 Limite inferiore al problema dell’ordinamento per confronti Insertion-Sort a1:a2 a2:a3 a1:a3 a1:a3 <a1,a2,a3> <a1,a3,a2> <a2,a1,a3> <a3,a1,a2> a2:a3 <a2,a3,a1> <a3,a2,a1> Notiamo che: - il numero di foglie dell’albero di decisione deve essere almeno pari al numero di possibili permutazioni della sequenza iniziale: (# foglie) n! - Come già detto, sussiste la seguente relazione tra il numero di foglie e l’altezza di un albero binario: (# foglie) 2h Ciò implica: 2h n! Da cio’ segue h log2(n!), e per l’approssimazione di Stirling su n!, che impone n!>(n/e)n, ne segue che h > log2((n/e)n) = (n log2(n)) 14 In conclusione: Ricordando che l’altezza dell’albero di decisione indica il numero di confronti che un generico algoritmo effettua nel caso peggiore, otteniamo: Algoritmo T(n) = (n log2 (n)) Quindi: Lower Bound = (n log2 (n)) (problema dell ’ordinamento per confronti) Esercizi – 1) Albero di decisione per SS (3 elementi) 2) Ho scritto T(n) = (n log2 (n)). Implica T(n) = (n ln (n)) ? 15