Complessità Computazionale - Università degli Studi di Messina

Università degli studi di Messina – Facoltà di Ingegneria
Corso di Laurea in Ingegneria Informatica e delle Telecomunicazioni
Fondamenti di Informatica
II
Prof. D. Bruneo
Complessità
Computazionale
La Nozione di Algoritmo
Informalmente, un algoritmo è un procedimento formato da una sequenza
finita di operazioni elementari che trasforma uno o più valori di ingresso
(input) in uno o più valori di uscita (output)
Dato un algoritmo A, denoteremo con fA la funzione che associa a
ogni ingresso x di A la corrispondente uscita fA(x)
Questa corrispondenza tra input e output rappresenta il problema risolto
dall’algoritmo
Fondamenti di Informatica II
Prof. D. Bruneo
2
Definizione di Problema
Formalmente, un problema è una funzione:
f : DI → DS
definita su un insieme DI di elementi che chiameremo istanze, a valori su un
insieme DS di soluzioni
Un problema verrà in generale descritto usando la seguente rappresentazione:
Problema
NOME
Istanza:
x ∈ DI
Soluzione:
Fondamenti di Informatica II
f ( x) ∈ DS
Prof. D. Bruneo
3
Algoritmi e Problemi
Diremo che un algoritmo A risolve un problema f
se f(x) = fA(x) per ogni istanza x
L’esecuzione di un algoritmo su un dato input richiede il consumo di una
certa quantità di risorse:
tempo di computazione
spazio di memoria usato
numero di dispositivi di calcolo utilizzato
È in generale importante saper valutare la quantità di risorse consumate
perché un consumo eccessivo può pregiudicare le stesse possibilità di
utilizzo di un algoritmo
Noi faremo riferimento a modelli di calcolo formati da un solo processore trattando, quindi,
unicamente la teoria degli algoritmi sequenziali
Fondamenti di Informatica II
Prof. D. Bruneo
4
Studio degli Algoritmi
Possiamo raggruppare le problematiche riguardanti lo studio degli algoritmi in
tre ambiti principali:
Sintesi: dato un problema f, costruire un algoritmo A per risolvere f, cioè
tale che f = fA
Analisi : dato un algoritmo A ed un problema f, dimostrare che A risolve f,
cioè che f = fA (correttezza) e valutare la quantità di risorse usate
da A (complessità concreta)
Classificazione: data una quantità T di risorse, individuare la classe di
problemi risolubili da algoritmi che usano al più tale
quantità
Fondamenti di Informatica II
Prof. D. Bruneo
5
Complessità di un Algoritmo
Due misure ragionevoli per sistemi di calcolo sequenziali sono i valori:
TA (x)
tempo di calcolo
S A (x)
spazio di memoria
Valori richiesti da un algoritmo A
su input x
Una soluzione che fornisce buone informazioni su TA e su SA consiste
nell’introdurre il concetto di “dimensione” di un’istanza
Fondamenti di Informatica II
Prof. D. Bruneo
6
Tempo di Calcolo
Dato un algoritmo A su un insieme di input I, può accadere che due istanze
x, x'∈ I
di eguale dimensione, cioè tali che:
x = x'
diano luogo a tempi di esecuzione diversi, ovvero:
TA ( x ) ≠ T A ( x ' )
Come definire allora il tempo di calcolo di A in funzione di una sola dimensione?
Fondamenti di Informatica II
Prof. D. Bruneo
7
Tempo di Calcolo (cont.)
Una possibile soluzione è quella di considerare il tempo peggiore su tutti gli
input di dimensione n fissata. Una seconda è quella di considerare il tempo
medio.
Avremo quindi:
Complessità “nel caso peggiore”
In questo modo la complessità diventa
una funzione T(n) definita sugli interi
positivi, con tutti i vantaggi che la
semplicità di questa notazione comporta
Complessità “nel caso medio”
Risulta significativa la cosiddetta “complessità asintotica”, cioè il comportamento
della funzione T(n) per grandi valori di n
Quale delle due complessità fornisce più informazione? Entrambe hanno vantaggi e svantaggi. Ad
esempio la complessità “nel caso peggiore” fornisce spesso una valutazione troppo pessimistica;
viceversa, la complessità “nel caso medio” assume una distribuzione uniforme sulle istanze, ipotesi
discutibile in molte applicazioni
Fondamenti di Informatica II
Prof. D. Bruneo
8
Esempio – Ricerca Esaustiva
int Ricerca_esaustiva (int a[], int key; int n){
int i;
C1
for (i=1; a[i]!=key && i!=n; i++);
C2
return (a[i]==key ? i :0);
C3
}
Il tempo di esecuzione si esprime come: T(n) = C1 + αC2 + C3
dove α =
n
se l’elemento non esiste
i
se l’elemento è in posizione i
Come fissare α ? Assumendo una distribuzione uniforme, l’elemento si troverà mediamente
in posizione
1
1 n
1 n (n +1)
n +1
∑n ∗i = n ∑i = n 2 = 2
i =1
i =1
n
conviene, però, prescindere da distribuzione e ordinamento dei dati
Fondamenti di Informatica II
Prof. D. Bruneo
9
Complessità in Tempo – Ordini di Grandezza
Il criterio principale solitamente usato per valutare il comportamento
di un algoritmo è basato sull’analisi asintotica della sua complessità
in tempo (nel caso peggiore o in quello medio).
In particolare l’ordine di grandezza di tale quantità, al tendere di n a ∞
fornisce una valutazione della rapidità di incremento del tempo di
calcolo al crescere delle dimensioni del problema
Una differenza anche piccola nell’ordine di grandezza della complessità di due
procedure può comportare enormi differenze nelle prestazioni di due algoritmi
Fondamenti di Informatica II
Prof. D. Bruneo
10
Tempi effettivi
La seguente tabella fornisce un’idea più precisa del tempo effettivo corrispondente a funzioni di
complessità tipiche.
Si è supposto di poter eseguire un’operazione elementare in un microsecondo.
Si sono confrontati i tempi di calcolo richiesti su istanze di varie dimensioni da sei algoritmi di
complessità in tempo differente.
Complessità
n=10
n=20
n=50
n=100
n=103
n=104
n=105
n=106
n
10μs
20μs
50μs
0,1ms
1ms
10ms
0,1s
1s
nlog2n
33,2μs
86,4μs
0,28ms
0,6ms
9,9ms
0,1s
1,6s
19,9s
n2
0,1ms
0,4ms
2,5ms
10ms
1s
100s
2,7h
11,5g
n3
1ms
8ms
125ms
1s
16,6mn
11,5g
31,7a
≈300c
2n
1ms
1s
35,7a
≈1014c
∞
∞
∞
∞
3n
59ms
58mn
≈108c
∞
∞
∞
∞
∞
μs = microsecondi
a = anni
c = secoli
ms = millisecondi
s = secondi
mn = minuti
h = ore
∞ = periodo superiore al millennio
Fondamenti di Informatica II
Prof. D. Bruneo
11
Considerazioni
Dall’analisi della tabella precedente si possono trarre le seguenti considerazioni:
Gli algoritmi dotati di una complessità in tempo lineare o di poco
superiore (nlogn) sono utilizzabili in maniera efficiente anche per
elevate dimensioni dell’input
Gli algoritmi che hanno una complessità dell’ordine di nk (con k≥2)
sono applicabili solo quando la dimensione dell’ingresso non è
troppo elevata
Gli algoritmi che hanno una complessità esponenziale (ad esempio
2n o 3n) presentano tempi di calcolo proibitivi anche per dimensioni
di input limitate
Fondamenti di Informatica II
Prof. D. Bruneo
12
Considerazioni (cont.)
Si potrebbe pensare che le valutazioni generali riportate precedentemente
dipendano dall’attuale livello tecnologico e siano destinate ad essere
superate con l’avvento di una tecnologia più sofisticata che permetta di
produrre strumenti di calcolo sensibilmente più veloci
In realtà:
algoritmi lineari (n) o quasi lineari (nlogn) traggono pieno vantaggio dal
passaggio alla tecnologia più potente
algoritmi polinomiali (n2) traggono un vantaggio evidente ma smorzato
algoritmi esponenziali (2n) non traggono nessun vantaggio
Fondamenti di Informatica II
Prof. D. Bruneo
13
Espressioni Asintotiche
Il comportamento asintotico ci permette di valutare l’efficienza quando le
dimensioni tendono all’infinito, considerando il caso peggiore tra quelli di pari
dimensione.
In certi casi si valuta il caso medio, ma è più complicato
Analisi asintotica di un algoritmo
Valutazione comportamento
asintotico di una sequenza di
interi {T(n)}.
dove:
∀n ∈ N
T (n) : quantità di una certa risorsa consumata su un input di dimensione n
(nel caso peggiore o in quello medio)
Fondamenti di Informatica II
Prof. D. Bruneo
14
Relazioni tra sequenze numeriche
Date due funzioni f,g definite su N a valori in R+ si possono
definire le seguenti relazioni:
1) f(n) è “o grande” di g(n)
f (n) = O( g (n))
2) f(n) è “omega grande” di g(n)
f (n) = Ω( g (n))
3) f(n) e g(n) hanno lo stesso ordine di grandezza
Fondamenti di Informatica II
f (n) = Θ( g (n))
Prof. D. Bruneo
15
Relazioni tra sequenze numeriche (cont.)
1) f (n) = O( g (n))
Si dice che f(n) è “o grande” di g(n) se:
∃c > 0, n0 ∈ N : ∀n > n0 ⇒ f (n) ≤ c ∗ g (n)
si dice anche che f(n) ha ordine di grandezza minore o uguale a quello di g(n)
Esempi:
5n 2 + n = O ( n 2 )
Fondamenti di Informatica II
3n 4 = O(n 5 )
n log n = O(n 2 )
Prof. D. Bruneo
16
Relazioni tra sequenze numeriche (cont.)
2) f (n) = Ω( g (n))
Si dice che f(n) è “omega grande” di g(n) se:
∃c > 0, n0 ∈ N : ∀n > n0 ⇒ f (n) ≥ c ∗ g (n)
si dice anche che f(n) ha ordine di grandezza maggiore o uguale a quello di g(n)
Esempi:
10n 2 log n = Ω(n 2 )
Fondamenti di Informatica II
n1
k
= Ω(log n)
per ogni intero k>0
1 k
en
= Ω( n)
Prof. D. Bruneo
17
Relazioni tra sequenze numeriche (cont.)
3) f (n) = Θ( g ( n))
Si dice che f(n) e g(n) hanno lo
stesso ordine di grandezza se:
∃c, d > 0, n0 ∈ N : ∀n > n0 ⇒ c ∗ g (n) ≤ f (n) ≤ d ∗ g (n)
Esempi:
5n 2 + n = Θ(n 2 ) 100n log 2 n = Θ(n log 2 n)
Fondamenti di Informatica II
2
1
log(1 + ) = Θ( )
n
n
Prof. D. Bruneo
18
Proprietà
Dalle definizioni precedenti si deducono le seguenti proprietà:
f (n) = O( g ( n)) ⇔ g (n) = Ω( f (n))
f (n) = Θ( g (n)) ⇔ f (n) = O ( g (n)), f (n) = Ω( g (n))
Fondamenti di Informatica II
Prof. D. Bruneo
19
Regole
Dalla definizione di “O grande” discende che:
1) I fattori costanti non interessano
f (n) = O( g (n)) ⇒ c ∗ f (n) = O( g (n)), ∀c > 0
2) I termini di grado inferiore non interessano
f (n) = ak n k + ak −1n k −1 +
+ a1n + a0 , ak > 0 ⇒ f (n) = O(n k )
2) Vale la regola della somma
 f 1( n) = O( g 1( n))
