Ingegneria e Scienze Informatiche - Cesena A.A. 2014-2015 pietro.dilena@unibo.it
MergeSort MergeSort MergeSort: esempio MergeSort: pseudocodice Algoritmo di ordinamento ricorsivo basato sulla tecnica Divide Et Impera. Ideato da Jon Von Neumann nel 1945 Costo computazionale: Caso pessimo: Θ(n log n) Caso medio: Θ(n log n) Caso ottimo: Θ(n log n) Algoritmo non in place: richiede O(n) di memoria ausiliaria. Implementato come algoritmo di ordinamento standard (con alcune ottimizzazioni) nelle librerie di alcuni linguaggi (Perl, Java) e alcune versioni di Linux.
MergeSort: esempio intuitivo MergeSort MergeSort: esempio MergeSort: pseudocodice
MergeSort: esempio intuitivo MergeSort MergeSort: esempio MergeSort: pseudocodice Suddivisione in sub-array di dimensione inferiore.
MergeSort: esempio intuitivo MergeSort MergeSort: esempio MergeSort: pseudocodice Suddivisione in sub-array di dimensione inferiore. Riordiniamo indipendentemente i due sotto-array (chiamate ricorsive). 2 4 5 1 3 6
MergeSort: esempio intuitivo MergeSort MergeSort: esempio MergeSort: pseudocodice Suddivisione in sub-array di dimensione inferiore. Riordiniamo indipendentemente i due sotto-array (chiamate ricorsive). 2 4 5 1 3 6 Merge dei due sotto-array ordinati (richiede memoria ausiliaria). 1 2 3 4 5 6
MergeSort: esempio intuitivo MergeSort MergeSort: esempio MergeSort: pseudocodice Suddivisione in sub-array di dimensione inferiore. Riordiniamo indipendentemente i due sotto-array (chiamate ricorsive). 2 4 5 1 3 6 Merge dei due sotto-array ordinati (richiede memoria ausiliaria). 1 2 3 4 5 6 Lista ordinata. 1 2 3 4 5 6
MergeSort: pseudocodice MergeSort MergeSort: esempio MergeSort: pseudocodice 1: procedure MergeSort(A, p, r) 2: if p < r then 3: q (p + r)/2 4: MergeSort(A, p, q) 5: MergeSort(A, q + 1, r) 6: Merge(A, p, q, r) 7: end if 8: end procedure Nota. Attenzione a come implementare il vettore di supporto B in C! 1: procedure Merge(A, p, q, r) 2: i p 3: j q + 1 4: k 1 5: while i q and j r do 6: if A[i] A[j] then 7: B[k] A[i] 8: i i + 1 9: else 10: B[k] A[j] 11: j j + 1 12: end if 13: k k + 1 14: end while 15: while i q do 16: B[k] A[i] 17: i i + 1 18: k k + 1 19: end while 20: while j r do 21: B[k] A[j] 22: j j + 1 23: k k + 1 24: end while 25: for k p to r do 26: A[k] B[k p + 1] 27: end for 28: end procedure
QuickSort QuickSort QuickSort: esempio QuickSort: pseudocodice Algoritmo di ordinamento ricorsivo basato sulla tecnica Divide Et Impera. Ideato da Tony Hoare nel 1960, in visita come studente presso la Moscow State University. Oggetto della tesi si dottorato di Robert Sedgewick nel 1975. Originariamente implementato come algoritmo di ordinamento standard nella libreria C di Unix (qsort()). Costo computazionale: Costo pessimo: Θ(n 2 ) Caso medio: Θ(n log n) Caso ottimo: Θ(n log n) Molto veloce in termini pratici, nonostante il caso pessimo quadratico. A differenza del MergeSort, il QuickSort non utilizza memoria ausiliaria (algoritmo in place).
QuickSort: esempio intuitivo QuickSort QuickSort: esempio QuickSort: pseudocodice
QuickSort: esempio intuitivo QuickSort QuickSort: esempio QuickSort: pseudocodice Scegliamo un elemento pivot random (4).
QuickSort: esempio intuitivo QuickSort QuickSort: esempio QuickSort: pseudocodice Scegliamo un elemento pivot random (4). Riorganizziamo attorno al pivot: x < pivot a sinistra, x pivot a destra. 3 2 1 6 4 5
QuickSort: esempio intuitivo QuickSort QuickSort: esempio QuickSort: pseudocodice Scegliamo un elemento pivot random (4). Riorganizziamo attorno al pivot: x < pivot a sinistra, x pivot a destra. 3 2 1 6 4 5 Riordiniamo indipendentemente i due sotto-array (chiamate ricorsive). 1 2 3 4 5 6
QuickSort: esempio intuitivo QuickSort QuickSort: esempio QuickSort: pseudocodice Scegliamo un elemento pivot random (4). Riorganizziamo attorno al pivot: x < pivot a sinistra, x pivot a destra. 3 2 1 6 4 5 Riordiniamo indipendentemente i due sotto-array (chiamate ricorsive). 1 2 3 4 5 6 Lista ordinata. 1 2 3 4 5 6
QuickSort: pseudocodice QuickSort QuickSort: esempio QuickSort: pseudocodice 1: procedure QuickSort(A, p, r) 2: if p < r then 3: q Partition(A, p, r) 4: QuickSort(A, p, q) 5: QuickSort(A, q + 1, r) 6: end if 7: end procedure 1: function Pivot(A, p, r) 2: return random number in [p,.., r] 3: end function 1: procedure Swap(A, i, j) 2: tmp A[i] 3: A[i] A[j] 4: A[j] tmp 5: end procedure 1: function Partition(A, p, r) 2: i p 3: j r 4: q Pivot(A, p, r) 5: Swap(A, q, p) 6: pivot A[p] 7: while i < j do 8: while j > p and pivot A[j] do 9: j j 1 10: end while 11: while i < r and pivot > A[i] do 12: i i + 1 13: end while 14: if i < j then 15: Swap(A, i, j) 16: end if 17: end while 18: Swap(A,p,j) 19: return j 20: end function Nota. Codice C per generare un intero random tra p e r: p + rand()%(r p + 1).
La libreria standard del C (stdlib.h) mette a disposizione una serie di implementazioni efficienti di algoritmi ordinamento In particolare: quicksort, mergesort e heapsort L interfaccia di tali implementazioni è sufficientemente generica da poter essere utilizzata con qualsiasi tipo di array di strutture La funzione di confronto tra elementi nella lista da ordinare deve essere definita dall utente
MergeSort/QuickSort in stdlib.h void qsort(void *base, size t nel, size t width, int (*compar)(const void *, const void *)); void mergesort(void *base, size t nel, size t width, int (*compar)(const void *, const void *)); base: puntatore al primo elemento della lista (convertito ad un puntatore ) nel: numero di elementi nella lista width: dimensione in byte di ogni elemento nella lista compar: puntatore ad una funzione che confronta due elementi. La funzione deve restituire un valore minore di zero, se il primo elemento deve essere posizionato prima del secondo nell ordinamento zero, se i due elementi hanno la stessa posizione nell ordinamento un valore maggiore di zero, se il primo elemento deve essere posizionato dopo il secondo nell ordinamento
MergeSort/QuickSort in stdlib.h: esempio di utilizzo con liste di interi # include <stdio.h> # include <stdlib.h> int * readlist ( char * file, int *n) { // Procedura per leggere una lista di interi } int int_compare ( const void *v1, const void * v2) { return (*( int *) v1 - *( int *) v2); } int main ( int argc, char * argv []) { int * list,i,n; list = readlist ( argv [1],& n); qsort (list,n, sizeof ( int ),int_compare ); /* mergesort (list,n, sizeof ( int ),int_compare ); */ for (i=0; i<n; i++) printf ("% d ", list [i]); printf ("\ n"); return 0; }
Confrontare i tempi di calcolo delle vostre implementazioni di mergesort e quicksort con le implementazioni fornite nella libreria C. In particolare, confrontare rispetto a Una lista (sufficientemente grande) di numeri interi generata in modo random Una lista (sufficientemente grande) di 0 e 1 generata in modo random
Nota: come generare una lista di numeri random # include <stdio.h> # include < stdlib.h> # include <time.h> int main ( int argc, char * argv []) { int i, max, size ; if( argc!=3) { fprintf ( stderr," Usage : randlist <maxint > <size >\ n"); return 1; } max=atoi ( argv [1]) ; size=atoi ( argv [2]) ; /* Inizializza il seed del generatore di numeri casuali utilizzando l orologio di sistema. */ srand ( time ( NULL )); for (i=0; i<size ; i++) printf ("% d ", rand () %( max +1) ); printf ("\ n"); return 0; }
Nota: come calcolare il tempo di CPU in un programma C # include <stdio.h> # include < stdlib.h> # include <time.h> int main ( int argc, char * argv []) { clock_t start, end ; start = clock (); // Codice della procedura end = clock (); printf (" Time : %f sec \n",( double )(end - start )/ CLOCKS_PER_SEC ); return 0; }