30 ottobre Mergesort F. Bombi 30 ottobre 2002
2Lalgoritmo Lalgoritmo di ordinamento mergesort o per fusione è un algoritmo efficiente in quanto ha una complessità temporale O(nlog(n)) e può essere utilizzato per ordinare un vettore di oggetti Comparable oppure una lista Lalgoritmo si basa sullesistenza di un algoritmo efficiente in grado di fondere due vettori (o due liste) ordinate in un vettore (o in una lista) ordinata in un tempo O(n+m) essendo n ed m la lunghezza dei due vettori
30 ottobre Fusione di due liste ordinate Due liste l1 e l2 ordinate di lunghezza n e m possono essere fuse in ununica lista ordinata con il seguente algoritmo Sia l una nuova lista vuota mentre le due liste l1 e l2 non sono vuote se testa(l1) < testa(l2) accoda(testa(l1)) a l; l1 = resto(l1) altrimenti accoda(testa(l2)) a l; l2 = resto(l2) se l1 non è vuota accoda l1 a l altrimenti accoda l2 a l
30 ottobre 20024Analisi Ogni operazione elementare richiede un tempo costante, alla fine la lista l avrà una lunghezza pari a n+m e di conseguenza le operazioni necessarie sono n+m e quindi lalgoritmo ha una complessità O(n+m) Il numero di confronti necessari è al minimo pari a n (o m ) se una delle due liste è composta da elementi minori degli elementi dellaltra lista, è invece O(n+m) se gli elementi delle due liste sono intercalati Lalgoritmo può essere usato per fondere due vettori parzialmente riempiti in un vettore ordinato con efficienza analoga, si utilizzeranno tre cursori per tenere traccia della posizione della testa dei due vettori dorigine e della posizione della coda nel vettore risultato
30 ottobre private void merge (Lista l, Lista l1, Lista l2) { Iteratore i1 = new Iteratore(l1); Iteratore i2 = new Iteratore(l2); while (i1.hasNext() && i2.hasNext()) { if (((Comparable)i1.get()).compareTo(i2.get()) < 0) l.addLast(l1.removeFirst()); else l.addLast(l2.removeFirst()); } while (i1.hasNext()) l.addLast(l1.removeFirst()); while (i2.hasNext()) l.addLast(l2.removeFirst()); } Fusione di due liste
30 ottobre 20026Ordinare Sia l la lista da ordinare se la lista ha lunghezza > 1 copiare la lista l in due liste l1 e l2 (alternando un elemento per lista) ordinare ricorsivamente l1 ordinare ricorsivamente l2 fondere l1 e l2 in l NB: il caso base si ha quando la lista ha lunghezza 0 o 1
30 ottobre private void ms (Lista l) { Lista l1 = new Lista(); Lista l2 = new Lista(); while (l.size() > 0) { l1.addLast(l.removeFirst()); if (l.size() > 0) l2.addLast(l.removeFirst()); } if (l1.size() > 1) ms(l1); if (l2.size() > 1) ms(l2); merge(l, l1, l2); } NB: il codice effettua le chiamate ricorsive solo per liste di lunghezze maggiore di 1
30 ottobre , 7, 6, 5, 4, 3, 2, 1 8, 6, 4, 27, 5, 3, 1 8, 46, 27, 35, , 8 2, 6 3, 7 1, 5 2, 4, 6, 81, 3, 5, 7 1, 2, 3, 4, 5, 6, 7, 8 private void ms (Lista l) { Lista l1 = new Lista(); Lista l2 = new Lista(); while (l.size() > 0) { l1.addLast(l.removeFirst()); if (l.size() > 0) l2.addLast(l.removeFirst()); } if (l1.size() > 1) ms(l1); if (l2.size() > 1) ms(l2); merge(l, l1, l2); } Albero delle chiamate ricorsive per una lista di 8 elementi
30 ottobre import catena.*; public class Fusione { public Fusione (Lista x) { ms(x); } private void ms (Lista l) {…} private void merge (Lista l, Lista l1, Lista l2) {…. } }
30 ottobre import catena.*; import java.io.*; public class Ordina { public static void main (String[] arg) throws IOException { BufferedReader in = new BufferedReader(new FileReader(arg[0])); String str; Lista l = new Lista(); while ((str = in.readLine()) != null) l.addLast(str); System.out.println(l); new Fusione(l); System.out.println(l); } Ordinare le righe di un file
30 ottobre Analisi di mergesort Vogliamo dimostrare che mergesort ha una complessità temporale O(n log(n)) Supponiamo che ogni operazione sulle liste richieda un tempo costante Per semplicità supponiamo anche che la lunghezza n della lista da ordinare sia una potenza di 2 e quindi si possa dire che n = 2 k Analizziamo il tempo in funzione di k
30 ottobre A meno di costanti (inessenziali nella valutazione del comportamento asintotico dellalgoritmo) possiamo dire che: T(k) = 2 T(k-1) + 2 k T(0) = 1 La ricorrenza ha come soluzione T(k) = (k+1)2 k Infatti questo è vero per k = 0, supponendo che sia vero per un valore qualsiasi di k= k avremo che T(k) = (k+1) 2 k
30 ottobre Dobbiamo dimostrare che è vero T(k+1) = (k+1+1)2 k+1 Infatti dalla ricorrenza iniziale abbiamo: T(k+1) = 2 T(k+1-1) + 2 k+1 Dallipotesi fatta per k = k si ha: T(k+1) = 2(k+1)2 k + 2 k+1 E quindi: T(k+1) = (k+1+1)2 k+1 Che era quanto volevamo dimostrare Ora dato che k=log(n) abbiamo che T(n) = n(log(n)+1) = O(n log(n))
30 ottobre Per ordinare un vettore Dividere il vettore s d c s1d1s2d2
30 ottobre Per ordinare un vettore Fondere due vettori 214 s1 d1 s2 d2 Vettore temporaneo