Algoritmi di ordinamento
In questa pagina 7
In questa pagina 7
Problema: disporre gli elementi di una lista in ordine non decrescente. Tutti gli algoritmi qui sotto ordinano per confronti (<, <=), quindi funzionano con numeri, stringhe, tuple.
Proprietà da considerare:
Definizione (proprietà degli algoritmi di ordinamento)
- costo nel caso peggiore, migliore, medio (vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →);
- sul posto (in place): usa solo memoria aggiuntiva;
- stabile: elementi uguali restano nell'ordine relativo di partenza (conta quando si ordina per una chiave, es. studenti per voto).
Esempio. Ordinando [("b", 1), ("a", 1)] per il secondo campo con un algoritmo stabile, ("b", 1) resta prima di ("a", 1).
Selection sort
A ogni passo i si cerca il minimo di v[i:] e lo si scambia con v[i].
def selection_sort(v):
n = len(v)
for i in range(n - 1):
im = i
for j in range(i + 1, n):
if v[j] < v[im]:
im = j
v[i], v[im] = v[im], v[i]Proprietà (selection sort)
- Invariante:
v[:i]contiene gliielementi più piccoli, in ordine.- Confronti: sempre → in tutti i casi. Al più scambi.
- Sul posto, non stabile.
Esempio. Con v = [5, 2, 4] il selection sort esegue confronti e porta v a [2, 4, 5].
Insertion sort
Si scorre la lista; ogni elemento viene inserito al posto giusto nella parte iniziale già ordinata, spostando a destra i maggiori.
def insertion_sort(v):
for i in range(1, len(v)):
x = v[i]
j = i - 1
while j >= 0 and v[j] > x:
v[j + 1] = v[j]
j -= 1
v[j + 1] = xProprietà (insertion sort)
- Invariante:
v[:i]è ordinata (ma contiene gli elementi originali div[:i], non i più piccoli).- Caso peggiore (lista al contrario) ; caso migliore (già ordinata) : molto buono su liste quasi ordinate.
- Sul posto, stabile (grazie a
>stretto nelwhile).
Esempio. L'insertion sort su [1, 2, 3, 4] esegue confronti (), su [4, 3, 2, 1] ne esegue ().
Bubble sort
Si scorrono le coppie adiacenti scambiando quelle fuori ordine; dopo il passo k gli ultimi k elementi sono definitivi. Con un flag ci si ferma se in un passo non ci sono scambi.
def bubble_sort(v):
n = len(v)
for k in range(n - 1):
scambi = False
for j in range(n - 1 - k):
if v[j] > v[j + 1]:
v[j], v[j + 1] = v[j + 1], v[j]
scambi = True
if not scambi:
breakProprietà (bubble sort). nel caso peggiore, se già ordinata; sul posto e stabile. Più lento di insertion sort in pratica.
Esempio. Con il flag, bubble_sort([1, 2, 3]) si ferma dopo un solo passo di confronti: costo .
Merge sort
Divide et impera (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →): si divide la lista a metà, si ordinano ricorsivamente le due metà, si fondono le due metà ordinate (fusione con due indici, vedi Pattern algoritmici iterativiSchemi ricorrenti nei cicli: accumulatore, contatore, massimo e minimo, ricerca con uscita anticipata, verifica universale, filtro e trasformazione, finestra scorrevole, due indici, elaborazione di coppie.Pattern algoritmici iterativi →).
def merge_sort(v):
if len(v) <= 1:
return v
m = len(v) // 2
a = merge_sort(v[:m])
b = merge_sort(v[m:])
# fusione
out, i, j = [], 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: # <= rende l'algoritmo stabile
out.append(a[i]); i += 1
else:
out.append(b[j]); j += 1
out.extend(a[i:])
out.extend(b[j:])
return outProprietà (merge sort: costo e memoria)
- Costo → in tutti i casi: livelli di ricorsione, ciascuno con lavoro complessivo per le fusioni.
- Memoria aggiuntiva (non sul posto); stabile.
- Questa versione restituisce una lista nuova:
v = merge_sort(v).
Esempio. merge_sort([5, 2, 4, 1]) restituisce [1, 2, 4, 5], con livelli di divisione.
Confronto
| Algoritmo | Peggiore | Migliore | Sul posto | Stabile |
|---|---|---|---|---|
| Selection sort | sì | no | ||
| Insertion sort | sì | sì | ||
| Bubble sort (con flag) | sì | sì | ||
| Merge sort | no | sì |
Teorema (limite inferiore per l'ordinamento per confronti). Nessun ordinamento per confronti può fare meglio di nel caso peggiore.
Esempio. Per ordinare elementi servono nel caso peggiore almeno confronti.
In Python: sort e sorted
Definizione (sort e sorted in Python). Nella pratica si usa l'ordinamento predefinito (Timsort: , stabile, su dati già ordinati).
v.sort() # sul posto, restituisce None w = sorted(v) # nuova lista sorted(v, reverse=True) # decrescente sorted(parole, key=str.lower) # ignorando maiuscole/minuscole sorted(studenti, key=lambda s: (-s[1], s[0])) # voto decrescente, poi nome
Esempio. sorted([3, 1, 2], reverse=True) restituisce [3, 2, 1] e sorted(["b", "A", "c"], key=str.lower) restituisce ['A', 'b', 'c'].
key è una funzione applicata a ogni elemento: si confrontano i risultati. La stabilità permette ordinamenti a più livelli in passaggi successivi.
Errori tipici
v = v.sort()(vedi Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →).- Nell'insertion sort, controllare
v[j] > xprima dij >= 0: conj = -1si leggev[-1](l'ultimo elemento) invece di fermarsi. - Merge sort che fonde senza aggiungere i residui (
extend) di una delle due metà.
Versione ripasso
Problema: disporre gli elementi di una lista in ordine non decrescente (per confronti <,<=).
Proprietà:
- costo (peggiore, migliore, medio, vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →);
- sul posto (in place): memoria;
- stabile: preserva l'ordine relativo di elementi uguali.
Selection sort
Cerca il minimo di v[i:] e lo scambia con v[i].
- Confronti: sempre ovunque.
- Sul posto, non stabile.
Insertion sort
Inserisce ogni elemento al posto giusto nella parte iniziale già ordinata, spostando a destra i maggiori.
- Caso peggiore , migliore (liste quasi ordinate).
- Sul posto, stabile.
Bubble sort
Scambia coppie adiacenti fuori ordine; con un flag si ferma se non ci sono scambi.
- Peggiore , migliore (già ordinata).
- Sul posto e stabile.
Merge sort
Divide et impera (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →): divide a metà, ordina ricorsivamente e fonde (Pattern algoritmici iterativiSchemi ricorrenti nei cicli: accumulatore, contatore, massimo e minimo, ricerca con uscita anticipata, verifica universale, filtro e trasformazione, finestra scorrevole, due indici, elaborazione di coppie.Pattern algoritmici iterativi →).
- Costo in tutti i casi.
- Memoria aggiuntiva (non sul posto), stabile.
Tabella riassuntiva
| Algoritmo | Peggiore | Migliore | Sul posto | Stabile |
|---|---|---|---|---|
| Selection sort | sì | no | ||
| Insertion sort | sì | sì | ||
| Bubble sort (con flag) | sì | sì | ||
| Merge sort | no | sì |
Nessun ordinamento per confronti supera nel peggiore dei casi.
In Python: sort e sorted
Timsort: , stabile, su dati già ordinati.
v.sort(): sul posto, restituisceNone.w = sorted(v): nuova lista.sorted(v, reverse=True): decrescente.sorted(v, key=...): confronta i risultati della funzionekey.
Errori tipici
v = v.sort()(vedi Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →).- Nell'insertion sort, controllare
v[j] > xprima dij >= 0(rischiov[-1]). - Dimenticare di aggiungere i residui (
extend) nel merge sort.