Algoritmi di ordinamento
In questa pagina 9
Problema. Input: sequenza di chiavi confrontabili. Output: gli stessi elementi in ordine crescente (vedi Problemi computazionali e algoritmiProblema computazionale come insieme di coppie (istanza, soluzione); algoritmo e modello di calcolo RAM; pseudocodice; taglia di un'istanza; ADT e struttura dati concreta; esempio svolto con ricerca lineare e binaria in un array ordinato.Problemi computazionali e algoritmi →). La taglia è . Qui si studiano algoritmi basati su confronti (le chiavi si usano solo con , , ).
Nota sulle fonti: insertion sort e selection sort compaiono nel materiale del corso (esercizi su complessità e invarianti); merge sort, quick sort, heap sort e il limite inferiore sono scritti dal programma ufficiale con conoscenze standard.
Selection sort
Si cerca il minimo della parte non ordinata e lo si porta in posizione .
Algoritmo selectionSort(S, n)
for i <- 0 to n-2 do
minIndex <- i
for k <- i+1 to n-1 do
if S[k] < S[minIndex] then minIndex <- k
swap(S[i], S[minIndex])Invariante all'inizio dell'iterazione : contiene gli elementi minori, già in ordine crescente (e ciascuno tutti gli elementi di ). Alla fine e tutto è ordinato. Costo: il ciclo interno fa confronti, in totale per ogni istanza, anche già ordinata. Corrisponde alla coda con priorità su lista non ordinata (vedi Code con prioritàEntry chiave-valore; ADT coda con priorità (insert, min, removeMin) con chiave minima = priorità massima; esempio di esecuzione; applicazioni; implementazioni con lista non ordinata e ordinata e relativi costi; ordinamento tramite coda con priorità.Code con priorità →).
Insertion sort
Si mantiene ordinato un prefisso e si inserisce al posto giusto spostando a destra i maggiori (pseudocodice e analisi in Complessità in tempo e caso pessimoPerché lo studio sperimentale non basta; complessità al caso pessimo come massimo sul numero di operazioni tra le istanze di una data taglia; stima con limiti superiore e inferiore senza trovare l'istanza peggiore; esempi arrayMax, prefixAverages e InsertionSort; efficienza asintotica e limiti dell'analisi.Complessità in tempo e caso pessimo →). Invariante: all'inizio dell'iterazione , è ordinato (e contiene gli elementi originali di ). Caso pessimo (array decrescente), caso migliore (array ordinato), veloce su sequenze quasi ordinate.
Merge sort
Divide et impera: si divide in due metà, si ordinano ricorsivamente, si fondono le due sequenze ordinate con un solo passaggio.
Algoritmo mergeSort(S)
Input: sequenza S di n elementi Output: S ordinata
if n <= 1 then return S
metà <- floor(n/2)
L <- mergeSort(S[0..metà-1]); R <- mergeSort(S[metà..n-1])
return merge(L, R)merge(L, R) confronta le due teste e sposta la minore nell'output: . Costo: l'albero della ricorsione ha livelli e al livello le chiamate lavorano su sequenze di taglia , con costo totale per livello (il costo di un nodo è la fusione): per ogni istanza. Richiede di spazio ausiliario. È stabile se in caso di parità si prende l'elemento di sinistra (verificato: per già ordinato i confronti sono ).
Quick sort
Si sceglie un pivot (ad esempio l'ultimo elemento), si partiziona in elementi e in loco con un passaggio, si mette il pivot al suo posto definitivo e si ordinano ricorsivamente le due parti.
Algoritmo quickSort(S, lo, hi)
if lo >= hi then return
p <- S[hi]; i <- lo
for j <- lo to hi-1 do
if S[j] < p then swap(S[i], S[j]); i <- i + 1
swap(S[i], S[hi])
quickSort(S, lo, i-1); quickSort(S, i+1, hi)Invariante del ciclo di partizione: . Costo: per partizione. Se il pivot divide in parti bilanciate l'albero ha altezza e il costo è ; se è sempre il minimo o il massimo (array già ordinato con pivot ultimo) il costo è . Con pivot scelto a caso il tempo atteso è : è l'esempio classico di caso pessimo patologico (vedi Complessità in tempo e caso pessimoPerché lo studio sperimentale non basta; complessità al caso pessimo come massimo sul numero di operazioni tra le istanze di una data taglia; stima con limiti superiore e inferiore senza trovare l'istanza peggiore; esempi arrayMax, prefixAverages e InsertionSort; efficienza asintotica e limiti dell'analisi.Complessità in tempo e caso pessimo →). Spazio ausiliario attesi (stack).
Heap sort
Si costruisce uno heap sull'array e si estrae ripetutamente il massimo (heap sort con max-heap) mettendolo in fondo all'array: in loco.
bottom-upheap-construction: (vedi Costruzione di uno heapCostruire uno heap da un array di n entry: approccio top-down con n-1 insert, Theta(n log n); approccio bottom-up con down-heap dalle foglie verso la radice, Theta(n), con dimostrazione della somma; esempi svolti, in loco; unione di due alberi con heap-order.Costruzione di uno heap →);- per fino a : scambia con e fai down-heap su : passi da .
Totale per ogni istanza e di spazio ausiliario. Equivale all'ordinamento con coda con priorità su heap (vedi HeapAlbero binario completo e sua altezza floor(log2 n); heap = albero completo con heap-order property; proprietà (radice minima, cammini non decrescenti); rappresentazione su array con level numbering; insert con up-heap bubbling, removeMin con down-heap bubbling, rimozione di una entry qualsiasi; invarianti e costi Theta(log n); esempio svolto.Heap →).
Ordinare senza confrontare
Se le chiavi sono interi in si può ordinare (o contare) in con un array di contatori : una scansione che fa e una scansione di . Non usa confronti, quindi non è soggetto al limite sotto. Con è lineare (si usa nell'esercizio della mediana di chiavi in ).
Limite inferiore
Teorema. Ogni algoritmo di ordinamento basato su confronti richiede confronti al caso pessimo.
Dimostrazione. L'esecuzione su ogni istanza è descritta da un albero di decisione binario: ogni nodo interno è un confronto , ogni foglia una permutazione dell'input prodotta come risposta. Per essere corretto l'algoritmo deve poter produrre ognuna delle permutazioni, quindi l'albero ha almeno foglie. Un albero binario con foglie ha altezza (relazione , vedi Alberi binariAlbero binario e albero binario proprio; interfaccia; relazioni tra nodi, foglie e altezza (m = n-m+1, h+1 <= m <= 2^h, 2h+1 <= n <= 2^(h+1)-1) con dimostrazioni; visita inorder; parse tree e valutazione di espressioni; heightSum come esempio di calcolo di un'informazione più ricca.Alberi binari →), perciò l'altezza è . Poiché , . L'altezza dell'albero è il numero di confronti nel caso peggiore.
Quindi merge sort e heap sort sono ottimi (asintoticamente) tra gli algoritmi basati su confronti.
Riepilogo
| Algoritmo | Caso pessimo | Caso migliore | Spazio ausiliario | In loco |
|---|---|---|---|---|
| selection sort | sì | |||
| insertion sort | sì | |||
| merge sort | no | |||
| quick sort | (atteso ) | sì | ||
| heap sort | sì |
Errori comuni
- Dire che il quick sort è al caso pessimo: lo è atteso (o con buon pivot); al caso pessimo è quadratico.
- Confondere il caso pessimo con il caso migliore dell'insertion sort (lineare solo su array ordinato).
- Dimenticare che l'invariante di selection sort riguarda anche i valori, non solo l'ordine: il prefisso contiene gli elementi più piccoli.
- Applicare il limite ad algoritmi che non usano confronti.
Versione ripasso
- Problema: ordinare ; algoritmi basati su confronti. Fonti: insertion e selection dal materiale, il resto dal programma ufficiale.
- Selection sort: minimo del suffisso in posizione ; invariante: = gli minimi ordinati; confronti sempre, (= coda con priorità su lista non ordinata, vedi Code con prioritàEntry chiave-valore; ADT coda con priorità (insert, min, removeMin) con chiave minima = priorità massima; esempio di esecuzione; applicazioni; implementazioni con lista non ordinata e ordinata e relativi costi; ordinamento tramite coda con priorità.Code con priorità →).
- Insertion sort: invariante ordinato; pessimo , migliore (vedi Complessità in tempo e caso pessimoPerché lo studio sperimentale non basta; complessità al caso pessimo come massimo sul numero di operazioni tra le istanze di una data taglia; stima con limiti superiore e inferiore senza trovare l'istanza peggiore; esempi arrayMax, prefixAverages e InsertionSort; efficienza asintotica e limiti dell'analisi.Complessità in tempo e caso pessimo →).
- Merge sort: dividi, ordina le metà, fondi in ; per ogni istanza (livelli , costo per livello); spazio .
- Quick sort: pivot e partizione in loco (), per partizione; pessimo (pivot minimo/massimo), atteso .
- Heap sort: bottom-up + down-heap, , spazio (vedi Costruzione di uno heapCostruire uno heap da un array di n entry: approccio top-down con n-1 insert, Theta(n log n); approccio bottom-up con down-heap dalle foglie verso la radice, Theta(n), con dimostrazione della somma; esempi svolti, in loco; unione di due alberi con heap-order.Costruzione di uno heap →).
- Senza confronti: interi in con contatori, .
- Limite inferiore: albero di decisione con foglie, altezza , ; merge e heap sort sono ottimi.
- Pseudocodice selection sort: per : cerca
minIndexin ,swap(S[i], S[minIndex]). Quick sort: pivot ;for j = lo..hi-1: if S[j] < p then swap(S[i], S[j]); i++;swap(S[i], S[hi]); ricorsione su e . - Merge sort:
mergeconfronta le teste di e e sposta la minore; livelli , costo per livello. - Errori: quick sort al caso pessimo; migliore e pessimo dell'insertion sort scambiati; limite applicato ad algoritmi senza confronti.