Sei sulla pagina 1di 3

ANALISI COMPLESSITÁ

Per qualificare algoritmi e strutture dati come ‘buoni’ bisogna individuare dei metodi per
analizzarli, un principale strumento di analisi è la caratterizzazione dei tempi di esecuzione e
dello spazio di memoria utilizzato dall’algoritmo o da una struttura dati.

Per procedere con l’analisi abbiamo bisogno di implementare l’algoritmo e poi effettuare
esperimenti, atti a misurare il comportamento e le risorse utilizzate dallo stesso.

Dobbiamo sviluppare un modello che:

- Consenta di valutare l’efficienza relativa di due algoritmi in modo indipendente


dall’ambiente hardware e software;
- Operi analizzando una descrizione ad alto livello dell’algoritmo, senza che sia necessario
implementarlo;
- Tenga considerazione tutti i possibili insiemi di dati ingresso.

Per sviluppare un modello di questo tipo abbiamo bisogno di:

- Contare le operazioni elementari o dette primitive;


- Contare le operazioni in funzione dei dati;
- Porre attenzione sul caso peggiore.

Supponiamo di voler caratterizzare la complessità di un algoritmo A. se indichiamo con T il tempo


impiegato dall’algoritmo e con n la dimensione della struttura dati d su cui opera A, allora noi siamo
interessati a caratterizzare la funzione T = T(n). il modello che assumeremo per il calcolo del tempo
T(n) sarà il modello RAM.

Quindi T rappresenta il tempo complessivo impiegato dalle istruzioni dell’algoritmo, che


dipenderà dalla dimensione n dei dati, ma non solo anche dagli n valori assunti dalla struttura
dati d. allora la caratterizzazione T(n) va esaminata in tre casi:

- Caso migliore: corrisponde alle configurazioni di d che danno luogo ad un minimo della
funzione T(n);
- Caso peggiore: corrisponde alle configurazioni di d che danno luogo ad un massimo della
funzione T(n);
- Caso medio: che corrisponde al comportamento medi di T(n) al variare della configurazione
di d.

L’analisi che si compie è per un valore di n sufficientemente grande, infatti parleremo di analisi al
limite. Per fare ciò useremo le notazioni asintotiche, rappresentano classi di funzioni e non una
singola specifica funzione e sono:

- O: fornisce un limite lasco per il limite superiore (solitamente unica utilizzata);


- Ω: fornisce un limite lasco per il limite inferiore;
- Θ: fornisce un limite stretto

1
Notazione O (O – grande)

Siano f(n) e g(n) due funzioni che mettano in corrispondenza numeri interi positivi con numeri reali
positivi. Si dice che f(n) ∈ O(g(n)) se esiste una costante reale c > 0 e una costante n0 ≥ 1 tale che:

f(n) ≤ c * g(n) per n ≥ n0

la notazione O ci consente di affermare che una funzione f(n) è minore di / uguale a un’altra
funzione g(n) a meno di un fattore costante, in senso asintotico.

Notazione Ω (Omega – grande)

Siano f(n) e g(n) due funzioni che mettano in corrispondenza numeri interi positivi con numeri reali
positivi. Si dice che f(n) ∈ Ω(g(n)) se esiste una costante reale c > 0 e una costante n0 ≥ 1 tale che:

f(n) ≥ c * g(n) per n ≥ n0

Notazione Θ (theta – grande)

Siano f(n) e g(n) due funzioni che mettano in corrispondenza numeri interi positivi con numeri reali
positivi. Si dice che f(n) ∈ Θ(g(n)) se f(n) ∈ O(g(n)) e f(n) ∈ Ω(g(n)) ovvero esistono due costanti
reali c1 > 0 e c2 > 0 e una costante n0 ≥ 1 tale che:

c’ * g(n) ≤ f(n) ≤ c’’ * g(n) per n ≥ n0

Assegnazioni del costo computazionale

Le istruzioni di assegnamento, che coinvolgono solo operatori aritmetici, relazionali o logici


hanno tutte lo stesso costo computazionale pari a O (1), rientrano in questa categoria anche le
operazioni primitive di input e output.
I blocchi di codice con m istruzioni caratterizzate dalle rispettive funzioni hanno come complessità
il valore dato da: Θ(maxi(fi(n))). E tramite la regola della somma, la complessità è la somma delle
complessità delle singole istruzioni.
Costrutti selettivi come if…else la complessità è data dalla somma delle funzioni caratterizzanti
della condizione if, del blocco di istruzioni successivo all’if e dal blocco di istruzioni nel ramo
dell’else. Allora avremo O (max(fcond + fthen + felse)). Quest’analisi vale anche per costrutti con il
semplice if e switch.
Per il ciclo while la complessità è data dal prodotto delle complessità del corpo del while per il
numero di volte che esse vengono eseguite. Siano k max e kmin il numero massimo e minimo di
interazioni di un generico ciclo e siano fcond e fbody le funzioni caratterizzanti, la complessità sarà
data allora da: O (fcond + kmax * fbody)
Per ciclo for la complessità è data dal prodotto della complessità del corpo per il numero di
interazioni, sia k il numero di interazioni, la complessità sarà: Θ(fcond + k * fbody)
Per l’invocazione di una funzione la complessità è data dalla somma del costo dell’esecuzione e
dal costo dell’invocazione stessa.

Se abbiamo un algoritmo con cicli annidati tali operazioni del ciclo interno aumentano di un’unità
ogni volta, allora il numero totale di operazioni è quadratico in funzione del numero di esecuzioni
del ciclo esterno: (1 + 2 + 3 + … + (n – 1) + n = [n*(n+1)] / 2

2
3

Potrebbero piacerti anche