Laboratorio di Algoritmi e Strutture Dati II Semestre 2005/2006 Ordinamenti Marco Antoniotti Il problema dell’ordinamento • Dato un insieme di “dati” (strutturati) ordinarli in senso crescente o decrescente sulla base di una loro caratteristica • Esempi – Lista di studenti del corso da ordinare alfabeticamente – Lista di chiamate ad un cellulare da ordinare per durata – Lista di transazioni finanziarie (e.g. acquisti e vendite di pacchetti azionari) da ordinare per tempo • È necessario che la relazione d’ordine tra due elementi (od una loro caratteristica) sia totale II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 1 1 Il problema della genericità • Obiettivo: scrivere una libreria generica che possa ordinare qualsiasi tipo di dato • “Callbacks” – Il programma “cliente” passa un array di oggetti alla libreria – La libreria chiama • Implementazione dei “callbacks” – – – – – C C++ Java C# Lisp II Semestre 2005/2006 puntatore a funzione funtori interfacce “delegates” funzioni di prima classe Laboratorio Algoritmi - Marco Antoniotti 2 Puntatori a funzioni in C • Si aggira il sistema di tipizzazione del C dichiarando una funzione di ordinamento come void sorting_sort(void* base, int n, size_t element_size, int (*compare)(void*, void*)) II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 3 2 L’interfaccia dichiarata del puntatore compare • L’operazione di confronto di due elementi è fondamentale per molti algoritmi di ordinamento • La funzione (puntata da) compare e chiamata come compare(x, y) deve ritornare i seguenti valori – Un numero negativo se x < y – Un numero positivo se x > y – 0 se x è uguale ad y • Consistenza: è responsabilità del programmatore garantire che la funzione puntata da compare implementi un ordinamento totale – Transitività: se a < b e b < c allora a < c – Tricotomia: solo uno dei casi a < b, b < a, o a = b è vero II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 4 Esempio: confronto di date • Supponiamo di avere la seguente definizione typedef struct _data { int anno; int mese; int giorno; } Data; • Una definizione valida da passare come compare è int confronto_date(void * pd1, void * pd2) { Data* d1 = (Data*)pd1; /* Riconversione. */ Data* d2 = (Data*)pd2; /* Riconversione. */ if (d1->anno < if (d1->anno > if (d1->mese < if (d1->mese > if (d1->giorno if (d1->giorno return 0; d2->anno) return -1; d2->anno) return +1; d2->mese) return -1; d2->mese) return +1; < d2->giorno) return -1; > d2->giorno) return +1; } II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 5 3 Esempio: confronto di date • Il codice /* Cinque date importanti */ Data date[5] = {{1947, 12, 27}, {1945, 4, 25}, {1948, 12, 10}, {2006, 4, 9}, {1946, 6, 2} }; qsort((void*) date, 5, sizeof(Data), confronto_date); riordina l’array date II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 6 Costruzione di funzioni di ordinamento generiche in C • Ma come possiamo costruire delle funzioni di ordinamento generiche in C? – Scambio di elementi in un array Uno dei problemi che sorgono riguarda la funzione di scambio di due valori in un array • La soluzione in C utilizza la funzione di libreria di basso livello memcpy, coordinata con il passaggio delle dimensioni dell’elemento nell’array void memcpy(void * dest, const void * src, size_t size); void exchange(void * data, int i, int j, size_t item_size) { void * temp = (void*) malloc(item_size); int is = i * item_size; int js = j * item_size; assert(temp); memcpy(temp, data + is, item_size); memcpy(data + is, data + js, item_size); memcpy(data + js, temp, item_size); free(temp); } II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 7 4 Costruzione di funzioni di ordinamento generiche in C • L’utilizzo di memcpy e di funzioni simili richiede molta attenzione • Passando un valore scorretto come dimensione degli elementi dell’array si possono causare (e quasi sicuramente si causano) errori di esecuzione • Tutti gli altri linguaggi di livello più alto hanno a disposizione dei meccanismi per evitare questo tipo di manipolazioni di basso livello II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 8 Costruzione di funzioni di ordinamento generiche in C: bubble sort • • Vediamo un’implementazione generica in C di bubble sort da zero L’algoritmo fa le seguenti cose – – – – Scandisce l’array da sinistra a destra Gli elementi alla destra di ↑ non vengono toccati Invariante: gli elementi alla sinistra di ↑ sono in ordine crescente Ciclo interno: scambia ripetutamente l’elemento ↑ con l’elemento alla sua sinistra In ordine II Semestre 2005/2006 Ancora non visti In ordine Ancora non visti Laboratorio Algoritmi - Marco Antoniotti 9 5 Costruzione di funzioni di ordinamento generiche in C: bubble sort • Esempio II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 10 Costruzione di funzioni di ordinamento generiche in C: bubble sort • Implementazione – Supponiamo di avere a disposizione la funzione generica exchange ed un tipo di puntatore a funzione sorting_cmp_fun_t typedef int (*sorting_cmp_fun_t)(void *, void *); – Il codice generico di bubble sort è void sorting_bubble(void* data, int n_items, size_t item_size, sorting_cmp_fun_t compare) { int i = 0; int j = 0; for (; i < n_items; i++) for (j = i; j > 0; j--) { if (0 > compare(data + (j * item_size), data + ((j - 1) * item_size))) exchange(data, j, j - 1, item_size); else break; } } II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 11 6 Costruzione di funzioni di ordinamento generiche in C: bubble sort • La chiamata di bubble sort con il nostro array di date è la seguente sorting_bubble((void *) date, 5, sizeof(Data), confronta_date); • Il codice seguente si comporta come previsto (fprintf_data stampa una data in maniera leggibile) fputs("Date iniziali\n", stdout); for (i = 0; i < 5; i++) { fprint_data(stdout, &date[i]); fputc('\n', stdout); } sorting_bubble((void*) date, 5, sizeof(Data), confronta_date); fputs("Date ordinate\n", stdout); for (i = 0; i < 5; i++) { fprint_data(stdout, &date[i]); fputc('\n', stdout); } II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 12 Costruzione di funzioni di ordinamento generiche in C: selection sort • • Vediamo un’implementazione generica in C di selection sort da zero L’algoritmo fa le seguenti cose – – – Scandisce l’array da sinistra a destra Gli elementi alla sinistra di ↑ sono fissati in ordine crescente Invariante: nessun elemento alla destra di ↑ è più grande degli elementi alla sinistra In ordine finale ↑ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 13 7 Costruzione di funzioni di ordinamento generiche in C: selection sort • Esempio II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 14 Costruzione di funzioni di ordinamento generiche in C: selection sort • • Ciclo interno Si identifica l’elemento minimo int min = i; int j = i + 1; for (; j < n_items; j++) if (0 > compare(key, data + (j * item_size), data + (min * item_size))) min = j; • Si effettua lo scambio exchange((void*) data, i, min, sizeof(…)) II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 15 8 Costruzione di funzioni di ordinamento generiche in C: selection sort • Implementazione – Come prima, supponiamo di avere a disposizione la funzione generica exchange ed un tipo di puntatore a funzione sorting_cmp_fun_t typedef int (*sorting_cmp_fun_t)(void *, void *); – Il codice generico di selection sort è void sorting_selection(void* data, int n_items, size_t item_size, sorting_cmp_fun_t compare) { int i = 0; int j = 0; for (; i < n_items; i++) { int min = i; int j = i + 1; for (; j < n_items; j++) if (0 > compare(key, data + (j * item_size), data + (min * item_size))) min = j; exchange((void*) data, i, min, item_size); } } II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 16 Analisi dei due algoritmi • Assunzione: input casuale, quindi caso “medio” • Selection sort – Cerca sempre nella sua parte destra • Confronti • Scambi (1 + 2 + … + N) = N(N-1)/2 N • Bubble sort – Ogni elemento si muove di metà • Confronti • Scambi II Semestre 2005/2006 (1 + 2 + … + N)/2 = N(N-1)/4 (1 + 2 + … + N)/2 = N(N-1)/4 Laboratorio Algoritmi - Marco Antoniotti 17 9 Sommario • La costruzione di librerie generiche in C richiede attenzione e disciplina • Due algoritmi semplici di ordinamento e loro analisi – Bubble sort – Selection sort II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 18 10