Valutare la complessità in tempo
Complessità in tempo: cosa serve?
✦
Per stimare il tempo impiegato da un programma
✦
Informatica II
Per stimare il più grande input gestibile in ‘tempi ragionevoli’
✦
Per confrontare l'efficienza di algoritmi diversi
✦
Capitolo 2
Analisi di algoritmi
Per ottimizzare le parti più importanti
✦
“Complessità”: “Dimensione” → “Tempo”
✦
Dobbiamo definire “dimensione” e “tempo”!
✦
Adattamento delle slide originali di A.Montresor. Disponibili secondo Creative Commons Attribution-NonCommercial-ShareAlike License.
Dimensione dell'input
Criterio di costo logaritmico:
✦
Definizione di tempo
Prima idea: Tempo = “Wall-clock” time
✦
La taglia dell'input è il numero di bit necessari per rappresentarlo
Il tempo effettivamente impiegato per eseguire un algoritmo
✦
Esempio: moltiplicazione di numeri binari lunghi n bit
✦
✦
Dipende da troppi parametri:
✦
bravura del programmatore
Criterio di costo uniforme:
✦
✦
La taglia dell'input è il numero di elementi che lo costituiscono
✦
linguaggio di programmazione utilizzato
Esempio: ricerca minimo in un array di n elementi
✦
✦
codice generato dal compilatore
✦
velocità di processore e memoria (cache, primaria, secondaria)
✦
In molti casi:
✦
Spesso possiamo assumere che gli “elementi” siano rappresentati da
un numero costante di bit (a volte questa assunzione non è legittima).
In tal caso le due misure coincidono a meno di una costante
moltiplicativa
sistema operativo, processi attualmente in esecuzione
✦
✦
Dobbiamo considerare un modello astratto
✦
Definizione di tempo
Idea migliore: Tempo = “# operazioni elementari”
✦
Modello RAM
Random Access Machine (RAM)
✦
Quali operazioni possono essere considerate elementari?
Memoria:
✦
✦
Quantità infinita di celle di dimensione finita
✦
Esempio: ripensate a min(A, n)
✦
Accesso in tempo costante (indipendente dalla posizione)
✦
✦
In generale serve un modello di calcolo: cioè una rappresentazione
astratta di un calcolatore
✦
Processore (singolo)
Set di istruzioni elementari simile a quelli reali:
✦
Astrazione:
deve semplificare dettagli, altrimenti è inutile (non troppo complesso)
✦
somme, addizioni, moltiplicazioni, operazioni logiche, etc.
✦
istruzioni di controllo (salti, salti condizionati)
✦
Realismo:
deve riflettere la situazione reale (non troppo semplice)
✦
Costo delle istruzioni elementari:
✦
Uniforme, ininfluente ai fini della valutazione (alla fine dei conti
potremo non distinguere tra i diversi tipi di istruzioni)
✦
“Potenza espressiva” :
deve essere abbastanza espressivo da permettere di trarre conclusioni
“formali” sul costo (usando ragionamenti formali, dimostrazioni,…)
✦
Tempo di calcolo di min()
Ogni istruzione richiede un tempo costante per essere eseguita
✦
Le costanti possono essere diverse da istruzione a istruzione
✦
Ogni istruzione viene eseguita un certo # di volte, dipendente da n
✦
Tempo di calcolo di binarySearch()
Il vettore viene suddiviso in due parti
✦
Tempo di calcolo di binarySearch()
Assunzioni
✦
Per semplicità, n =2k è una potenza di 2
✦
Tempo di calcolo di binarySearch()
Soluzione della equazione di ricorrenza per sostituzione
✦
Ricordate che abbiamo assunto che n = 2k ⇒ k = log n
✦
L’elemento cercato non è presente (caso pessimo)
✦
Ad ogni suddivisione, scegliamo sempre la parte destra, di
dimensione n/2 (di nuovo: il caso pessimo)
✦
Due casi
✦
Equazione di ricorrenza
✦
Analisi di algoritmi
Ci sono varie analisi possibili:
Analisi del caso pessimo
✦La più importante
✦Il tempo di esecuzione nel caso peggiore è un limite superiore al
tempo di esecuzione per qualsiasi input
✦Per alcuni algoritmi, il caso peggiore si verifica molto spesso
✦Es.: ricerca di un dato non presenti in un vettore
✦Analisi del caso medio
✦Difficile in alcuni casi: cosa si intende per “medio”?
✦Distribuzione uniforme
✦Il caso medio è spesso tanto difficile quanto quello peggiore
(ad esempio per l’algoritmo insertionSort(), che vedremo)
✦Analisi del caso ottimo
✦Può avere senso se i possibili input presentano una distribuzione
particolare
✦
Limiti asintotici superiori e inferiori
Limitazioni inferiori e algoritmi ottimi
Proprietà degli ordini di grandezza
Per ogni f(n), g(n), h(n), q(n) e ogni c, valgono le seguenti proprietà
Dato un problema
✦
Se trovate un algoritmo A con complessità O(g(n)), avete stabilito un
limite superiore alla complessità del problema - g(n)
✦
Se dimostrate che qualunque algoritmo per il problema deve avere
complessità Ω(f(n)), avete stabilito un limite inferiore alla complessità
del problema - f(n)
✦
Se f(n)=g(n), allora A è un algoritmo ottimo
Riflessività:
✦
c*f(n) è O(f(n)) , Ω(f(n)) e Θ(f(n))
Transitività: se g(n) è O(f(n)) e f(n) è O(h(n)) allora g(n) è O(h(n))
(idem per Ω e Θ)
✦
Simmetria: g(n) è Θ(f(n)) se e solo se f(n) è Θ(g(n))
✦
✦
Simmetria trasposta: g(n) è O(f(n)) se e solo se f(n) è Ω(g(n))
✦
Somma: f(n)+g(n) è O(max{f(n),g(n)})
✦
(idem per Ω e Θ)
Prodotto: se g(n) è O(f(n)) e h(n) è O(q(n)) allora g(n)*h(n) è O(f(n)*q(n))
(idem per Ω e Θ)
✦
Proprietà degli ordini di grandezza
Quindi Θ descrive una relazione di equivalenza. Percui ogni insieme
Θ(f(n)) è una classe di equivalenza detta
classe di complessità
✦
Θ(1) è ordine di grandezza costante
✦
Θ(log n) è ordine logaritmico
✦
Θ(n) è ordine lineare mentre
✦
Θ(n2) è quadratico, Θ(n3) è cubico, in generale Θ(nk) è ordine polinomiale
✦
Θ(kn) è ordine esponenziale
Θ(n log n) è ordine pseudo-lineare
Un algoritmo è reputato efficiente se ha complessità polinomiale; è
invece considerato intrattabile se ha complessità super-polinomiale
✦
Un problema computazionale è considerato difficile se non esiste alcun
algoritmo polinomiale che lo risolva
✦
Confronto fra ordini di grandezza
Un caso classico: l’ordinamento di una sequenza di numeri
Selection sort
Problema dell'ordinamento (sorting)
✦
Input: una sequenza A di n numeri <a1, a2, ..., an>
✦
Output: una permutazione B=<b1, b2, ..., bn> di A tale per cui
b1 ≤ b2 ≤ ... ≤ bn
✦
Algoritmo “naif”
✦
Generare tutte le permutazioni (n!) della sequenza e per ognuna di
esse verificare in tempo O(n) se sia ordinata
✦
Costo totale: O(n n!)
✦
Complessità (caso medio, pessimo, ottimo)
✦
Vediamo degli algoritmi migliori….
Insertion Sort
Algoritmo efficiente per ordinare piccoli insieme di elementi
✦
Insertion Sort - Analisi
Per questo algoritmo:
✦
il costo di esecuzione non dipende solo dalla dimensione...
✦
Come ordinare una sequenza di carte da gioco “a mano”
✦
ma anche dalla distribuzione dei dati in ingresso
✦
Domande
✦
Qual è il costo nel caso l'array sia già ordinato?
✦
Qual è il costo nel caso l'array sia ordinato in ordine inverso?
✦
Cosa succede “in media”?
✦
Merge Sort
Insertion Sort
✦
Merge Sort
Il nucleo di Merge Sort è nel passo combina (merge), definiamola così:
✦
E' basato su un approccio incrementale
(A[1...j-1] ordinato, aggiungi A[j])
✦
merge(A, primo, ultimo, mezzo)
Merge Sort
✦
E' basato sulla tecnica divide-et-impera
dove A è un array di lunghezza n, mentre
✦
primo, ultimo, mezzo sono tre indici tali che
1 ≤ primo ≤ mezzo < ultimo ≤ n
✦
Divide: dividi l'array di n elementi in due sottovettori di n/2 elementi
La procedura merge() assume che i sottovettori A[primo...mezzo] e
A[mezzo+1...ultimo] siano già ordinati
✦
✦
Impera: chiama MergeSort ricorsivamente su i due sottovettori
✦
I due vettori vengono fusi in un unico sottovettore ordinato
A[primo...ultimo]
✦
Combina: fondi le due sequenze ordinate (merge)
✦
Domanda:
✦
Qual è l'idea?
✦
Fondere i due sottovettori “sfruttando” il fatto che sono ordinati.
✦
Seguendo queste indicazioni, sapreste scrivere un algoritmo ricorsivo
che esegua il merge sort?
Merge Sort
Merge Sort: il passo di merge
Come funziona merge():
A
B
✦
1 5 7 + 2 4 6
5 7 + 2 4 6
1
5 7 +
4 6
1 2
5 7 +
6
1 2 4
7 +
6
1 2 4 5
7 +
+
1 2 4 5 6
7
1 2 4 5 6
1 2 4 + 5 6 7
Domanda
✦ Quale è il costo computazionale di merge() ?
✦
Merge Sort: l’algoritmo completo
Merge Sort: un esempio
Il programma completo:
✦
Chiama ricorsivamente se stesso e usa merge() per unire i risultati
✦
Caso base: sequenze di lunghezza ≤ 1 sono già ordinate
✦
Analisi di Merge-Sort
Una assunzione semplificativa
✦
Alcune tecniche per stabilire limitazioni inferiori
✦
n=2k, ovvero l'altezza dell'albero di sottodivisioni è esattamente k
✦
✦
tutti i sottovettori hanno dimensioni che sono potenze esatte di 2
✦
Costi di Merge Sort
Dimensione dei dati
✦Se un problema ha in ingresso n dati e richiede di esaminarli tutti,
allora una limitazione inferiore della complessità è Ω(n)
✦
✦
Eventi contabili
✦Se un problema richiede che un certo evento sia ripetuto almeno n
volte, allora una limitazione inferiore della complessità è Ω(n)
✦
Risoluzione della ricorrenza
✦
Esercizio
✦
Ricavare questo risultato svolgendo l’equazione di ricorrenza
✦
Esempio: sommare n numeri
Esempio: ricerca del minimo richiede almeno n-1 confronti
Oracolo
✦Se un oracolo, utilizzando una certa regola ignota all’algoritmo (anche
se valida solo per certi input), “divina” ad ogni opportunità la
situazione più sfavorevole all’algoritmo, allora combattendo contro di
esso si può individuare una limitazione inferiore
Limitazioni inferiori
ATTENZIONE!
✦
Limitazioni inferiori
ATTENZIONE!
✦
Le tecniche illustrate sono semplici, ma:
Le tecniche illustrate sono semplici, ma:
Si deve fare attenzioni alle assunzioni di base. Per esempio, si ha che:
Si deve fare attenzioni alle assunzioni di base. Per esempio, si ha che:
✦
✦
Ricerca in vettore ordinato: è O(log n), non O(n)
✦
Ricerca in vettore ordinato: è O(log n), non O(n)
Ricerca del minimo in vettore ordinato: è O(1), non O(n)
✦
✦
Ricerca del minimo in vettore ordinato: è O(1), non O(n)
✦
Ecco un esempio più complesso: l’ordinamento di una sequenza
✦
Ecco un esempio più complesso: l’ordinamento di una sequenza
✦
Limitazione inferiore: è Ω(n) - perché?
✦
Limitazione inferiore: è Ω(n) - perché?
Limitazione superiore: è O(n log n)
✦
Possiamo “restringere” questo scarto?
✦
Si dimostra che...
✦
✦
Limitazione superiore: è O(n log n)
✦
Possiamo “restringere” questo scarto?
✦
Si dimostra che...
✦
… Merge Sort è ottimo, in quanto è possibile dimostrare che
Ω(n log n) è un limite inferiore all’ordinamento per gli algoritmi di
ordinamento basati su confronti.
Un approccio diverso: Counting Sort
Come funziona:
✦
Counting Sort
Complessità
✦
I numeri (interi) da ordinare sono compresi in un intervallo [1..k]
O(n+k)
✦
✦
Costruire un array B[1..k] che conta il numero di volte che compare
un valore in [1..k]
✦
Se k è O(n), allora la complessità è O(n)
✦
Ricollocare i valori così ottenuti in A
✦
Discussione su limite inferiore
✦
Counting Sort non è basato su confronti
✦
Abbiamo cambiato le condizioni di base
✦
Se k è O(n3), questo algoritmo è peggiore di tutti quelli visti finora
✦
Esercizi
Esercizio 1
✦
Dato un array A[1..n] di interi e un intero v, descrivere un algoritmo
che determini se esistono due elementi in A la cui somma sia
esattamente v
Esercizio 2
✦
Dato un array A[1..n] di interi positivi, descrivere un algoritmo O(n)
che determini se esistono due elementi in A la cui somma sia
esattamente 17
Esercizio 3
✦
Siano date n monete d'oro, tutte dello stesso peso tranne una
contraffatta perché pesa meno, ed una bilancia con due piatti.
Descrivere un algoritmo per individuare la moneta contraffatta in al
più log n operazioni di pesatura.