Algoritmi di ordinamento Selection Sort Quick Sort Lower bound alla complessità degli algoritmi di ordinamento giu 03 ASD 1
Selection Sort SelectionSort(dati[]) { for (i=0; i<dati.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 giu 03 ASD 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 giu 03 ASD 3
Selection Sort/3 Ordinamento del vettore di interi {5, 2, 3, 8, 1 giu 03 ASD 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? giu 03 ASD 5
Come ordinare oggetti diversi da numeri/2 Occorre: 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à) giu 03 ASD 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) giu 03 ASD 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 giu 03 ASD 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 giu 03 ASD 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 i = 1 ( n i) = n 1 i = n( n 2 i = 1 1) Si osservi che tale costo non dipende dall eventuale ordinamento parziale dell array (cfr. Insertion Sort) giu 03 ASD 10
Quick Sort quicksort(array[]) { if (array.length>1) { 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); giu 03 ASD 11
Quick Sort/2 < bound Array subarray1 subarray2 >= bound < bound1 < bound2 >= bound1 >= bound2 giu 03 ASD 12
Partizionamento dell array [8 5 4 7 6 1 6 3 8 12 10] con quicksort giu 03 ASD 13
Partizionamento dell array [8 5 4 7 6 1 6 3 8 12 10] con quicksort giu 03 ASD 14
void quicksort(comparable[] data, int first, int last) { int lower = first + 1, upper = last; swap(data, first, (first+last)/2); /* Questo serve solo perché 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++; /* End while */ swap(data, upper, first); if (first < upper-1) /* se first == upper-1 il sottoarray ha un solo elemento ed è ordinato */ quicksort(data, first, upper-1); if (upper+1 < last) quicksort(data, upper+1, last); giu 03 ASD 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); /* largest el is now in its final position */ quicksort(data, 0, data.length-2); giu 03 ASD 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 giu 03 ASD 17
No. confronti per sotto-array Quick Sort Caso peggiore n-1 n-2 n-3 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 ) giu 03 ASD 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 n n n logn i 2 (log 1) giu 03Costo = n + 2 + 4 + L+ n = = + ASD n n i 19 2 4 n i= 0 2 n
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 giu 03 ASD 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 giu 03 ASD 21
Alberi di decisione Albero di decisione 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 giu 03 ASD 22
Alberi di decisione/2 Albero di decisione 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 giu 03 ASD 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 giu 03 ASD 24
Dimostrazione teorema 1. Un albero di decisione è binario 2. Albero binario di altezza h non ha più di 2 h-1 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!) giu 03 ASD 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 giu 03 ASD 26