Complessità e Notazione Asintotica
Quando si confrontano algoritmi, determinare
il tempo di esecuzione
-È complicato
-Contiene dettagli inutili che vorremmo
ignorare
-Dipende da costanti non note
vogliamo darne una visione
più astratta (tasso di crescita)
Paragonare tra loro algoritmi
Abbiamo una scala di complessità:
...
2n
...
n3
n2
n log n
n
log n
1
vogliamo inserire ogni
algoritmo in questa scala
Un algoritmo può richiedere tempi diversi per input
della stessa taglia. Ad esempio il tempo per ordinare
n oggetti può dipendere dal loro ordine iniziale.
...
2n
...
n3
n2
n log n
n
log n
1
complessità massima
complessità media
complessità minima
Nell’analizzare la complessità tempo di un
algoritmo siamo interessati a come aumenta
il tempo al crescere della taglia n dell’input.
Siccome per valori “piccoli” di n il tempo
richiesto è comunque poco, ci interessa
soprattutto il comportamento per valori
“grandi” di n (il comportamento asintotico)
Inoltre, siccome la velocità del processore
influisce sul tempo calcolo per una costante
moltiplicativa noi valuteremo la complessità
a meno di una tale costante.
Questo giustifica le seguenti definizioni:
Notazione asintotica O
(limite superiore asintotico)
( g (n)) { f (n) : esistono c 0 ed n0 tali che
0 f (n) cg (n) per ogni n n0 }
cg (n)
f (n)
n0
O(g(n))
Scriveremo f(n) = O(g(n)) per dire che f(n) è
una delle funzioni dell’insieme O(g(n))
f(n) = O(g(n))
si legge:
f(n) è “o grande” di g(n)
Se f(n) = O(g(n)) rappresenta il tempo calcolo
richiesto da un algoritmo diciamo che
O(g(n)) è un limite superiore asintotico per la
complessità tempo di tale algoritmo.
esempi
f (n) 2n 5n 5 O(n )
2
2
infatti 0 2n 5n 5 cn
per c = 4 ed n0 = 5
Vedremo che in generale per a2 > 0
2
2
f (n) a2 n a1n a0 O(n )
2
f (n) 2 sin n O(1)
infatti 0 2 sin n c 1
per c = 3 ed n0 = 1
2
Notazione asintotica .
(limite inferiore asintotico)
( g (n)) { f (n) : esistono c 0 ed n0 tali che
f (n) cg (n) 0 per ogni n n0 }
f (n)
cg (n)
n0
Scriveremo f(n) = (g(n)) per dire che f(n) è
una delle funzioni dell’insieme (g(n)).
f(n) = (g(n))
si legge:
f(n) è “omega” di g(n)
Se f(n) = (g(n)) rappresenta il tempo
calcolo richiesto da un algoritmo diciamo che
(g(n)) è un limite inferiore asintotico per la
complessità tempo di tale algoritmo.
esempi
f (n) 2n 5n 5 (n )
2
2
2
2
0
cn
2
n
5n 5
infatti
per c = 1 ed n0 = 10
Vedremo che in generale se a2 > 0
f (n) a2 n a1n a0 (n )
2
f (n) 2 sin n (1)
infatti 0 c 1 2 sin n
per c = 1 ed n0 = 1
2
Notazione asintotica .
(limite asintotico stretto)
( g (n)) O( g (n)) ( g (n)) { f (n) : esistono
c1 , c2 0 ed n0 tali che per ogni n n0
0 c1 g (n) f (n) c2 g (n)}
c2 g (n)
f (n)
c1 g (n)
n0
Scriveremo f(n) = (g(n)) per dire che f(n) è
una delle funzioni dell’insieme (g(n)).
f(n) = (g(n))
si legge:
f(n) è “theta” di g(n)
Se f(n) = (g(n)) rappresenta il tempo calcolo
richiesto da un algoritmo diciamo che (g(n))
è un limite asintotico stretto per la
complessità tempo di tale algoritmo.
esempi
2n 5n 5 O(n )
2
2
2n 2 5n 5 (n 2 )
2n 2 5n 5 (n 2 )
Dunque
0 c1n 2n 5n 5 c2 n
2
2
2
per c1 = 1, c2 = 4 ed n0 = 10
2 sin n (1)
2 sin n O(1)
Dunque 2 sin n (1)
f (n) 2n 5n 5 (n ) altrimenti
2
3
0 c1n 2n 5n 5
3
2
2
5
5
c1 2 3
n n
n
per ogni n n0 allora
per ogni n n0. Assurdo!
f (n) 2n 2 5n 5 (n) altrimenti
0 2n 2 5n 5 c2 n per ogni n n0 allora
5 5
c2
per ogni n n0. Assurdo!
2 2
n n
n
Metodo del limite
Spesso è possibile determinare dei limiti
asintotici calcolando il limite di un rapporto.
Ad esempio se lim n f (n) / g (n) k 0
allora per ogni > 0 esiste n0 tale che per n ≥ n0
k f ( n) / g ( n) k
Preso 0 < < k e posto c1 = k e c2 = k +
c1 g (n) f (n) c2 g (n)
e quindi f ( n) ( g ( n))
f ( n)
diciamo che f (n) ( g (n))
Se lim n
g ( n)
ed in questo caso
f (n) ( g (n)) ma f (n) ( g (n))
f ( n)
Se lim n
0 diciamo che f (n) o( g (n))
g ( n)
ed in questo caso
f (n) O( g (n)) ma f (n) ( g (n))
Attenzione: quando il limite del rapporto non
esiste questo metodo non si può usare.
k
k
f
(
n
)
a
n
...
a
n
a
(
n
)
In generale
k
1
0
per ogni funzione polinomiale di grado k con
coefficiente ak > 0.
Inoltre
k
h
(n ) (n ) per k h
(a ) (b ) per a b
n
n
(log a n) (log b n) per ogni a e b
perché log a n log a b log b n
(n log n) (n )
k
k
Per 0 < h < k e 1 < a < b :
O(log n) O(n ) O(n )
h
k
O(n log n) O(a ) O(b )
k
n
n
(log n) (n ) (n )
h
k
(n log n) (a ) (b )
k
n
n
Useremo le notazioni asintotiche anche
all’interno delle formule.
In questo caso le notazioni O(f(n)), Ω(f(n)) e
ϴ(f(n)) stanno ad indicare una qualche funzione
appartenente a tali insiemi e di cui non ci
interessa conoscere la forma esatta ma solo il
comportamento asintotico.
Ad esempio T(n)=n2+O(n) significa che T(n) è
la somma di n2 e di una funzione che cresce al
più linearmente.