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.
Scarica

PPT