Teoria+Esercizi di Sistemi
Algoritmi di ordinamento
Parte I. Teoria
1
Introduzione
L’ordinamento di una sequenza di numeri (o di stringhe) è un problema solo apparentemente semplice,
per un motivo principalmente pratico.
Gli ordinamenti sono spesso usati in applicazioni che hanno bisogno di un risultato in tempo reale
oppure in tempi ridotti (interfacce grafiche o database), quindi l’algoritmo di ordinamento usato deve
essere molto veloce. La lentezza degli algoritmi più banali ha spesso portato all’elaborazione di nuovi
algoritmi, tanto che oggi se ne contano almeno una decina.
Tratteremo approfonditamente solo gli algoritmi più semplici. Tutti questi algoritmi operano sugli
array.
2
Algoritmi principali
2.1
Bubble Sort
Il B S (ordinamento a bolla) è uno dei più semplici algoritmi di ordinamento, ma è anche uno
dei più lenti.
2.1.1
Algoritmo standard
1. Assumiamo che nessun elemento nell’array sia in ordine.
5
2
9
1
4
2. Consideriamo i primi due elementi dell’array.
5
2
9
1
4
3. Li confronto: se il primo è maggiore del secondo, li scambio.
5>2, perciò:
5
2
9
1
4
2
5
9
1
4
4. Considero il paio di elementi successivo e li scambio come al passaggio 3. Vado avanti così finchè
non ho finito di leggere l’array.
2
5
9
1
4
2
5
9
1
4
2
5
1
1
9
4
2
5
1
4
9
5. Ora, l’elemento più grande dell’array è sicuramente ordinato in fondo; infatti, essendo maggiore di
tutti gli altri, è stato sempre scambiato fino alla fine dell’array. Ripartiamo dal punto 2, considerando
la parte di array non ordinata, a meno che tutti gli elementi tranne uno non siano già ordinati.
2
5
1
4
9
2
1
4
5
9
1
2
4
5
9
1
2
4
5
9
6. Se tutti gli altri elementi sono ordinati anche questo è ordinato. Quindi l’array è ordinato.
1
2
4
5
9
Lepri, tartarughe e bolle Come si può vedere, la lettura dell’array procede lentamente in più passaggi;
il percorso somiglia a quello di una bolla d’aria che, dal fondo, lentamente arriva alla superficie dell’acqua.
Il nome dell’algoritmo deriva da questa caratteristica.
Inoltre, gli elementi che devono migrare verso la fine dell’array si spostano più velocemente, perchè
il percorso che devono seguire è lo stesso di quello compiuto dall’algoritmo; per questo, tali elementi
vengono chiamati lepri. Gli elementi che invece si devono spostare nella direzione contraria sono molto
lenti, perciò vengono chiamati tartarughe.
2.1.2
Varianti
L’estrema semplicità del Bubble Sort controbilancia la sua lentezza: infatti è spesso usato su array piccoli.
Per migliorare la velocità dell’algoritmo sono nate diverse varianti:
• Al posto di interrompere il processo quando non rimangono implicitamente elementi da ordinare, viene svolto un controllo esplicito: l’algoritmo si ferma quando non vengono registrati scambi
durante la lettura.
• Invece di leggere l’array a senso unico, la lettura viene eseguita a senso alternato. Questo accorgimento uniforma la velocità delle tartarughe a quella delle lepri.
2.1.3
Implementazione in Pascal
program BubbleSort;
const
MAX = 32;
var
i, j: integer;
temp: integer;
Vett: array [1..MAX] of integer;
begin
{Riempimento dell'array}
randomize;
for i:=1 to MAX do Vett[i] := random(128);
{Ordinamento}
for i:=MAX-1 downto 1 do
for j:=1 to i do
if Vett[j] > Vett[j+1] then begin
temp := Vett[j];
Vett[j] := Vett[j+1];
Vett[j+1] := temp;
end;
2
{Scrittura dell'array}
for i:=1 to MAX do write(Vett[i]:4);
end.
2.2
Selection Sort
Il S S (ordinamento a selezione), talvolta abbreviato in S-S, è un altro semplice algoritmo. È molto lento, ma necessita di scambiare i valori meno spesso rispetto al Bubble Sort, quindi,
nella pratica, è leggermente più veloce.
2.2.1
Algoritmo Standard
1. Assumiamo che nessun elemento nell’array sia in ordine.
5
2
9
1
4
2. Operiamo sul primo elemento dell’array.
5
2
9
1
4
3. Confronto il primo elemento con quelli successivi. Se il primo elemento è maggiore del secondo,
li inverto prima di continuare.
5
2
9
1
4
2
5
9
1
4
2
5
9
1
4
1
5
9
2
4
4. A questo punto il primo elemento è in ordine.
1
5
9
2
4
5. Torno al passaggio 3 operando sull’elemento successivo, a meno che non sia l’ultimo elemento.
1
5
9
2
4
1
2
9
5
4
1
2
4
9
5
1
2
4
5
9
6. Siccome tutti gli altri elementi sono in ordine, anche l’ultimo elemento è in ordine. Quindi tutto
l’array è ordinato.
1
2.2.2
2
4
5
9
Varianti
Nonostante il leggero (e non costante) vantaggio rispetto al Bubble Sort, il Selection Sort è comunque
molto lento. È possibile migliorarne marginalmente la velocità applicando le seguenti varianti:
• Quando si confronta un elemento con quelli successivi, in linea di massima si sta stabilendo qual
è il minore tra essi. Quindi, al posto di effettuare uno scambio ogni volta che si trova un numero
minore del primo, si può effettuare un solo scambio tra il primo elemento e il minore tra gli elementi
successivi (se viene trovato).
• Normalmente, gli elementi ordinati si accumulano dal minore al maggiore. È possibile accumularli
contemporaneamente anche dal maggiore al minore, leggendo l’array una volta nel senso normale,
una volta nel senso contrario, e così via.
3
2.2.3
Implementazione in Pascal
program SelectionSort;
const
MAX = 256;
var
i, j: integer;
temp: integer;
Vett: array [1..MAX] of integer;
begin
{Riempimento dell'array. Qui usiamo dei numeri casuali.}
randomize;
for i:=1 to MAX do Vett[i] := random(128);
{Ordinamento.}
for i:=1 to MAX-1 do
for j:=i+1 to MAX do
if Vett[i] > Vett[j] then begin
temp := Vett[i];
Vett[i] := Vett[j];
Vett[j] := temp;
end;
{Visualizzazione dell'array ordinato}
for i:=1 to MAX do write(Vett[i]:4);
end.
2.3
Note di applicazione
2.3.1
Ordinamenti su array non di numeri
È chiaro come questi ordinamenti possano lavorare su dati di tipo numerico (in Pascal, di tipo integer
oppure real), ma talvolta è necessario ordinare dati non numerici, come delle stringhe.
In Pascal, gli operatori >, < e = funzionano su qualsiasi tipo di dato, seguendo dei criteri di ordinamento convenzionale. Nella maggior parte dei casi, dato che nella memoria del computer qualsiasi tipo
di dato è memorizzato come un numero, il confronto di variabili non numeriche viene effettuato sulla
rappresentazione in memoria di queste variabili.
Nel caso di char e string, i numeri corrispondenti ai vari caratteri sono stabiliti dallo standard 
(American Standard Code for Information Interchange), e l’ordinamento viene stabilito in base a questo
codice. Vedi la dispensa Teoria+Esercizi di Sistemi: Stringhe, paragrafo 1.2.2, per maggiori dettagli su
come funziona il confronto.
2.3.2
Ordinamenti in senso decrescente
Finora abbiamo visto algoritmi che ordinano un array in ordine crescente, ma non è difficile ottenere, da
questi algoritmi, la loro controparte che ordina in modo decrescente: basta sostituire i segni > con dei <,
e viceversa.
2.3.3
Determinazione del tempo di esecuzione
Per conoscere precisamente quanto tempo ha impiegato un algoritmo di ordinamento a portare a termine
l’operazione, è possibile utilizzare la funzione GetTime:
Utilizzo:
GetTime(Ore, Minuti, Secondi, Centesimi);
4
dove Ore, Minuti, Secondi e Centesimi sono le variabili (rigorosamente di tipo word, variante del tipo
integer) che riceveranno i dati dell’orario in vigore nel momento in cui la funzione viene utilizzata.
La funzione GetTime si trova nell’unità1 Dos, quindi, per poterla usare, è necessario aggiungere la
seguente riga al di sotto della clausola program:
Uses Dos;
Si può chiamare GetTime prima e dopo l’inizio dell’ordinamento, sottraendo poi i due orari, per
sapere quanto tempo ha impiegato il processo ad eseguire.
3
Altri algoritmi
Proponiamo una rapida carrellata di altri algoritmi di ordinamento spesso usati in contesti reali.
3.1
Insertion Sort
Nell’I S (ordinamento ad inserimento) l’array ordinato viene costruito un elemento alla volta.
Ogni elemento viene prelevato in sequenza, confrontato con quelli precedenti, e posizionato tra l’elemento
minore più prossimo e quello maggiore pìù prossimo. Ciò implica che, per ogni spostamento, parte
dell’array dovrà essere spostato verso destra.
3.2
Merge Sort
L’array, nel M S (ordinamento a integrazione) viene suddiviso a metà ripetutamente, fino a ottenere tanti piccoli array di un elemento. Poi, questi array vengono riuniti a cascata, in modo da mantenere,
in ogni passaggio successivo, l’ordine crescente degli elementi. Al termine di questo processo, si ottiene
un unico array ordinato.
3.3
QuickSort
Il QS (ordinamento veloce) consiste nel dividere ripetutamente l’array a metà; ogni volta che viene
divisa una metà, gli elementi minori di quello centrale vengono messi nella metà sinistra, e gli elementi
maggiori alla destra. Ogni metà viene divisa a sua volta finchè non si ottiene una parte lunga meno di tre
elementi. Al termine del processo l’array è ordinato.
Parte II. Esercizi
1. RRRQ Scrivere un programma che metta in ordine crescente una sequenza di 10 numeri, inserita
da tastiera, utilizzando l’algoritmo B S.
2. RRQQ Scrivere un programma che metta in ordine decrescente una sequenza di 15 numeri, scelta
casualmente, utilizzando l’algoritmo S S.
3. RRQQ Scrivere un programma che, messa in ordine crescente una sequenza di 15 numeri, inserita
da tastiera, con un algoritmo a piacere, trovi quante ripetizioni del numero 5 si trovano nell’array.
1
Un’unità o libreria è una collezione di funzioni, già pronte da poter usare. Il Pascal carica, per ogni programma, la libreria
System, che include le funzioni writeln, readln, le stringhe e altro ancora.
5
4. RRQQ Scrivere un programma che ordini una sequenza di 64 numeri, scelta casualmente, in ordine
decrescente, utilizzando sia l’algoritmo S S che l’algoritmo B S, verificando
che entrambi gli algoritmi diano come risultato lo stesso array.
5. RQQQ Scrivere un programma che, mettendo in ordine crescente una sequenza di 64 numeri,
scelta casualmente dal programma, utilizzando l’algoritmo S S, calcoli quanti scambi
sono stati necessari per eseguire l’ordinamento.
6. RQQQ Scrivere un programma che, dati in input da tastiera 10 nomi, li riscriva in ordine alfabetico.
Usare l’algoritmo B S.
7. RQQQ Scrivere un programma che, dopo aver messo in ordine crescente 128 numeri scelti casualmente da 1 a 10, scriva quante volte compare ogni numero nell’array. Usare l’algoritmo S
S.
8. QQQQ Scrivere un programma che, dopo aver messo in ordine crescente 128 numeri scelti casualmente da 1 a 10, scriva quante volte compare ogni numero nell’array. Usare un algoritmo a
piacere.
6