caricato da common.user4502

Complessità computazionale

Introduzione
 Per ogni problema esistono molteplici algoritmi in
grado di risolverlo
 Esempio:
 Ricerca di un numero in un insieme di numeri


Ricerca sequenziale
Ricerca dicotomica
 Questi due algoritmi risolvono lo stesso problema ma
uno è migliore dell’altro.
Definizioni
 Un problema è costituito da:
 Una descrizione generale in cui sono presenti i
parametri che possono variare (variabili)
 Una questione cioè un insieme di proprietà che la
soluzione del problema deve soddisfare.
 Istanza di un problema
 L’istanza di un problema è una possibile formulazione
del problema quando alle sue variabili sono stati
assegnati valori particolari.
 Per ogni problema esistono infinite istanze.
Esempio
 Determinare se un numero è primo
 Una possibile istanza del problema potrebbe essere:
 I 1 = 5, I2 ? 27, …., In=2394892348
n – dimensione di un’istanza
 Se indichiamo con n la dimensione di un’istanza,
questa raggruppa tutti gli input che hanno la stessa
dimensione
 La indicheremo come cardinalità n dei dati in
ingresso.
 Per ogni istanza del problema, l’algoritmo
risolutivo deve fornire la soluzione
indipendentemente dai valori, dal tipo o dalla
cardinalità delle variabili.
Definizione – Algoritmo risolutore
 Un algoritmo A risolve un problema p se applicato
a ogni istanza I produce la soluzione corretta.
Algoritmo risolutore
 La generalità dell’algoritmo, propria della definizione
di algoritmo, ci permette di affermare che la
risoluzione del problema implica la risoluzione di
tutte le sue istanze, quindi deve fornire il corretto
risultato al variare di tutti i dati in ingresso.
Individuare la complessità
computazionale
 Il nostro obiettivo è individuare la complessità
computazionale indipendentemente dalla cardinalità
n e dalla tipologia x dei dati in ingresso.
Parametri di qualità di un
algoritmo
 Scrivere un algoritmo non è sempre semplice.
 Può capitare di scrivere una soluzione ma notare fin da
subito che ne avremmo potuto trovare una migliore.
Parametri di qualità di un
algoritmo
 Per definire la qualità degli algoritmi dobbiamo anche
tenere presente com’è stato descritto un algoritmo e
cioè:
 La semplicità di comprensione/modificabilità del codice




sorgente
La qualità di scrittura del codice del programma
L’accessibilità per l’utilizzo del programma
La bellezza grafica dell’interfaccia utente
ecc
Parametri di qualità di un
algoritmo
 Non è possibile utilizzare parametri soggettivi o
qualitativi per classificare gli algoritmi.
 È necessario scegliere parametri oggettivi così da
ottenere un criterio di classificazione universale
Criteri di efficienza
 Complessità temporale: tempo impiegato
dall’esecuzione dell’algoritmo
 Complessità spaziale: dimensione del programma,
occupazione della memoria
 Complessità di I/O: tempo impiegato per
l’acquisizione o trasferimenti di dati tra periferiche
 Complessità di trasmissione: efficienza di I/O di un
algoritmo rispetto a “stazioni remote”.
Criteri di efficienza
 Tempo di elaborazione e quantità di memoria sono i
parametri più utilizzati.
 Per effettuare la classificazione degli algoritmi
utilizzeremo la complessità tempoale
Complessità temporale
 Studiare la complessità di tempo di un algoritmo
significa stimare la quantità di tempo necessaria
affinché la CPU esegua il corrispondente
programma.
 La complessità temporale è un ottimo parametri per
scegliere il migliore tra due o più algoritmi che
risolvono lo stesso problema e costituirà la base per
poter passare dalla complessità di calcolo di un
algoritmo A alla complessità di calcolo del problema p.
Modello di costo per il calcolo del
tempo di esecuzione
 Possiamo calcolare re il tempo di esecuzione di un
algoritmo cronometrandone l’esecuzione su alcune
istanze e riportando i risultati in una tabella.
Esempio
 Elenchiamo in secondi il tempo di esecuzione di quattro
