3. «Se un problema è umanamente calcolabile, allora
esisterà una macchina di Turing in grado di risolverlo »
La tesi di Church-Turing prende il nome dai matematici Alonzo Church e Alan Turing, che
la introdussero tra gli anni trenta e quaranta. Il lavoro dei due sull'argomento prese avvio
per risolvere il famoso problema di decisione sollevato da David Hilbert. In sostanza, Hilbert
si chiedeva se potesse esistere un algoritmo che potesse decidere circa la verità o la falsità
di qualsiasi enunciato matematico. Indipendentemente, e per mezzo di strade molto diverse,
essi arrivarono agli stessi risultati.
4. «Se un problema è umanamente calcolabile, allora
esisterà una macchina di Turing in grado di risolverlo »
I due matematici giunsero alla conclusione che i
sistemi di calcolo automatico che avevano
proposto erano equivalenti sotto il punto di vista
della potenza computazionale. Ne consegue che
ognuno di questi strumenti incarna, in realtà, il
concetto stesso di algoritmo.
5. Dato un problema, è possibile determinare un insieme,
generalmente finito, di algoritmi in grado di risolverlo.
La scelta di un algoritmo piuttosto che un altro può
essere basata su differenti fattori. Tra essi, quello che
gioca un ruolo essenziale è la complessità
computazionale.
6. L'obiettivo è quello di comprendere le prestazioni
massime raggiungibili da un algoritmo applicato
ad un determinato problema.
8. TEMPO
Tempo di elaborazione
SPAZIO
Quantità di memoria
utilizzata
La complessità computazionale si occupa della
valutazione del costo degli algoritmi in
termini di risorse di calcolo.
La bontà o efficienza di un algoritmo si misura
in base alla quantità minima di:
9. Con il passare degli anni la risorsa SPAZIO è
divenuta MENO RILEVANTE, in quanto i moderni
calcolatori sono dotati di grande capacità di
memoria. Il fattore su cui ci si concentra è il
tempo di esecuzione.
10. DETERMINAZIONE
DEI COSTI
il tempo di esecuzione di un algoritmo è
misurato in numero di operazioni che
l’algoritmo deve compiere per fornire i
risultati.
Tale numero è detto costo dell’algoritmo.
11. DETERMINAZIONE DEI
COSTI
Per individuare il costo di un algoritmo si applicano
regole ben precise:
Le istruzioni SEMPLICI quali
lettura, scrittura,
assegnamento hanno un
costo pari a UNO
costrutti iterativi (WHILE, DO-WHILE)
hanno un costo pari alla somma di:
● costi del test (costo UNO)
● corpo del ciclo, dato dalla somma
dei costi delle singole istruzioni.
ciclo iterato K volte
Costo = CostoTest*K + CostoCorpo*K
Ciclo a controllo in testa iterato K+1 volte
Costo = CostoTest*(K+1) + CostoCorpo*K
12. DETERMINAZIONE DEI
COSTI
Per individuare il costo di un algoritmo si applicano
regole ben precise:
costrutti iterativi FOR hanno un costo pari alla somma di:
● costo di inizializzazione della variabile del ciclo (UNO)
● costo di condizione di fine ciclo (K volte di esecuzione più
test finale UNO)
● costo del corpo del ciclo ( somma dei costi delle singole
istruzioni)
● costo di incremento della variabile del ciclo (K volte di
esecuzione)
Costo = 1 + CostoTest*(K+1) + CostoIncremento*K + CostoCorpo*K
costrutto di selezione IF
ha un costo pari alla
somma di:
● costi del test
(UNO)
● costo delle singole
istruzioni semplici
contenute
all’interno di
ALLORA.
Costo = 1 + MAX(CostoRamoALLORA,
CostoRamoALTRIMENTI)
13. COMPLESSITA’ COMPUTAZIONALE
La notazione T(N) indica la funzione
matematica che esprime la relazione
esistente tra il numero di operazioni di un
programma e la dimensione N dei dati in
input.
La complessità computazionale è la
funzione T(N)
14. ORDINI DI GRANDEZZA E COMPLESSITA’
ASINTOTICA
L’andamento della complessità può
risultare molto complesso. La complessità
asintotica, indicata con un limite, può
monitorare l’andamento di numeri molto
grandi.
lim T(N)
N➝∞∞
15. ORDINI DI GRANDEZZA E COMPLESSITA’
ASINTOTICA
lim T(N)
N➝∞∞
Il simbolo O è detto "o grande".
Oltre a O grande si utilizzano anche le altre notazioni
del calcolo asintotico, ossia omega (Ω) e theta (θ).
La differenza tra O, Ω e θ è la delimitazione
superiore, inferiore o media della funzione T(n)
16. CLASSI DI COMPLESSITA’
Possiamo individuare alcuni ordini di grandezza per le funzioni T(N) che
individuano le cosiddette classi di complessità
Complessità
costante O(1) o O(C)
Complessità
logaritmica O(logN)
Complessità
lineare O(N)
Complessità NlogN O
(NlogN)
Complessità polinomiale
O(N^K)
Complessità
esponenziale O(K^N)
17.
18. COMPLESSITA’ COSTANTE
0(1) O 0(C)
È posseduta dagli algoritmi che eseguono sempre lo stesso numero di operazioni
indipendentemente dalla dimensione dei dati (algoritmi in cui non vi sono costrutti iterativi).
ESEMPIO:
Int main() {
Int a,b,n;
cin>>a>>b;
If(a>b) 1
n=a-b;
Else 1
n=b-a;
}
COSTO 1 + 1 = 2
ESEMPIO: inserimento o estrazione di un elemento
dalla testa di una lista concatenata
19. COMPLESSITA’ LOGARITMICA
O (logN)
A causa dell'uso del sistema numerico binario da parte dei
computer, il logaritmo è frequentemente a base 2 (cioè log2
n,
talvolta scritto lg n).
Tuttavia, con il cambiamento della base per i logaritmi, loga
n e
logb
n differiscono soltanto per un moltiplicatore costante, che
nella notazione O grande è scartato; così O(log n) è la notazione
standard per gli algoritmi in tempo logaritmico
indipendentemente dalla base del logaritmo.
20. COMPLESSITA’ LOGARITMICA
O (logN)
Un algoritmo O(log n) è
considerato altamente
efficiente, in quanto le
operazioni da completare
richieste per istanza
diminuiscono con ogni
istanza.
La ricerca dicotomica (o
ricerca binaria) è un
algoritmo di ricerca che
individua l'indice di un
determinato valore presente
in un insieme ordinato di
dati. La ricerca dicotomica
richiede un accesso casuale
ai dati in cui cercare.
21. COMPLESSITA’ LOGARITMICA
O (logN)
RICERCA BINARIA IN UN INSIEME ORDINATO:
il numero di confronti per cercare un elemento in un vettore, confrontandolo ad ogni passo con l'elemento che si
trova a metà della parte in cui potrebbe trovarsi l'elemento, cresce come il log2
n, dove n è il numero degli
elementi.
- Se gli elementi sono 250000 allora il numero di confronti necessari è pari a log2
250000 = 17,9..., quindi 18 confronti.
- Se gli elementi sono 500000 allora il numero di confronti necessario è pari a log2
500000 = 18,9..., quindi 19 confronti.
- Se gli elementi sono 1000000 allora il numero di confronti necessario è pari a log2
1000000 = 19,9..., quindi 20 confronti.
Possiamo concludere che se il log. in base 2 di un numero è cresciuto di una unità, anche il numero di
confronti necessari è cresciuto di una unità, quindi raddoppiando il numero degli elementi ci vuole solo 1
confronto in più.
22. COMPLESSITA’ LINEARE
0(N)
La complessità lineare O(N) indica
la complessità degli algoritmi che
eseguono un numero di
operazioni proporzionale a N,
cioè proporzionale alle
dimensioni del problema.
ESEMPIO: ricerca sequenziale, somma di
numeri di n cifre
23. COMPLESSITA’ LINEARE
0(N)
ESEMPIO
Si suppone di avere il seguente array A di quattordici elementi:
3 -2 0 7 5 4 0 8 -3 -1 9 12 20 5
La dimensione dellʼinput è la lunghezza dellʼarray (14).
Si cerca lʼelemento "8" con un algoritmo di ricerca lineare
i=0;while ((i <= 13) && (A[i] < > 8))
{i=i+1;}
1 assegnamento + 8 test + 7 cicli = 16
Se si fosse cercato lʼelemento "6"?
Poiché tale elemento non è presente nellʼarray, si sarebbe dovuto percorrere
lʼarray per intero, con complessità:1+ n +1 + n = 2 + 2*n = 30
25. COMPLESSITA’ NlogN
O(NlogN)
ESEMPIO: Algoritmi di ordinamento ottimi.
E’ una funzione dalla forma n · log n (cioè un prodotto di un termine lineare e di
uno logaritmico).
Alcuni famosi algoritmi comprendono:
● Quicksort ("ordinamento rapido") nel caso medio;
● Heap sort ("ordinamento a catasta"), merge sort ("ordinamento per fusione"), introsort
("ordinamento introspettivo"), binary tree sort ("ordinamento ad albero binario"), smoothsort
("ordinamento liscio"), patience sorting ("solitario"), ecc. nel caso peggiore;
● Trasformate di Fourier veloci;
● Calcolo della matrice di Monge;
26. COMPLESSITA’ NlogN
O(NlogN)
Il merge sort è un algoritmo di
ordinamento basato su confronti
che utilizza un processo di
risoluzione ricorsivo, sfruttando la
tecnica del Divide et Impera, che
consiste nella suddivisione del
problema in sottoproblemi della
stessa natura di dimensione via via
più piccola.
27. COMPLESSITA’ NlogN
O(NlogN)
1. Se la sequenza da ordinare ha lunghezza 0 oppure 1, è già ordinata. Altrimenti:
2. La sequenza viene divisa (divide) in due metà (se la sequenza contiene un numero dispari di
elementi, viene divisa in due sottosequenze di cui la prima ha un elemento in più della seconda);
3. Ognuna di queste sottosequenze viene ordinata, applicando ricorsivamente l'algoritmo (impera)
4. Le due sottosequenze ordinate vengono fuse (combina). Per fare questo, si estrae ripetutamente il
minimo delle due sottosequenze e lo si pone nella sequenza in uscita, che risulterà ordinata
FUNZIONAMENTO
28. COMPLESSITA’
POLINOMIALE O(N^K)
Si dice che un algoritmo è in tempo polinomiale
se il suo tempo di esecuzione è limitato
superiormente da un'espressione polinomiale
nella dimensione dell'input per l'algoritmo, cioè,
T(n) = O(nk
) per una qualche costante k.
Tutte le operazioni aritmetiche basilari (addizione,
sottrazione, moltiplicazione, divisione e confronto)
possono essere fatte in tempo polinomiale.
ESEMPIO: Bubblesort
main()
{
int i, j;
for(i=0;i<3;i++) {
for (j=0;j<5;j++)
printf("%d ", i+1);
printf("n");//va a capo
}
ESEMPIO
29. COMPLESSITA’
POLINOMIALE O(N^K)
BUBBLE SORT
L’algoritmo opera su una sequenza non ordinata come se fosse composta da due sotto-sequenze: la prima
ordinata e costituita da elementi non più grandi dell’altra che non è ordinata. Sulla sequenza non-ordinata,
partendo dal fondo, fa risalire il minimo confrontando ed eventualmente scambiando coppie di elementi
adiacenti. Quando nella sotto-sequenza non ordinata non si eseguono scambi (è ordinata) l’algoritmo termina.
31. COMPLESSITA’ ESPONENZIALE
O(K^N)
L’algoritmo della Torre di Hanoi ha complessità Ω(2n).
La Torre di Hanoi è un rompicapo matematico composto da tre paletti e un
certo numero di dischi di grandezza decrescente, che possono essere infilati in
uno qualsiasi dei paletti.
Il gioco inizia con tutti i dischi incolonnati su un paletto in ordine
decrescente, in modo da formare un cono.
Lo scopo è portare tutti dischi sull’ultimo paletto, potendo spostare solo un
disco alla volta e potendo mettere un disco solo su uno più grande, mai su uno
più piccolo.
32. COMPLESSITA’ ESPONENZIALE
O(K^N)
-Possono richiedere tempi di esecuzione grandissimi anche per n piccoli ed
indipendentemente dalla velocità dell'elaboratore.
-Esistono molti problemi per i quali non si conoscono ancora algoritmi non
esponenziali (problemi intrattabili)