Selection sort: miglioramenti
Riprendiamo la nostra implementazione del selection sort finora, cercando di approfondire alcuni aspetti ed apportare delle migliorie.
Ordinamento sul posto
Eravamo arrivati a questo punto:
my_list = [7, 12, 18, 5, 6]
for _ in range(len(my_list)):
min_val = min(my_list)
sorted_list.append(min_val)
my_list.remove(min_val)
Per ordinare la lista, abbiamo creato una nuova lista di appoggio sorted_list
. Se l'array è molto grande questo potrebbe essere un problema.
In effetti, pensandoci, quando abbiamo ordinato il mazzo di carte non abbiamo creato una copia del mazzo, ma abbiamo spostato la posizione delle carte in modo che il mazzo di partenza fosse ordinato. Questo tipo di ordinamento che modifica direttamente l'array in input si chiama ordinamento sul posto (in inglese in place).
Non ripeteremo qui la soluzione originale che abbiamo svolto in laboratorio, potete trovare tutto il codice qui.
Calcolo della complessità temporale
Cerchiamo di calcolare perché questo algoritmo impiega un O(n2) operazioni per ordinare l'array.
Analizziamo il codice, ricordando che len(my_list)
è il numero di elementi della lista e quindi esattamente il nostro n.
# Qui c'è un for: le operazioni al suo interno
# vengono ripetute per n volte
for _ in range(len(my_list)):
# per trovare il minimo, bisogna scorrere tutta
# la lista, quindi servono circa n operazioni
min_val = min(my_list)
# append è un'operazione singola
sorted_list.append(min_val)
# remove può essere considerata come un'operazione singola
my_list.remove(min_val)
Quindi, il numero di operazioni è:
- n operazioni per min
- 1 operazione per append
- 1 operazione per remove
- il tutto ripetuto per n volte
Mettiamo insieme le cose e abbiamo:
complessità temporale = (n+1+1)*n = (n+2)*n = n2+2*n
OK, ci siamo quasi. La nostra complessità ha due addendi:
- n2
- 2*n
Quando n diventa molto grande, il primo termine cresce molto di più del secondo, e quindi il secondo si può trascurare.
Per dimostrazione, facciamo una prova ad esempio con n = 1000
:
- n2 = 10002 = 1 milione
- 2*n = 2000
Considerando che stiamo facendo dei calcoli approssimati, possiamo tranquillamente trascurare 2000 rispetto ad un milione!
Per la complessità temporale consideriamo solo il primo termine, e quindi otteniamo:
complessità temporale = O(n2)
Caso migliore, peggiore e medio
La complessità di un algoritmo di ordinamento potrebbe dipendere da alcuni fattori, come ad esempio se gli elementi della lista sono già ordinati, se sono quasi ordinati (cioé la maggior parte degli elementi sono ordinati e solo qualcuno è fuori posto), o completamente casuali.
Si distinguono quindi tre casi di complessità:
- caso migliore: il minimo valore possibile di complessità
- caso peggiore: il massimo valore possibile di complessità
- caso medio: un valore che mediamente assume la complessità
Per l'algoritmo di selection sort, il calcolo della complessità temporale non dipende da come è fatta al suo interno la lista: in ogni caso ci fa sempre lo stesso numero di operazioni.
Recap
Selection sort | complessità temporale |
---|---|
caso migliore | O(n2) |
caso peggiore | O(n2) |
caso medio | O(n2) |