programmi che implementano rispettivamente due
algoritmi A1 e A2 che risolvono lo stesso problema,
mandandoli in esecuzione su due architetture hardware.
Computer 1
Computer 2
Dim. input
A1
A2
A1
A2
50
0,005
0,07
0.05
0.25
100
0,003
0,13
0,18
0,55
200
0,13
0,27
0,73
1,18
300
0,32
0,42
1,65
1,85
400
0,55
0,57
2,93
2,67
500
0,67
0,72
4,60
3,28
1000
3,57
1,60
18,32
7,03
Esempio
 Possiamo osservare come non sia molto significativo
misurare il tempo in secondi
 I tempi sono fortemente influenzati dal compilatore,
dall’architettura e dalle caratteristiche hardware del
computer, dai dati in ingresso.
Modello di costo per il calcolo per il
calcolo del tempo di esecuzione
 Non è possibile esprimere la complessità temporale
intrinseca di un algoritmo utilizzando come unità di
misura i secondi.
Modello di costo per il calcolo per il
calcolo del tempo di esecuzione
 È necessario svincolarsi dalla tecnologia
 La valutazione non deve dipendere da una
particolare macchina o da un particolare
compilatore.
 Per individuare un parametro indipendente da questi
fattori non analizzeremo il programma ma
l’algoritmo.
Modello di costo per il calcolo per il
calcolo del tempo di esecuzione
 Effettueremo l’analisi della complessità
computazionale dell’algoritmo e stabiliremo una
classificazione degli algoritmi definendo non il
tempo di esecuzione ma l’ordine di grandezza del
numero di istruzioni eseguite tenendo conto dei dati
in input e della loro dimensione
Esempio
 Supponiamo di avere due algoritmi diversi per
ordinare n numeri interi
 Il primo impiega n2 istruzioni
 Il secondo impiega nlog(n) istruzioni
 Supponiamo che ogni istruzione venga eseguita in un
µsec e osserviamo i tempi di esecuzione al variare della
dimensione del problema /per esempio dimensione
del vettore) e quindi il numero di istruzioni eseguite
Esempio
Numero di
istruzioni
n=10
n=1000
n=106
n2
0,0001 sec
100 sec
106 sec (12 gg ca)
nlog(n)
0,00003 sec
0,13 sec
19 sec
 Osserviamo come per i piccoli valori i tempi siano
simili mentre al variare della dimensione del
problema i comportamenti siano divergenti
Considerazioni
 Non è importante tanto conoscere il tempo di
esecuzione di un’istanza quanto il suo comportamento
approssimato, cioè l’ordine di grandezza con il quale
un algoritmo si comporta al variare della dimensione
del problema  complessità asintotica.
 Non occorre calcolare un valore ma “la tendenza” cioè
l’andamento che un algoritmo assume all’aumentare
della dimensione del problema nel caso peggiore dei
dati in ingresso.
Caso migliore, peggiore e medio
 La tipologia di dati in input influenza inevitabilmente
il tempo di calcolo poiché a parità di dimensione
incidono notevolmente sul numero di istruzioni che
vengono eseguite.
Caso migliore, peggiore e medio
 Esempio:
 Considerando l’ordinamento di un vettore tramite un
algoritmo di bubble sort con sentinella dove possiamo
individuare due casi estremi:
 Vettore già ordinato  numero di operazioni minimo
 Vettore completamente disordinato  numero di operazioni
massimo
 Siamo in presenza di due casi estremi: caso migliore e
caso peggiore.
 Le condizioni normali sono il caso intermedio ma per
confrontare gli algoritmi indipendentemente dai dati è più
significativo considerare sempre il caso peggiore.
Calcolo della complessità in
funzione del caso base
 La dimensione di un problema incide pesantemente
nel calcolo del tempo di esecuzione di un algoritmo
 È necessario esprimere il tempo di calcolo di un
algoritmo A mediante una funzione T(n) dove n è la
dimensione del problema (dati in ingresso).
 Funzione T(n): esprime il tempo necessario affinché
l’algoritmo A possa produrre la soluzione di un’istanza
di dimensione n.
Chi è n?
 Nel caso di vettori o matrici n è la dimensione del