⇒ f 1 ( n) + f 2 ( n) = O(max[ g1 (n ), g 2 ( n)])

 f 2 ( n) = O( g 2 ( n))
Le stesse regole valgono per Ω, Θ
Fondamenti di Informatica II
Prof. D. Bruneo
20
Dimostrazione
Al fine di dimostrare la regola della somma si consideri:
 f 1 ( n) = O ( g 1( n))

 f 2 ( n) = O ( g 2 ( n))
dalla definizione di “o grande” risulta:
 f1 ( n) ≤ a ∗ g 1( n)

 f 2 ( n) ≤ b ∗ g 2 (n )
quindi:
f1 (n) + f 2 (n) ≤ ag1 (n) + bg 2 (n) ≤ (a + b) ∗ max[ g1 (n), g 2 (n)]
da cui deriva:
f1 (n) + f 2 (n) = O (max[ g1 (n), g 2 (n)])
Fondamenti di Informatica II
Prof. D. Bruneo
21
Considerazioni
Non è corretto considerare solo il caso migliore per valutare il comportamento asintotico
di un algoritmo. Il fatto che un algoritmo nel caso migliore abbia una complessità lineare
non basta per affermare che l’algoritmo appartenga a Θ(n) oppure a O(n). Per lo stesso
motivo non è corretto neppure affermare che l'algorimto appartiene a Θ(n2) solo perché
nel caso peggiore la complessità è quadratica. Viceversa, è corretto affermare che, nel
caso migliore, la complessità dell'algoritmo è Θ(n), che nel caso medio e nel caso
peggiore la complessità è Θ(n2) e, più in generale, che l'algoritmo è O(n2) ovvero è Ώ(n).
Un altra importante considerazione fa riferimento ai casi in cui non sono verificate le
condizioni che sono alla base dell’introduzione delle notazioni asintotiche. Infatti, proprio
perché le notazioni introdotte sono asintotiche, vengono trascurati i termini di ordine
inferiore e le costanti moltiplicative.
Tuttavia, nel caso in cui è necessario confrontare algoritmi aventi tempi di esecuzione
T(n) il cui andamento al limite è uguale (es. sono entrambe O(n2)), non è più possibile
trascurare tali termini. In tal caso per stabilire quale algoritmo è più conveniente usare
bisogna necessariamente tener conto, in primo luogo delle costanti moltiplicative e poi
dei termini di ordine inferiore.
Fondamenti di Informatica II
Prof. D. Bruneo
22
Tempo di esecuzione: Operazioni semplici
Le seguenti operazioni hanno un tempo di esecuzione dell’ordine di:
T(n) = O(1)
operazioni aritmetiche (+,*,…)
operazioni logiche(&&, ||,….)
confronti (<=, ==…)
operazioni di accesso a strutture dati semplici
(p.e.: ad elem. di array)
operazioni di assegnazioni di valore (p.e.: a=b)
senza chiamate di funzione
operazioni di lettura e scrittura (p.e.: printf(“...”))
operazioni di controllo (p.e.: break,continue, return)
Spiegazione intuitiva: sono operazioni che possono essere
realizzate con poche istruzioni in linguaggio macchina
Fondamenti di Informatica II
Prof. D. Bruneo
23
Tempo di esecuzione: Blocchi di comandi semplici
Un blocco composto da una serie di comandi semplici ha un tempo di
esecuzione dell’ordine di:
T(n) = O(1)
Infatti, per la regola della somma, ogni somma di un numero costante di
O(1) restituisce come risultato O(1)
Fondamenti di Informatica II
Prof. D. Bruneo
24
Tempo di esecuzione: ciclo for
ciclo for
La complessità di un ciclo for è data dal
prodotto della complessità del corpo del for
stesso per il numero di volte che esso
viene eseguito.
O(1)
inizializza
O(1)
test
k
volte
corpo
end
T(n) = O(k*f(n))
O(f(n))
incremento
O(1)
Trattandosi di ciclo non predeterminato,
sarà necessario distinguere un caso
migliore ed un caso peggiore. Dette kmin e
kmax il numero minimo e massimo di volte
che viene iterato un generico ciclo for si
potranno ottenere i tempi di esecuzione
Tmin e Tmax
Il caso del ciclo while risulta essere analogo
Fondamenti di Informatica II
Prof. D. Bruneo
25
Tempo di esecuzione: costrutto if-then-else
If-then-else
O(1)
test
parte if
O(f(n))
parte else
La complessità di un istruzione del tipo
if <condizione>
then <istruzioni ramo then>
else<istruzioni ramo else>
è limitata superiormente dal ramo che ha la
complessità maggiore. In realtà
bisogna anche considerare il tempo
impiegato a verificare la condizione, che in
genere ha complessità costante O(1).
O(g(n))
T(n) = O(max[f(n),g(n)])
Ragionamenti analoghi valgono nel caso di un costrutto di tipo case.
Fondamenti di Informatica II
Prof. D. Bruneo
26
Tempo di esecuzione: chiamata di funzioni
La complessità di una chiamata di funzione è data dalla somma di due termini: il
costo, in termini di complessità computazionale, dell’esecuzione della funzione
stessa ed il costo della chiamata. Quest’ultimo è in genere trascurabile, ma non
lo è se si effettuano chiamate per valore che hanno l’effetto di copiare sullo stack
la struttura dati d la cui dimensione n è proprio quella da cui dipende la nostra
funzione T(n) di interesse. Tuttavia, se la struttura dati è un vettore ed il
linguaggio considerato è il C, poiché alla funzione viene passato di fatto il
puntatore al primo elemento del vettore, il tempo di chiamata è indipendente da
n.
T(n) = O(ffunzione(n)+fchiamata(n))
T(n) = O(ffunzione(n))
Fondamenti di Informatica II
Passaggio dell’intera struttura
dati
Passaggio del puntatore alla struttura
dati
Prof. D. Bruneo
27
Tempo di esecuzione: funzioni ricorsive
La complessità di una funzione ricorsiva (cioè di una funzione che contiene al suo
interno una chiamata a se stessa) non può essere calcolata con le tecniche
precedenti. Infatti, data una generica funzione ricorsiva R, la complessità della
chiamata di un’istanza ricorsiva di R, che compare nel corpo di R stessa,
dovrebbe essere data dalla somma del costo di chiamata e del costo
dell’esecuzione dell’istanza di R. Ma quest’ultimo costo è proprio quello che
stiamo cercando di calcolare, ed è quindi incognito.
Per risolvere questo problema è necessario esprimere il tempo incognito T(n)
dell’esecuzione di R, come somma di due contributi: un tempo O(f(n)) che deriva
dall’insieme di tutte le istruzioni che non contengono chiamate ricorsive, ed un
tempo T(k) che deriva dalle chiamate ricorsive, invocate su di una dimensione del
dato di ingresso più piccola, cioè con k < n. Otterremo quindi un’equazione, detta
formula di ricorrenza, del tipo:
T(n)=aT(n/b)+O(f(n))
Fondamenti di Informatica II
Prof. D. Bruneo
28
Tempo di esecuzione: funzioni ricorsive (cont.)
Ad esempio, nell’ambito del paradigma divide-et-impera, avremo ricorrenze del
tipo:
T(n)=
O(1)
per n≤c
aT(n/b)+C(n)+D(n)
per n>c
derivanti dall’aver diviso un certo problema padre in a sottoproblemi figlio di
dimensione n/b, avendo indicato con C(n) il costo derivante dalla combinazione
dei risultati e con D(n) il costo relativo alla divisione del problema padre nei
problemi figlio ed essendo costante (e quindi pari a O(1)) il tempo per la soluzione
dei problemi nel caso banale, cioè quando n<c.
Per risolvere una formula di ricorrenza, e quindi giungere a determinare la complessità
asintotica di T(n), è possibile adottare dei particolari metodi di soluzione. Uno di questi
(detto metodo iterativo) consiste semplicemente nell’iterare la ricorrenza proposta, finché
non si riesce a trovare una generalizzazione. A questo punto occorre trovare il valore per il
quale si chiude la ricorrenza, sostituirlo nella formula generale e calcolare il risultato
applicando le regole per limitare le sommatorie.
Fondamenti di Informatica II
Prof. D. Bruneo
29
Esercizio 1
Calcolare la complessità in funzione di n≥0 della seguente funzione:
int g (int n){
int a=n;
if (n<=500) for (int i=1; i<=n; i++)
for (int j=1; j<=n;j++) a+=n;
else for (int i=1; i<=n; i++) a+=n;
return a;
}
Soluzione:
Istruzione ricorrente: a+=n;
O(n2) per n ≤500 (istr. ricorrente in un doppio ciclo for
ciascuno dei quali itera n volte)
T(n)=
O(n) per n >500
(istr. ricorrente in un ciclo for da 1 ad n.
Complessità asintotica)
Fondamenti di Informatica II
Prof. D. Bruneo
30
Esercizio 2
Dato il seguente frammento di programma:
i=n;
while (i>=1) {
for (int j=1; j<=n;j++) a++;
i=E;
}
calcolare la complessità asintotica in funzione di n nei casi:
a) E=i-1;
b) E=i-n;
Fondamenti di Informatica II
c) E=i/2.
Prof. D. Bruneo
31
Esercizio 2: soluzione
Istruzione ricorrente: a++;
Tale istruzione verrà eseguita almeno n volte nel for, resta da determinare
la stima delle iterazioni nel ciclo esterno (while) in funzione di n che
andranno a moltiplicare le iterazioni del ciclo interno (n)
=> T(n) = O(T(while)*n).
Il while itera per i che va da n ad 1 variando nei tre casi il passo di
decremento => 1≤i≤n.
Calcoliamo dunque la complessità del while:
a) i=E=i-1: il passo è lineare => il while itera n volte => T(n) = O(n*n) = O(n2);
b) i=E=i-n: alla prima iterazione i=n, viene eseguito il for e dunque ad i viene
assegnato i-n => i=0 che non soddisfa la condizione del while => una sola
iterazione => T(n) = O(n*1) = O(n);
Fondamenti di Informatica II
Prof. D. Bruneo
32
Esercizio 2: soluzione (cont.)
c) I=E=i/2: supponiamo sia k il numero di iterazioni del quale vogliamo stabilire una
stima della complessità in funzione di n:
inizialmente
passo K
passo K-1
passo K-2
…
passo k-z
passo j
…
passo 1
i=n;
i=n/2;
i=n/4;
i=n/8;
…
i=n/2z+1;
i=n/2k-j+1;
…
i=n/2k=1;
Da cui possiamo stimare k:
k = log2n
Il che vuol dire che la complessità asintotica sarà:
T(n) = O(n*log2n)
Fondamenti di Informatica II
Prof. D. Bruneo
33
Esercizio 3 a
Date le seguenti dichiarazioni di funzione, calcolare la complessità in funzione
di n>0 della chiamata P(F(n),y).
int F(int n) {
int b; int a=1;
for (int i=1; i<=n; i++)
for (int j=1; j<=n;j++) a++;
b= a/n; return 2*b;
} void P (int m, int &x) { for (int i=1; i<=m*m; i++) x+=3;
}
Fondamenti di Informatica II
Prof. D. Bruneo
34
Esercizio 3 a: soluzione
L’istruzione ricorrente di F si trova all’interno del doppio for, cioè: a++;
Quella di P è invece localizzata all’interno del for: x+=3;
La complessità di F risulta essere:
TF(n) = O(n2)
essendo l’istruzione ricorrente iterata in un doppio for con 1≤ i ≤ n e step unitario, quella di P:
TP(m) = O(m2)
in quanto il ciclo itera per i tra 1 ed m2 con incremento unitario.
Il valore restituito dalla funzione F che diventa il limite del ciclo in P (il parametro m) facendo un semplice
calcolo risulta essere uguale ad n, per cui nella stima della complessità possiamo sostituire ad m n; da cui la
complessità totale si trova sommando le complessità delle due funzioni, essendo la loro esecuzione non
sovrapposta ne iterata:
T(n) = TF(n) + TP(n) = O(n2) + O(n2) = O(n2)
Fondamenti di Informatica II
Prof. D. Bruneo
35
Esercizio 3 b
Come nell’esempio precedente ma con F definita come segue:
int F(int n) {
int b; int a=1;
for (int i=1; i<=n; i++)
for (int j=1; j<=n;j++) a++;
b= a;
return 2*b;
} Soluzione :
La complessità delle due funzioni rimane invariata rispetto al caso precedente,
differisce invece il valore restituito dalla funzione F, che sarà n2:
TF(n) = O(n2); TP(m) = O(m2)
T(n) = TF(n) + TP(n) = O(n2) + O(n4) = O(n4)
Fondamenti di Informatica II
Prof. D. Bruneo
36
Esercizio 4
Calcolare la complessità in funzione di n>0 del seguente ciclo:
for (int j=1; j<=f(n);j++) a+=n;
int f (int n){
int a=0;
for (int j=1; j<=n;j++) a+=n;
return a;
}
Soluzione:
numero di iterazioni = risultato[f(n)]: somma n volte n => n2
complessità di una iterazione: complessità della chiamata f(n):
TF(n) = O(1) + O(n) + O(1) = O(n)
complessità del for: numero iterazioni * complessita’ di una iterazione:
Tfor(n)= O(n2 *n) = O(n3 )
Fondamenti di Informatica II
Prof. D. Bruneo
37
Esercizio 5
Calcolare la complessità della funzione seguente (ricerca binaria)
int binSearch (int A [], int n, int x) {
int i=0; int j=n­1;
while (i <= j) {
int k=(i+j)/2;
if (x == A[k]) return 1;
if (x < A[k]) j= k­1;
else i= k+1;
}
return 0;
}
Fondamenti di Informatica II
Prof. D. Bruneo
38
Esercizio 5: soluzione
La funzione binSearch implementa l’algoritmo di ricerca dicotomica o binaria su vettori ordinati. È
basato sul principio divide et impera: scelto come elemento pivot quello con indice medio, si confronta
l’elemento in tale posizione nel vettore col valore ricercato, se il confronto dà esito positivo si è
ultimata la ricerca, altrimenti prosegue su una parte del vettore, per vettori ordinati in ordine
crescente, quella a destra del pivot se l’elemento ricercato è maggiore di quest’ultimo, a sinistra nel
caso opposto.
Per calcolarne la complessità utilizziamo una variabile k che conta il numero di cicli effettuati:
inizialmente
i=0;
passo K
passo K-1
passo K-2
…
passo K-z
...
passo j
…
passo 1
i=1/2(n+1);
i=3/4(n+1);
i=7/8(n+1);
…
i=[(2z+1-1)/2z+1](n+1);
i=[(2k-j+1-1)/2k-j+1](n+1);
…
i=[(2k-1)/2k](n+1)=n-1.
Da cui possiamo stimare k: k = log2[(n + 1)/2]
Il che vuol dire che la complessità asintotica sarà:
Fondamenti di Informatica II
O(log2n)
Prof. D. Bruneo
39
Esercizio 6
Calcolare la complessità asintotica in funzione di n della seguente funzione
ricorsiva che calcola il fattoriale di un numero
int fattoriale(int n){
if (n==1)
return 1;
else
return n*fattoriale(n-1);
}
Fondamenti di Informatica II
Prof. D. Bruneo
40
Esercizio 6: soluzione
La formula di ricorrenza è data in questo caso da:
T(n)=
O(1)
per n=1
T(n-1)+O(1)
per n>1
quindi:
T n=T n−1O 1=T  n−2O 1O 1=T  n−3O 1O 1O 1
generalizzando:
k
T n=T n−k ∑ O 1
i=1
la ricorsione si chiude quando T(•) sarà pari ad 1 quindi per k=n-1, quindi
n−1
n −1
n
i=1
i=1
i=1
T n=T 1∑ O 1=O 1∑ O 1= ∑ O 1=O  n
Fondamenti di Informatica II
Prof. D. Bruneo
41