Algoritmi di ordinamento! Selection Sort! Quick Sort! Lower bound alla complessità degli algoritmi di ordinamento Ordinamento 1 Selection Sort SelectionSort(dati[]) { for (i=0; idati.length-1; i++) { min = Seleziona min. in dati[i],., dati[dati.length-1] Scambia min con dati[i];! L elemento minimo viene messo in posizione 0! Si itera il procedimento sulle posizioni successive Ordinamento 2
Selection Sort/2 SelectionSort(dati[], i) { min = Seleziona min. in dati[i],., dati[dati.length-1] Scambia min con dati[i]; SelectionSort(dati[], i+1) ; SelectionSort(dati[], 0) ;! Versione ricorsiva Ordinamento 3 Selection Sort/3 Ordinamento del vettore di interi {5, 2, 3, 8, 1 Ordinamento 4
Come ordinare oggetti diversi da numeri! Ordinare un vettore i cui elementi sono oggetti complessi. Es. oggetti della classe: class Persona { String cognome; String CF; public Persona (String cognome, String CF) { this.cognome = cognome; this.cf = CF;! Come ordinare un array di tali oggetti rispetto al cognome? Ordinamento 5! Occorre: Come ordinare oggetti diversi da numeri/2 1. Dichiarare che tra gli oggetti della classe (Persona nell esempio) è definito un ordinamento 2. Dichiarare rispetto a quale o a quali membri della classe è definito l ordinamento (il cognome nel nostro caso) 3. Definire la regola che stabilisce l ordinamento tra due oggetti della classe (nel nostro caso: due oggetti di tipo persona sono ordinati alfabeticamente secondo i rispettivi cognomi)! In C++ si possono sovraccaricare gli operatori! In Java si può dichiarare che la classe (Persona) implementa l interfaccia Comparable (non è la sola possibilità) Ordinamento 6
Come ordinare oggetti diversi da numeri/3! Il passo 1 si traduce così: class Persona implements Comparable {! I passi 2 e 3 consistono nell implementare l unico metodo previsto dall interfaccia Comparable: int compareto(object o)! compareto definisce le regole che stabiliscono l ordinamento tra oggetti della classe (nel nostro caso, l ordinamento è quello alfabetico sui cognomi) Ordinamento 7 Come ordinare oggetti diversi da numeri/4! Quindi: class Persona implements Comparable { String cognome; String CF; public Persona (String cognome, String CF) { this.cognome = cognome; this.cf = CF; public int compareto (Object pers) { return cognome.compareto(((persona)pers).cognome); Nota: occorre fare il cast perché compareto vuole un Object Ordinamento 8
Selection Sort/4 public void selectionsort(comparable[] data) { int i, j, least; for (i = 0; i data.length-1; i++) { for (j = i+1, least = i; j data.length; j++) if (data[j].compareto(data[least]) 0) least = j; swap(data, least, i); /* Scambia gli oggetti in pos. i e least */ Es.: versione ricorsiva Ordinamento 9 Selection Sort - Tempo di esecuzione! Supponiamo che l array contenga n elementi! Alla i-esima iterazione occorre trovare il massimo di n-i+1 elementi e sono quindi necessari n-i confronti! Vi sono n-1 cicli Costo = n" 1 n" 1!( n " i) =! n( n " 1) i= 1 i= 1 2! Si osservi che tale costo non dipende dall eventuale ordinamento parziale dell array (cfr. Insertion Sort) i = Ordinamento 10
quicksort(array[]) { if (array.length1) { Quick Sort Scegli bound; /* subarray1 e subarray2 */ while (ci sono elementi in array) if (generico elemento bound) inserisci elemento in subarray1; else inserisci elemento in subarray2; quicksort(subarray1); quicksort(subarray2); Ordinamento 11 Quick Sort/2 bound Array subarray1 subarray2 = bound bound1 bound2 = bound1 = bound2 Ordinamento 12
Partizionamento dell array [8 5 4 7 6 1 6 3 8 12 10] con quicksort Ordinamento 13 Partizionamento dell array [8 5 4 7 6 1 6 3 8 12 10] con quicksort Ordinamento 14
Quick Sort/3 void quicksort(comparable[] data, int first, int last) { int lower = first + 1, upper = last; swap(data, first, (first+last)/2); /* Così, in pratica è spesso più veloce */ Comparable bound = data[first]; while (lower = upper) { while (data[lower].compareto(bound) 0) lower++; while (bound.compareto(data[upper]) 0) upper--; if (lower upper) swap(data, lower++, upper--); else lower++; /* 1 */ /* End while */ swap(data, upper, first); // Alla fine upper punta sempre a un elemento = bound if (first upper-1) /* se first == upper-1 il sottoarray ha solo 2 elementi ed è ordinato */ quicksort(data, first, upper-1); if (upper+1 last) quicksort(data, upper+1, last); Ordinamento 15 Quick Sort/4 void quicksort(comparable[] data) { if (data.length 2) return; int max = 0; /* Trova max. e mettilo alla fine; serve per evitare che lower cresca oltre la dim. dell array (non è detto che accada ma può succedere) */ for (int i = 1; i data.length; i++) if (data[max].compareto(data[i]) 0) max = i; swap(data, data.length-1, max); // Elemento piu grande in posizione finale quicksort(data, 0, data.length-2); Ordinamento 16
Analisi del Quick Sort! Costo = O(No. confronti)! Costo O(n 2 ) nel caso peggiore! Costo O(n log n) nel caso migliore e medio! In pratica l algoritmo è efficiente! Scelta pivot fondamentale Ordinamento 17 Quick Sort Caso peggiore No. confronti per sotto-array n-1 n-2 n-2 Array Array Array n-1 volte 2 1 L elemento di pivot è sempre il minimo Costo = O(n-1+n-2+...+2+1) = O(n 2 ) Ordinamento 18
Quick Sort Caso migliore No. confronti per sotto-array n potenza di 2 per semplicità n-1 n/2-1 n/4-1 Array log n+1 volte 2 1 Costo = logn n n n n + 2 + 4 + L + n =! 2 2 4 n i= 0 i n i 2 = n(log n + 1) Ordinamento 19 Efficienza algoritmi di ordinamento! Merge Sort (e Heap Sort): O(n log n)! Quick Sort, Selection Sort, Insertion Sort: O(n 2 )! Quick Sort: O(n log n) nel caso migliore! Selection Sort: O(n 2 ) in tutti i casi! Insertion Sort: O(n) nel caso migliore! Domanda: qual è l efficienza massima (complessità minima) ottenibile nel caso peggiore - Lower bound Ordinamento 20
Ordinamento limiti inferiori! Osservazione fondamentale: tutti gli algoritmi devono confrontare elementi! Dati a i, a k, tre casi possibili: a i a k, a i a k, oppure a i =a k! Si assume per semplicità che tutti gli elementi siano distinti! Si assume dunque che tutti i confronti abbiano la forma a i a k, e il risultato del confronto sia vero o falso! Nota: se gli elementi possono avere lo stesso valore allora si considerano solo confronti del tipo a i = a k Ordinamento 21 Alberi di decisione Albero di decisione per Insertion Sort sull insieme {a 1, a 2, a 3 a 1 :a 2 a 2 :a 3 a 1 :a 3 a 1,a 2,a 3 a 1 :a 3 a 2,a 1,a 3 a 2 :a 3 a 1,a 3,a 2 a 3,a 1,a 2 a 2,a 3,a 1 a 3,a 2,a 1! Un albero di decisione rappresenta i confronti eseguiti da un algoritmo su un dato input! Ogni foglia corrisponde ad una delle possibili permutazioni Ordinamento 22
Alberi di decisione/2 Albero di decisione per Insertion Sort sull insieme {a 1, a 2, a 3 a 1 :a 2 a 2 :a 3 a 1 :a 3 a 1,a 2,a 3 a 1 :a 3 a 2,a 1,a 3 a 2 :a 3 a 1,a 3,a 2 a 3,a 1,a 2 a 2,a 3,a 1 a 3,a 2,a 1! Vi sono n! possibili permutazioni - l albero deve contenere n! foglie! L esecuzione di un algoritmo corrisponde ad un cammino sull albero di decisione corrispondente all input considerato Ordinamento 23 Alberi di decisione/3! Riassumendo: " Albero binario " Deve contenere n! foglie! Il più lungo cammino dalla radice ad una foglia (altezza) rappresenta il No. confronti che l algoritmo deve eseguire nel caso peggiore! Teorema: qualunque albero di decisione che ordina n elementi ha altezza!(n log n)! Corollario: nessun algoritmo di ordinamento ha complessità migliore di!(n log n) Nota: esistono algoritmi di ordinamento con complessità più bassa, ma richiedono informazioni aggiuntive Ordinamento 24
Dimostrazione teorema 1. Un albero di decisione è binario 2. Albero binario di altezza h non ha più di 2 h foglie 1 2 3 1=2 1-1 2= 2 2-1 4= 2 3-1 h 2 h-1 3. Dobbiamo avere: 2 h-1 No. foglie = n! 4. h-1 log(n!) Ordinamento 25 Dimostrazione teorema/2 5. n! (n/e) n (approssimazione di Stirling) 6. h-1 log(n/e) n = n log(n/e) = n logn n loge =!(n log n)! Corollario: gli algoritmi Merge Sort e Heap Sort hanno complessità asintotica ottima Ordinamento 26