vettore.
Nel caso di stringhe è la dimensione di una stringa
Etc
In caso di algoritmi complessi non è semplice
individuare il parametro.
Inoltre utilizziamo sempre il caso peggiore.
 Tp(n)= max{T(x), per ogni x istanza del problema}
Criteri per l’individuazione del
tempo di esecuzione
 Definiamo il tempo di esecuzione delle istruzioni
elementari introducendo il costo unitario
 Istruzione a costo unitario: operazione (statement)
la cui esecuzione non dipende né dal valore né dal tipo
di variabili e prende il nome di passo base.
Istruzioni elementari a costo
unitario
 Assegnamento
 Operazioni di I/O (solo da tastiera e non da file)
 Operazioni aritmetiche elementari
 Valutazione di espressioni booleane
 Accesso a elementi di un array
Assunto che:
 Si acceda a qualsiasi cella della RAM con un costo uniforme
 Le istruzioni vengano svolte una volta senza grado di
parallelismo
 Indichiamo con C una costante numerica: il costo del test
di un’operazione booleana composta da più condizioni
booleane semplici sarà sempre minore o uguale a C volte il
costo del test di una condizione semplice  costo unitario
 Analogamente per le condizioni aritmetiche composte:
un’espressione con molteplici operazioni ha sempre un
costo inferiore a C volte una singola operazione aritmetica.
 Non consideriamo gli accessi a file.
Esempio
Esempio
Esempio
Esempio
Esempio
Il costo potrebbe dipendere dai
valori in ingresso..
Considerazioni
 Se estendiamo il procedimento a un intero programma
otteniamo comunque un’espressione matematica che
esprime la complessità in termini di passi base
 Questa espressione può essere un polinomio oppure,
nel caso generale, una funzione che può risultare
complessa da ricavare.
 Se abbiamo un programma strutturato in composto da
procedure e funzioni si deve:
 Calcolare la complessità di ogni procedura/funzione
 Per ogni chiamata di procedura/funzione aggiungere la
complessità della stessa al costo globale del programma.
Esercizi
i = 1;
k = 2;
while (i<=n) {
i = i+1;
k = k*1;
printf(k);
}
i = 1;
while (i<=n) {
i = i+1;
printf(i);
}
k = 1;
while(k<=m)
k = k+1;
Esercizi
i = 1;
k = 2;
while (i<=n) {
i = i+1;
k = k*1;
printf(k);
}
1 + 1 + (n+1) + n (1+1+1)
4n + 3
i = 1;
while (i<=n) {
i = i+1;
printf(i);
}
k = 1;
while(k<=m)
k = k+1;
1 + (n+1) + n(1+1) + 1 +
m+1 + m(1)=
3n + 2m +4
Esercizi
i = 1;
k = 1;
while (i <= n) {
i = i+1;
printf(i);
while(k<=m) {
k = k+1;
}
}
Esercizi
i = 1;
k = 1;
while (i <= n) {
i = i+1;
printf(i);
while(k<=m) {
k = k+1;
}
}
1+
1+
n+1 +
n(
1+
1+
n+1+
n(1)
)
= 2n2+4n+3
Confronto tra algoritmi
 Dopo aver espresso la complessità dei programmi in
termini di passi base ora vogliamo effettuare il
confronto tra algoritmi
Confronto tra algoritmi
 Supponiamo di avere, per uno stesso problema, sette
algoritmi diversi con diversa complessità.
 Per poterli confrontare meglio traduciamo la
complessità in tempo effettivo di elaborazione
supponendo che per un passo base occorra un
microsecondo (10-6 sec) e riportiamo i risultati nella
tabella per quattro istanze con dimensioni di dati di
input diverse
Confronto tra algoritmi
 I primi tre algoritmi hanno complessità descritta da un
polinomio di primo grado
 Il quarto e il quinto hanno una complessità descritta da un
polinomio di secondo grado
 Il sesto polinomio ha una complessità descritta da un
polinomio di terzo grado
 Il settimo polinomio ha una complessità descritta da una
funzione esponenziale.
Confronto tra algoritmi
 Osserviamo che per piccoli valori della dimensione n del
