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