Esercizio 5crescita delle funzioni e costo degli algoritmi
In questa pagina 10
Testo (questionario di dicembre 2022 e test d'esame di dicembre 2012 e gennaio 2014 del corso di Fondamenti di Informatica UniPD; i quesiti sono indipendenti dal linguaggio). Teoria: 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 →, Algoritmi di ordinamentoSelection sort, insertion sort e bubble sort (quadratici), merge sort (n log n, divide et impera); stabilità, ordinamento sul posto, costo nei vari casi; sort e sorted con key.Algoritmi di ordinamento →, Ricerca lineare e binariaRicerca lineare su sequenze qualsiasi in O(n); ricerca binaria su sequenze ordinate in O(log n), versione iterativa e ricorsiva, invariante e errori di indice; modulo bisect.Ricerca lineare e binaria →.
- Qual è la miglior stima asintotica di ?
- Della funzione dire se è , , , .
- Ordinare per complessità crescente , , .
- Se l'ordinamento per selezione di elementi richiede secondi, quanto servirà per elementi?
- Quale tra con tempo e con tempo è migliore asintoticamente?
- Se , quali delle seguenti affermazioni sono sempre vere: ; ; ; ?
- Un vettore dinamico raddoppia la capacità quando è pieno. Quante copie di elementi si fanno in inserimenti? E se la capacità cresce di una sola unità alla volta? Che cosa significa " ammortizzato"?
- Un programma usa una lista Python come coda: inserisce con
appended estrae conpop(0). Quanto costa svuotarla, e concollections.deque?
1.
Si guarda il termine che cresce più in fretta e si trascurano costanti e termini di ordine inferiore:
- cresce lentamente;
- : ;
- : una costante.
Quindi . Numericamente tende a (con vale ).
2.
Dominante: (gli altri termini sono , e una costante: ). Dunque:
- : vera;
- : vera (cresce almeno come );
- : vera (è un limite superiore non stretto);
- : falsa, perché cresce più di .
Un'affermazione è un limite superiore, non necessariamente stretto: è vero ma poco informativo.
3. Ordine
: .
4. Ordinamento per selezione
L'ordinamento per selezione fa confronti. Con il tempo si moltiplica per (esattamente ): 1000 secondi. (Con mergesort, , il fattore sarebbe circa .)
5. e
: per grande il rapporto ( per ; per , dove il termine pesa). Entrambi sono : dal punto di vista asintotico sono equivalenti. Le costanti ( contro ) e i termini lineari non contano nella notazione .
6. Conseguenze di
- : sempre vera (un limite più largo);
- : sempre vera;
- : non è detto ( la smentisce);
- : non è detto ( è ma non ).
dà un limite dall'alto, dal basso, i due insieme.
7. Vettore dinamico e costo ammortizzato
Una append che trova il vettore pieno copia tutti gli elementi. Con raddoppio le copie avvengono quando : in inserimenti sono . Con incremento di 1 si copia a ogni inserimento: .
| copie con raddoppio | copie con | copie con | ||
|---|---|---|---|---|
| 100 | 127 | 4950 | 460 | 200 |
| 1000 | 1023 | 499 500 | 49 600 | 2000 |
| 10 000 | 16 383 | 49 995 000 | 4 996 000 | 20 000 |
Costo ammortizzato : il costo totale di operazioni è ( copie più scritture), cioè per operazione in media sulla sequenza, anche se una singola operazione può costare . Non è una media su input casuali: vale nel caso peggiore, per qualsiasi sequenza. Un incremento costante non basta: il totale resta quadratico.
8. Coda su lista
pop(0) rimuove il primo elemento e sposta tutti gli altri di un posto: . Svuotare una lista di elementi costa spostamenti ( per , per : il doppio dei dati, quattro volte il lavoro). deque.popleft() è e svuotare la coda costa (Pila e codaPila (LIFO) e coda (FIFO) come ADT: operazioni e costi; realizzazione in Python con list e collections.deque; realizzazione in C con array e indici, coda circolare; applicazioni (parentesi bilanciate, stack delle chiamate, visite).Pila e coda →).
Verifica
import math
def copie(m, nuova_cap):
"""numero di elementi copiati in m inserimenti; nuova_cap(cap) = capacità dopo la crescita"""
cap, n, tot = 1, 0, 0
for _ in range(m):
if n == cap:
tot += n
cap = nuova_cap(cap)
n += 1
return tot
for m in (100, 1000, 10000):
print(m, copie(m, lambda c: 2 * c), copie(m, lambda c: c + 1), copie(m, lambda c: c + 10), 2 * m)
f = lambda n: math.log(n) + 2 * n * math.log(n + 4) + (3 * n + 1) * (n - 1) / n ** 2
print(f(10 ** 6) / (10 ** 6 * math.log(10 ** 6))) # 2.0000017962...
T = lambda n: 3 * n + 10 * math.log(math.log(n)) + n * math.log(n) + 8
print(T(10 ** 8) / (10 ** 8 * math.log(10 ** 8))) # 1.1628...: tende a 1
sel = lambda n: n * (n - 1) // 2 # confronti del selection sort
print(sel(10 ** 5) / sel(10 ** 4)) # 100.009
A0 = lambda n: 5 * n * n
A1 = lambda n: n * (n - 1000) + 7 * n
print(A1(10 ** 7) / A0(10 ** 7)) # 0.19998...: costante, stesso ordine
spostamenti = lambda n: sum(n - 1 - i for i in range(n)) # svuotare una lista con pop(0)
print(spostamenti(1000), spostamenti(2000)) # 499500 1999000Errori comuni
- Tenere le costanti o i termini di ordine inferiore nella risposta ( invece di ).
- Confondere (limite superiore) con (limite stretto).
- Dire che un algoritmo "da " è peggiore di uno "da ": stessa classe di crescita.
- Credere che ammortizzato voglia dire "veloce in media su dati casuali".
- Usare
list.pop(0)per fare una coda.
Versione ripasso
Questionario di dicembre 2022 e test di dicembre 2012 e gennaio 2014 (UniPD). Teoria: 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 →, Algoritmi di ordinamentoSelection sort, insertion sort e bubble sort (quadratici), merge sort (n log n, divide et impera); stabilità, ordinamento sul posto, costo nei vari casi; sort e sorted con key.Algoritmi di ordinamento →.
- Si tiene il termine dominante, senza costanti: (l'ultimo termine tende a 3). : , , ; non .
- Ordine crescente: .
- Selezione : moltiplica il tempo per 100 (); mergesort per .
- e sono entrambi : equivalenti asintoticamente.
- implica e , non né .
- Vettore dinamico: raddoppio → in inserimenti copie, ammortizzato (vale nel caso peggiore per ogni sequenza); incremento costante → copie (: 1023 contro 499 500).
list.pop(0)è : svuotare la lista costa ;deque.popleft()(Pila e codaPila (LIFO) e coda (FIFO) come ADT: operazioni e costi; realizzazione in Python con list e collections.deque; realizzazione in C con array e indici, coda circolare; applicazioni (parentesi bilanciate, stack delle chiamate, visite).Pila e coda →).
Errori comuni: costanti nel risultato; confuso con ; "peggiore" di ; ammortizzato inteso come caso medio; pop(0) per le code.