problema (1° colonna) tutti gli algoritmi hanno tempi di
risposta non significativamente differenti.
 Già con n = 100 l’algoritmo di capacità esponenziale si
differenzia in maniera considerevole
 Per dimensioni di input di grandi dimensioni (n=106) i sette
algoritmi si partizionano in cinque classi.
 Algoritmo √n frazioni di secondo
 Algoritmo n+5, 2*n  secondi
 Algoritmi n2,n2+n -_> giorni
 Algoritmo n3  secoli
 Algoritmo 2n  ?
Confronto tra algoritmi
 Possiamo dunque affermare che all’aumentare delle
dimensioni dei dati in ingresso di un dato problema
risulta determinante solo l’ordine di grandezza con
cui le funzioni crescono al crescere di n.
  Concetto di complessità asintotica
Gli O grandi
 È un criterio matematico per partizionare gli algoritmi in
classi di complessità
 Si dice che una funzione f(n) è di ordine g(n) e si scrive:
f(n) = O(g(n))
se esiste una costante numerica C positiva tale che valga, salvo al
più per un numero finito di valori di n, la seguente condizione:
f(n) ≤ C*g(n)
 Se g(n) non è identicamente nulla (fatto assolutamente certo
nell’esecuzione di un algoritmo), la condizione precedente può
essere espressa come:
f(n) / g(n) ≤ C
ovvero la funzione f(n)/g(n) è limitata
Esempi
 Tempo 3*n2+2*n+4 = O(n2)
 dato che 3*n2 + 2n + 4 ≤ 4*n2 per n > 3
 Tempo 4*n3 + 5logn + 8 = O(n3)
 dato che 4*n3 + 5logn+8 ≤ 5*n3 per n > 2
 Tempo 2n + 7n = O(2n)
 Dato che 2n + 7n ≤ 2* 2n per n>4
Termine prevalente
 Dato che a noi interessa il comportamento
asintotico ovvero con n tendente ad infinito non è
necessario soffermarsi a individuare i vari valori
 Basta soffermarsi a studiare il termine prevalente.
 Se abbiamo una generica funzione di n possiamo
trascurare sia eventuali fattori moltiplicativi sia
eventuali termini addizionati e considerare solo il
termine prevalente.
Complessità asintotica
 Si dice che f(n) ha complessità asintotica g(n) se
valgono le seguenti condizioni:
 f(n) = O(g(n))
 g(n) è la più piccola di tutte le funzioni che soddisfano la
prima condizione
Complessità asintotica
 Esempi:
Dalla complessità in passi base alla
complessità asintotica
 In pratica, la complessità asintotica è definita dal blocco
di complessità maggiore
 Non contano le costanti, né additive, né moltiplicative
Andamento caratteristico
Algebra degli O grandi
 In precedenza, abbiamo visto degli esempi di calcolo
della complessità in numero di passi base per
programmi a blocchi
 Definiamo ora, tramite l’algebra degli «O grandi», un
criterio per il calcolo della complessità asintotica di
un programma strutturato
 In un programma strutturato a blocchi si possono
presentare due situazioni:
 blocchi in sequenza
 blocchi annidati
Termine di complessità maggiore
 Nel caso di una funzione composta da diversi termini
la complessità asintotica è definita dal termine di
complessità maggiore
 Inoltre non contano le costanti, né additive, né
moltiplicative, ma solo l’ordine di grandezza del
termine più significativo.
Teorema della somma
 La complessità di un blocco costituito da più blocchi in
sequenza è quella del blocco di complessità
maggiore.
Teorema della somma - esempio
Teorema del prodotto
 La complessità di un blocco costituito da più blocchi
annidati è data dal prodotto delle complessità dei
blocchi componenti (applicazione del teorema del
prodotto).
Teorema del prodotto - esempio
Calcolare la complessità di un
qualsiasi algoritmo strutturato
 Con il concetto di «O grande» e le due operazioni
definite dall’algebra degli «O grandi» si può calcolare
la complessità di qualsiasi algoritmo strutturato senza
conoscerne preventivamente la complessità in passi
base
Esempio
Classi di complessità degli algoritmi
Classi di complessità degli algoritmi
Classi di complessità degli algoritmi