Testi
Teoria: Dispense di A.Laurentini, reperibili sul sito del corso
Testo Supplementare :Introduzione agli algoritmi e strutture dati, di T.
Cormen, C. Leiserson, R. Rivest, C. Stein, Edizioni McGraw Hill. .
Programmazione C: A.Bottino, A.Cappadonia, Introduzione alla
programmazione in C , CLUT
1
A.Laurentini-Analisi degli algoritmi
I O
A
L’algoritmo è una soluzione ad un problema. Per un problema ci possono essere più
algoritmi. L’algoritmo ideale, o astratto è un insieme di regole, usualmente espresse
in forma matematica. Ogni algoritmo può a sua volta avere differenti realizzazioni
(diversi linguaggi di programmazione, etc.)
Problema
Real. 1 Real. 2
Esempio:
2
A.Laurentini-Analisi degli algoritmi
…………..
Alg.2: riduci le divisioni: se hai già
Alg.1: dividi n per
2, 3, 4…… n/2 diviso per 2 e 3, non dividere per
6…..(Crivello di Eratostene)
Si noti che sia in ingresso che in uscita un algoritmo può avere pochi o molti dati.
n Verifica se si/no
n è primo
Topologia instradamento
rete,…….. Calcola l’instradamento
ottimo su Internet di un
pacchetto
• Descrizione di algoritmi.
- Informali (con parole, schemi, formule….)
- Pseudo-codice: lo scheletro di un programma. Contiene: istruzioni di controllo;
istruzioni imperative; descrizioni di operazioni ad alto livello. Non contiene
dettagli ovvi (dichiarative,etc.). Può essere simile ad un PASCAL semplificato, o
ad altri linguaggi (C) semplificati
3
A.Laurentini-Analisi degli algoritmi
Memoria
Testa di lettura/scrittura
4
A.Laurentini-Analisi degli algoritmi
m m'
w w'
Descrizione informale:
All’inizio tutti sono disaccoppiati. Ad un certo punto, un maschio m disaccoppiato si
propone alla donna w disaccoppiata che è in testa alle sue preferenze. La coppia viene
formata come provvisoria (un uomo m’ che è più in alto nella lista di w potrebbe
proporsi).
Successivamente, si saranno formate varie coppie provvisorie. Un uomo libero
qualunque si propone alla donna w più in alto nella sua lista. Se w è libera, si forma
una coppia provvisoria. Se w è accoppiata con m’, verifica chi è più in alto nella sua
lista, e si accoppia con lui, lasciando l’altro libero.
L’algoritmo termina quando non c’è più nessuno libero.
Descrizione in pseudocodice
function stableMatching {
Initialize all m M and w W to free
while free man m who still has a woman w to propose to {
w = m's highest ranked such woman
5
A.Laurentini-Analisi degli algoritmi
if w is free
(m, w) become engaged
else some pair (m', w) already exists
if w prefers m to m'
(m, w) become engaged
m' becomes free
else
(m', w) remain engaged
}
}
( http://en.wikipedia.org/wiki/Stable_marriage_problem
http://www.dcs.gla.ac.uk/research/algorithms/stable/)
Si dimostra che:
- L’algoritmo termina dopo al massimo n2 esecuzioni del loop while.
- L’accoppiamento finale è stabile
- Qualunque esecuzione dell’algoritmo da lo stesso accoppiamento (l’ordine degli
insiemi non ha importanza)
- L’algoritmo non trova tutti gli accoppiamenti stabili: ne esistono altri. Uno si
ottiene con le donne che si propongono!
- L’algoritmo come descritto non è simmetrico e favorisce gli uomini!! Diciamo che
una donna w è un partner valido di un uomo m se esiste un accoppiamento stabile
con la coppia (m,w ). Diciamo che w è il miglior partner valido di m se nessuna
donna più alta nella lista delle preferenze di m è un partner valido. L’algoritmo dà
agli uomini il miglior partner valido, ed alle donne il peggior partner valido!!
Gli scopi sono: dato un problema, scegliere tra diversi algoritmi, migliorarli,
comprendere che risorse di calcolo richiedono (tempo, spazio).
Un algoritmo può essere valutato secondo vari criteri:
- correttezza
- lavoro fatto(tempo impiegato)
- spazio(memoria)usata
- semplicità, chiarezza
- ottimalità
- …
-
2.1 - CORRETTEZZA
6
A.Laurentini-Analisi degli algoritmi
Per a) non ci sono regole generali. L’idea può essere intuitiva, o richiedere
dimostrazioni più o meno complesse.
Per b) esistono tecniche empiriche, informali (ad es. i casi prova), e tecniche formali.
Ad es., la tecnica dei loop invariants permette di verificare la correttezza di pezzi di
programma. Non sono tecniche pratiche: il controllo è molto complesso anche per
programmi semplici.
7
A.Laurentini-Analisi degli algoritmi
Esempi
Definiamo:
- ordinare 100 nomi è più lungo che ordinarne 10 (richiede più OF)
- cercare un elemento in una lista di 100 nomi è più lungo che in una lista di 10
- etc.
A parità d’INPUT, dipende dal tipo d’INPUT (ad es., se una lista è già ordinata…)
W(n):=Max{w(I)|I∈Dn}
N.B.
- la complessita’ nel caso pessimo è utile o indispensabile in molti casi pratici (es.
applicazioni REAL TIME)
- è spesso facile da calcolare
- senza altre precisazioni, con complessità si intende sempre complessità nel caso
pessimo
A(n) = ∑ p( I )w( I )
I ∈Dn
Esempio 1
9
A.Laurentini-Analisi degli algoritmi
w( I ) = i
n
1 1 n(n + 1) n + 1 n
n
A(n) = ∑ p ( I i )w( I i ) = ∑ i = = ≅
i =1 i =1 n n 2 2 2
b) Altra ipotesi: x presente nella lista una sola volta con probabilità q; se presente,
posizioni equiprobabili
n
q q n(n + 1) q(n + 1)
A(n) = ∑ i + (1 − q)n = + (1 − q)n = + (1 − q)n
i =1 n n 2 2
n +1
q = 1 ⇒ A(n) =
2
1 n +1 n 3
q = ⇒ A(n) = + ≅ n
2 4 2 4
N.B.
- Il worst case non dipende dal particolare algoritmo di scansione (e’ lo stesso
qualunque ordine di scansione si segua)
- l’analisi del caso medio può complicarsi ulteriormente (ad es., x può essere presente
più volte……)
Esempio 2
- Algoritmo:
cij = ∑ aik bkj
k =1
- OF= moltiplicazione
La teoria è analoga a quella del lavoro fatto. Lo spazio usato si può definire, in
funzione dell’INPUT, sia nel caso pessimo che nel caso medio.
Nell’analisi si trascura:
10
A.Laurentini-Analisi degli algoritmi
2.4 - SEMPLICITÀ
Caratteristica importante: permette una scrittura più agevole del programma, debug e
modifiche più semplici. Tuttavia è difficile da quantificare.
2.5 - OTTIMALITÀ
Ogni problema ha una sua complessità intrinseca, cioè un lavoro minimo al di sotto
del quale non si può andare. Il confronto tra algoritmi va fatto a parità di condizioni,
quindi entro classi in cui è ammessa la stessa operazione fondamentale.
• Definizione: Un algoritmo è ottimo (nel worst case) se, nella sua classe di algoritmi
non esiste un altro algoritmo che (nel worst case) faccia meno OF.
- se F(n) <WA(n), non si può dire niente. Potrebbe esistere un LOWER BOUND
F’(n)> F(n).
WA(n)
F(n)
11
A.Laurentini-Analisi degli algoritmi
Max:=L[1]
For index:=2 to n do
If Max<L[index] then
Max:=L[index]
end{if}
end{for}
- Lavoro fatto dall’algoritmo nel caso pessimo: W(n)=n-1 confronti (è uguale anche
ad A(n))
- LOWER BOUND- Se un elemento è max, n-1 elementi non sono max. Per
decidere che uno di questi non è max, ci vuole almeno un confronto. In totale,
almeno F(n)= n-1 confronti.
N.B.
L’algoritmo non è ottimo: sono stati trovati altri algoritmi migliori nella stessa classe.
Es:
- Alg. di Winograd (1968) W(n)=(n3/2) + n2
- Alg. di Strassen(1969) W(n)=n2.81
- Alg. di Coppersmith e Winograd W(n)=n2.376
12
A.Laurentini-Analisi degli algoritmi
Non sappiamo però se quest’ultimo algoritmo è ottimo (non sono stati trovati altri
LOWER BOUNDS)
Ricordiamo ancora che, quando non si dice esplicitamente, si tratta di ottimalità nel
caso pessimo.
13
A.Laurentini-Analisi degli algoritmi
3. CLASSIFICAZIONE ASINTOTICA
• Motivazioni
Qual è il migliore? I tempi di esecuzione saranno T’≅ c’2n, T”≅ c”5n. Non sappiamo
dirlo.
E’ ragionevole allora classificarli in una stessa classe di complessità(asintotica).
Possiamo anche dire che sono dello stesso ordine di complessita’.
Supponiamo invece:
c’n3
c”n2
n0=c”/c’
14
A.Laurentini-Analisi degli algoritmi
3.1-NOTAZIONI Ο, Ω, Θ, ο, ω
Definizione: O(f(n))= O(f):= insieme delle funzioni g tali che esiste una costante
positiva c ed un n0 tali che g(n)≤ cf(n) per n≥ n0 .
f f'
lim = lim se f’ e g’ esistono
n →∞ g n → ∞ g'
Esempio 1
f=n3/3 g=37 n2+120n+17
si ha
g∈ O(f) (ma f∉ O(g)
Infatti
a) per n≥78 g< f
oppure
g 37n 2 + 120n + 17 74 240 34
b) lim = lim 3
= lim( + 2 + 3 ) = 0
n →∞ f n →∞ n n→ ∞ n n n
2
Esempio 2
f= n2 g=nlgn
15
A.Laurentini-Analisi degli algoritmi
lg e
g n lg n lg n n =0
lim = lim 2 = lim = L → = lim
' Hopital
n →∞ f n →∞ n n →∞ n n→∞ 1
da cui
g∈ O(f) ; f∉ O(g)
• Notazione Ω
Ω(f):= insieme delle funzioni g tali che esiste un c ed un n0 tali che g(n)≥cf(n) per
n ≥n0
oppure
g g
g∈ Ω (f) se lim = c ≠0 oppure lim =∞
n →∞ f n →∞ f
E’ l’insieme delle funzioni che crescono almeno quanto f.
• Notazione Θ
Θ(f):=O(f)∩Ω(f)
oppure
g
g∈ Θ (f) se lim = c ≠0
n →∞ f
E’ l’insieme delle funzioni che crescono come f.
• Notazione o
o(f):= O(f)-Θ(f)
E’ l’insieme delle funzioni che crescono meno di f.
• Notazione ω
ω(f):= Ω(f)-Θ(f)
E’ l’insieme delle funzioni che crescono più di f.
Ω(f) ω(f)
Θ(f)
16
A.Laurentini-Analisi degli algoritmi
O(f) o(f)
3.2 - PROPRIETÀ DI Ο, Ω, Θ, ο, ω
Θ(10n3+3n3+5nlgn+57)= Θ( n3)=……………….
Per descrivere questa classe di complessità usiamo la funzione più semplice della
classe: n3.
Se f∈Θ (n) diciamo che f e’ lineare, se f∈Θ (n2) diciamo che f e’ quadratica, etc.
O(1) indica funzioni limitate da una costante.
17
A.Laurentini-Analisi degli algoritmi
4 - RICHIAMI MATEMATICI
• Notazioni “floor” x e “ceiling” x . Floor di x è il maggior intero che non supera
x, ceiling di x è il minor intero maggiore o uguale di x.
• Fattoriali
Ovviamente n!<nn . Una approssimazione piu’ stretta e’ data dalla formula di Stirling:
n
n 1
n!= 2πn 1 + Θ
e n
da cui si ottiene:
n!∈o(nn)
n!∈ω(2n)
lg(n!) ∈ Θ(n lgn)
18
A.Laurentini-Analisi degli algoritmi
ed anche:
1
n n+
n n 12 n
2πn ≤ n!≤ 2πn
e e
• Sommatorie
- Interi consecutivi:
n
n(n + 1)
∑i =
i =1 2
- Quadrati consecutivi:
n
2n3 + 3n 2 + n n3
∑i = 2
6
≅
3
i =1
- Potenze di 2:
k
∑2
i =0
i
= 2 k +1 − 1
- Somme geometriche
k
1 1 a k +1 − 1 k
∑ i
= 2− k ∑a = a −1 i
i =0 2 2 i =0
- Funzioni decrescenti
b +1 b
f ( x) dx ≤ ∑ f (i ) ≤ ∫
b
∫a
i=a
a −1
f ( x )dx
a b b+1 a-1 a b
- Funzioni crescenti
b b b +1
∫ f ( x) dx ≤ ∑ f (i ) ≤ ∫ f ( x) dx
a −1 a
i=a
Esempio 1
Funzione decrescente.
19
A.Laurentini-Analisi degli algoritmi
n
1 n dx
∑ ≤ ∫ = [ln x ]1 = ln n − ln 1 = ln n
n
i =2 i
1 x
ed anche:
n
1 n +1 dx
∑ = [ln x ]2 = ln(n + 1) − ln 2
n +1
i =2 i
≥ ∫2 x
da cui
n n
1 1
∑
i=2 i
≅ ln n ⇒ ∑
i =1 i
≅ 1 + ln n
Esempio 2
Funzione crescente.
n n n
= n lg n − n lg e + lg e ≥ n lg n − 1.44n
20
A.Laurentini-Analisi degli algoritmi
Utilità degli ADT. Se sono già forniti gli operatori necessari per gestire i dati, devo
occuparmi solo dell’algoritmo. Di conseguenza:
- il codice è più compatto
- il codice è portabile
- posso cambiare le routine che gestiscono i dati senza cambiare l’algoritmo
5.1-
5.1 LISTE
LISTA: Insieme ordinato di elementi dello stesso tipo (detti talora record):
a1, a2, …………..an,
- n=0, lista vuota
- a1 := testa (head) della lista
- an := coda (tail)
- esiste una posizione data dall’indice:
ai precede ai+1 ∀i, 1≤i<n
e segue ai-1 ∀i, 1<i≤n
- può essere utile avere eol(L), posizione (o indice ) successivo all’ultimo elemento
21
A.Laurentini-Analisi degli algoritmi
NIL
Inserzione:
Cancellazione:
Sono O(1)(non bisogna spostare gli elementi successivi) se sono già posizionato nel
punto giusto. Posizionarsi è (senza altri ausili) O(n), quindi Locate e Retrieve sono
O(n).
L’uso dello spazio disponibile è efficiente, a parte l’overhead dei puntatori.
Di solito si usano elementi aggiuntivi
22
A.Laurentini-Analisi degli algoritmi
Liste circolari.
E’ un particolare tipo di lista. Si può inserire solo da una parte, e prelevare dall’altra.
23
A.Laurentini-Analisi degli algoritmi
Può essere realizzata mediante puntatori o vettori. Nel caso di un vettore, questo è
considerato richiuso su se stesso (buffer circolare). Sono necessari due puntatori.
5.2– ALBERI
Sono un particolare tipo di grafi (si veda più avanti), precisamente grafi aciclici
(senza cammini chiusi).
Albero binario:=
Radice
L R 1) vuoto
oppure
2) nodo radice più due lati L(left) e
Albero Albero R(right) collegati a due alberi
binario binario binari. La radice è detta anche
Albero nodo padre (parent) e le radici dei
binario sotto-aberi di sinistra e destra nodi
figli (child)
• Definizioni
-grado di un nodo: numero di sotto-alberi non vuoti collegati (0, 1, 2)
-foglia: nodo di grado 0
-nodo interno: ha grado ≠0
-livello di un nodo: è 0 quello della radice, è quello del padre +1 per gli altri
-albero completo: tutti i nodi interni hanno grado 2, tutte le foglie sono al max. livello
-profondità p: massimo livello
N.B. Su alcuni testi, livello della radice =1 (tutti i livelli aumentano di 1)
Esempi.
Liv.0 A A
Liv.1 B C B C
Nodi interni D E F G
D E F 24
Albero completo
A.Laurentini-Analisi degli algoritmi
Liv.2
Liv.3
Liv.4
• Le informazioni sono di solito collegate ai nodi. Qualche volta vi sono informazioni
collegate anche ai lati.
• L’albero binario come ADT. Sono possibili numerose operazioni associate. Ad es.,
dato un nodo: trovare il padre, trovare il figlio sinistro ed il figlio destro….
• Rappresentazioni
-vettori.
Particolarmente semplice e compatta se l’albero binario è completo: si può
memorizzare per livelli, da sinistra a destra. Ogni nodo ha una posizione (indice)
predefinita, non sono necessari puntatori.
E’ facile vedere che: k
- la radice ha indice 1
- i figli di un nodo con indice k hanno indici 2k (L) e 2k+1 (R).
- il nodo con indice k si trova al livello lgk 2k 2k+1
l l+1
- i nodi del livello l hanno indici da 2 a 2 –1
-…..
Queste proprietà rendono semplici molte operazioni, come la ricerca del padre o dei
figli.
• Proprietà
- al livello l ci sono al massimo 2l nodi
25
A.Laurentini-Analisi degli algoritmi
Esempi.
Applicando i tre tipi di visite all’albero A della pagina precedente, si ottengono i
seguenti ordinamenti dei nodi:
- preorder(v)⇒ ABDECFGHI
- inorder(v) ⇒DBEAFHGIC
- postorder(v)⇒DEBHIGFCA
Sono come gli alberi binari, tranne che i figli di un nodo possono essere più di 2.
I termini: padre, figli, radice, livello, profondità, foglia, nodo interno, hanno lo
stesso significato che per gli alberi binari.
Se un albero ha grado k ,:
- al livello l ci sono al massimo kl nodi
- se la profondità è p, il max. numero di nodi è 1+k + k2 +…… kp =(kp+1–1)/(k-1)
26
A.Laurentini-Analisi degli algoritmi
Possono essere rappresentati con liste multiple (un puntatore per ciascuno dei figli)
ESERCIZI E PROBLEMI
2.5 2
• Un algoritmo richiede f(n)= 15 n +17 n lgn + 136n operazioni fondamentali.
Qual è la sua complessità asintotica? Quali delle asserzioni seguenti è vera?
f(n) ∈ Ω( n2.5) f(n) ∈ω( n2) f(n) ∈o( n2) f(n) ∈O( n3 )
• Dati tre numeri reali a, b, c diversi tra loro, scrivere un algoritmo che determini il
numero mediano. Si determini il numero di confronti nel caso pessimo ed in quello
medio.
• Scrivere un algoritmo che determini il secondo più grande elemento in una lista di
n elementi
( si potrebbe usare due volte l’algoritmo che determina il massimo in una lista,
facendo così in totale sempre (n-1)+(n-2)= 2n -3 confronti. Si può fare di meglio nel
caso pessimo? E nel caso medio?)
27
A.Laurentini-Analisi degli algoritmi
Il puzzle fu inventato dal matematico francese Édouard Lucas nel 1883. Si tratta di
spostare la pila di dischi dal primo perno al terzo , mantenendo la pila ordinata e
facendo solo mosse lecite, cioè:
- spostare un disco per volta
- mettere un disco solo su un perno vuoto, o sopra un disco più grande
Il problema viene usato per illustrare algoritmi recursivi. La soluzione recursiva è:
- applica recursivamente l’algoritmo per spostare n-1 dischi sul perno centrale
- muovi il disco rimasto sul terzo perno
- applica di nuovo l’algoritmo per spostare sul terzo perno gli n-1 dischi del
perno centrale
In pseudocodice (Src, Aux, Dst sono i tre assi)
HanoiTowers(N, Src, Aux, Dst)
if N is 0
exit
else
HanoiTowers(N-1, Src, Dst, Aux)
Move from Src to Dst
HanoiTowers(N-1, Aux, Src, Dst)
Determinare la complessità dell’algoritmo (si scriva la relazione ricorrente che la
determina) .
Se, come nella leggenda, i monaci buddisti lavorano con 64 dischi d’oro spostando un
disco al secondo, ed il mondo finirà quando avranno finito di spostare la pila,
dobbiamo preoccuparci?
28
A.Laurentini-Analisi degli algoritmi
a
b c
d f
e g h
i l m n
29
A.Laurentini-Analisi degli algoritmi
xi ≤ xi+1 i=1,…..n
Problema: dato x, trovare la sua posizione i nella lista (se non c’è, si fornisce 0).
In realtà di solito si cerca un elemento di informazione (record) di cui x è un campo
(chiave di ricerca):
x Informazione associata
6.1.1– ALGORITMO 1
• Caso pessimo. W(n)=n-1 , come nella ricerca sequenziale, poiché x può essere
uguale o maggiore dell’ultimo elemento xn.
• Caso medio. Per la ricerca sequenziale era A(n)≅3n/4 per p=0.5 ( prob. di elemento
presente nella lista).
30
A.Laurentini-Analisi degli algoritmi
Ipotesi:
- p=0.5
- se x∈lista, posizioni equiprobabili (prob. per ciascuna posizione= 1/2n)
- se x∉lista, gli intervalli tra gli elementi sono equiprobabili (prob. =1/2(n+1))
x1 x2 x3 xn
n n
1 1 1 n
A( n) = ∑ i+∑ i+ n≅
i =1 2n i =1 2(n + 1) 2( n + 1) 2
n=5
n=6
31
A.Laurentini-Analisi degli algoritmi
In ogni caso, la lista esaminata nel passo successivo è lunga, nel worst case, n/2.
W(n)= 1+W(n/2)
W(1)=1
dà la soluzione
W(n)= 1+ lgn
W(n)= 1+ lgn
A(n)≅1/2 + lgn∈Θ(lgn).
32
A.Laurentini-Analisi degli algoritmi
i
x<xi x>xi
Nodo generico:
1 2 3 4 5 6 7 8 9 10
5
2 8
1 3 6 9
4 7 10
Si ha che:
numero dei confronti fatti nel worst case = profondità dell’albero +1
p ≥ lg N ≥ lg n
33
A.Laurentini-Analisi degli algoritmi
e quindi
• Ipotesi:
- lista di n elementi con campo chiave (key) o sole chiavi di ordinamento
- OF: confronto a 3 vie (=, <, >). Gli spostamenti non contano.
- ordinamento voluto: non decrescente
7. 1- INSERTION SORT
• Descrizione algoritmo
Opero per scambi successivi. Ad ogni passo inserisco nel modo seguente una nuova
chiave nella lista ordinata :
34
A.Laurentini-Analisi degli algoritmi
- considero la prima chiave della lista da ordinare; la confronto con l’ultima delle
chiavi ordinate
- se è ≥, è già al posto giusto e considero la prossima chiave da ordinare
- se è <, la scambio con questa, la confronto con la penultima delle chiavi da
ordinare. Procedo per scambi finché non è inserita al posto giusto tra le chiavi già
ordinate.
Sono 2 loop :
- uno esterno per ognuna delle chiavi da inserire (n-1)
- uno interno per l’inserimento tra le chiavi già ordinate.
Per ciascuna delle n-1 chiavi da inserire si fanno tutti i confronti possibili con quelle
già ordinate.
n
n(n − 1)
W (n) = ∑ (i − 1) = ⇒W(n)∈Θ(n2)
i=2 2
35
A.Laurentini-Analisi degli algoritmi
i −1 1 1 1 i −1 1 i +1 1
∑ j =1
i
j + (i − 1) = ∑ j =1 j + 1 − =
i i i 2
−
i
n2
A(n) ≅ ∈ Θ( n 2 )
4
Il SORT può essere in place se ogni volta la chiave selezionata si scambia con
l’ultima delle chiavi non ordinate.
Per ogni selezione su di una lista di i chiavi si fanno i-1 confronti. Quindi:
n
n(n − 1)
W (n) = ∑ (i − 1) = = A(n)
i=2 2
Il numero di confronti del caso pessimo e medio sono uguali, ed esattamente uguali a
quelli dell’insertion sort nel caso pessimo.
• BUBBLE SORT
Questo SORT fa diversi passi sulla lista, confrontando chiavi contigue e
scambiandole se fuori ordine. Si inizia confrontando le prime due chiavi,
scambiandole se non sono ordinate. Si confronta poi la seconda con la terza,
scambiandole se necessario, la terza con la quarta, etc.. Alla fine di questo passo, la
chiave maggiore è arrivata in fondo alla lista.
36
A.Laurentini-Analisi degli algoritmi
L’algoritmo si riapplica alla lista dei primi n-1 elementi, poi ai primi n-2, etc.
Non si prosegue se su una di queste sottoliste non si fanno più scambi.
Anche in questo caso, nel caso pessimo ( lista ordinata al contrario), il numero di
confronti è uguale a quello del MAXSORT e a quello del caso pessimo dell’insertion
sort.
• Una lista può essere ordinata in n! modi. Le inversioni sono le coppie di elementi
scambiati. Ad es., nella lista
2, 4, 1, 5, 3
ci sono 4 inversioni: (2,1), (4,1), (4,3), (5,3).
• Worst case
Il numero massimo di inversioni si ha per una lista ordinata inversamente:
no inversioni: n(n-1)/2
Ne consegue che bubble SORT e insertion SORT sono ottimi in questa categoria!
• Caso medio.
Quante sono le inversioni in media? Supponiamo permutazioni equiprobabili.
Consideriamo una permutazione e la sua trasposta (Es. 2 4 1 5 3 ⇔ 3 5 1 4 2).
Per ogni possibile coppia di indici, se c’è un’inversione in una permutazione, non c’è
nella trasposta.
In totale, le coppie di indici sono n(n-1)/2, e quindi mediamente le inversioni sono
n(n-1)/4.
Quindi bubble SORT e insertion SORT sono ottimi anche nel caso medio.
Conclusione: per avere algoritmi migliori ci vogliono SORT che rimuovano più di
una inversione per confronto.
37
A.Laurentini-Analisi degli algoritmi
Se e’ possibile:
• Il Quicksort
L’idea generale è di dividere la lista in due sottoliste, una di chiavi più piccole, l’altra
di chiavi più grandi. A questo scopo, si considerare un elemento x della lista, e si
costruiscono due sottoliste, la prima costituita dagli elementi ≤ di x , la seconda da
quelli ≥ di x (procedura Split). Si applica recursivamente il Quicksort alle due
sottoliste.
first last
x
Split
first splitpoint last
≤x x ≥x
Quicksort Quicksort
Pseudocodice (recursivo):
38
A.Laurentini-Analisi degli algoritmi
procedure Quicksort
begin
if first<last then
Split(first, last, splitpoint);
Quicksort(first, splitpoint-1);
Quicksort(splitpoint+1,last);
end{ if }
end{ Quicksort}
La procedura Split:
la lista all’inizio
first last
x ?
splitpoint unknown
a metà
first last
x <x ≥x ?
splitpoint unknown
al termine
first last
x <x ≥x
splitpoint
Pseudocodice:
procedure Split
begin
x:=L(first)
splitpoint:=first
for unknown:=first+1 to last do
if L(unknown)<x then
splitpoint:=splitpoint+1
Interchange(L(splitpoint),L(unknown))
end{ if }
end{ for}
Interchange(L(first), L(splitpoint))
end{ Split}
• Worst case
39
A.Laurentini-Analisi degli algoritmi
Il caso peggiore si ha quando Split non divide in due parti, perché la prima chiave è la
più piccola (o la più grande). Quindi il caso pessimo si ha con lista già ordinata, o
ordinata al contrario (scegliendo x come primo elemento)
In tal caso, il numero totale di confronti è
n
n(n − 1)
W (n) = ∑ (k − 1) =
k =2 2
• Miglioramenti di Split
Il problema è di dividere ogni volta circa a metà. Esistono tecniche migliori per
scegliere la chiave x. Esse rendono estremamente improbabili casi vicini al pessimo.
• Spazio usato.
Il quicksort non è in-place. Ad ogni iterazione di Split occorre memorizzare (in uno
STACK) gli estremi delle sotto-liste. Nel caso peggiore, lo spazio aggiuntivo
richiesto è Θ(n).
A B C=A+B
+ =
a1 an b1 bm
• Algoritmo. Confronto a1 e b1. Trasferisco il più piccolo nella prima posizione della
lista C. Proseguo confrontando ogni volta i due elementi più piccoli di quello che è
rimasto delle liste A e B. La realizzazione è semplice, con tre puntatori, uno alla lista
C che si incrementa ogni volta, ed altri due in A e in B, di cui ogni volta uno solo si
incrementa.
40
A.Laurentini-Analisi degli algoritmi
Se ad un certo punto una lista si è svuotata, il rimanente dell’altra può essere copiato
in C senza ulteriori confronti.
• Variante. A è una lista concatenata, in cui inserisco uno dopo l’altro i bi.
• Caso pessimo. Quando le liste finiscono insieme si fanno tutti i confronti possibili:
W(n)= n+m-1
L’algoritmo è lineare, ed è facile dimostrare che è ottimo.
• Caso ottimo. Si ha quando tutti gli elementi di una lista sono maggiori (o minori)
di tutti quelli dell’altra. In questo caso il numero di confronti è:
Min[n,m]. Si può ridurre il numero dei confronti ad O(1) se si confrontano subito gli
estremi delle liste.
N.B. Un algoritmo lineare, che richieda di considerare tutti gli elementi dell’INPUT,
è sempre ottimo.
1 A n n+m 1 B m
7. 6- MERGE -SORT
• Idea generale: divido la lista in due parti uguali, applico a ciascuna recursivamente
il MERGE-SORT, ricombino con MERGE
41
A.Laurentini-Analisi degli algoritmi
merge-sort merge-sort
merge
sort merge
W(1) =0
42
A.Laurentini-Analisi degli algoritmi
……………………………………………………………………..
2 2
n-n/4 …………………………………………..
n-n/2 …………………………………………..
10 100 ≅30 ≅3
100 104 ≅600 ≅15
1000 106 ≅104 ≅100
106 1012 ≅2x107 ≅5x104
E’ possibile far meglio di Θ(nlgn)? No, nella classe di algoritmi che usano il
confronto a 3 vie.
• Lower bound.
Ad ogni algoritmo della classe e ad ogni dimensione dell’INPUT possiamo associare
un albero binario di decisione. I nodi interni sono i confronti, le foglie le
permutazioni (i vari ordinamenti possibili). Es.:SORT di x1 x2 x3; n!=3!=6
permutazioni.
x2: x3 x1: x3
x1<x3 x1>x3
x2<x3 x2>x3 43
x1 x2 x3 x:x x2 x1 x3 x2: x3
A.Laurentini-Analisi degli algoritmi
Il numero delle foglie deve essere ≥ n! Se fosse maggiore, ci sarebbero più foglie
uguali. Poiché l’albero è deterministico, solo una delle foglie uguali potrebbe essere
raggiunta, le altre non lo sarebbero mai.
Possiamo quindi sempre ricondurci ad esattamente n! foglie.
ma abbiamo anche:
lg (n!) ≥ (n/2)lg(n/2)
LB(n) ∈ Θ(nlgn).
lg n! = ∑ j =1 lg j ≥ n lg n − n lg e ≅ n lg n − 1 .44 n
n
44
A.Laurentini-Analisi degli algoritmi
• Idea: Devo ordinare dei nomi. Li metto in 26 sottoliste (buckets) a seconda della
prima lettera. Ordino le sottoliste e ricombino (applicazione del principio divide and
conquer). Si può fare lo stesso usando le cifre di un numero.
• Analisi
Consideriamo la complessità di uno degli algoritmi di SORT già esaminati applicato
all’intera lista o applicato a k sottoliste uguali:
45
A.Laurentini-Analisi degli algoritmi
• Problemi
- la suddivisione in sottoliste, se si usano regole semplici, può non essere è
uniforme ( ad. es, se ordino dei nomi, quelli che cominciano per C sono di più di
quelli che cominciano per Z.
- il numero di sottoliste deve aumentare con n. Se uso le prime lettere, l’aumento
non è continuo: 26⇒262⇒263⇒……
- se uso una recursione completa, devo gestire molti puntatori.
7. 7. 2 – RADIX SORT
• Algoritmo.
Si distribuiscono i numeri negli m buckets contigui in base all’ultima cifra (si usano
quindi 10 buckets se le chiavi sono numeri decimali).
Si fanno tanti passi quanti sono i caratteri della stringa, ridistribuendo ogni volta le
chiavi in m buckets secondo la cifra considerata, conservando, a parità di cifra,
l’ordine precedente (vedi esempio).
Al termine dei k passi, le stringhe sono ordinate in ogni bucket, ed i bucket contigui
danno la lista finale ordinata. Infatti, consideriamo due stringhe qualsiasi A e B nella
lista finale con i caratteri seguenti :
c1, c2, ….. ck……….cn, c’1, c’2, ……. c’k, ………. c’n,
46
A.Laurentini-Analisi degli algoritmi
ck< c’k, A è stato messo in un bucket prima di B nel passo n-k+1, riguardante il
caratter k-esimo, e quindi è nella posizione corretta rispetto a B. Tutto ciò è vero per
qualunque coppia A e B, e quindi l’intera lista è ordinata.
• Analisi
Se si usa una struttura dati a liste multiple, come quella indicata in figura, con una
lista concatenata per ogni bucket, la collocazione nei bucket delle chiavi richiede, per
ogni chiave un numero costante di operazioni. Così pure ad ogni passo la
ricombinazione dei bucket per formare una nuova lista richiede un numero costante
di operazioni.
Quindi la distribuzione nei buckets è Θ(n), e poiché avviene un numero di volte fisso
k (tante quante sono le cifre), l’intero algoritmo è Θ(n).
• Esempio numerico
47
A.Laurentini-Analisi degli algoritmi
Puntatore all’elemento
1 corrente
7. 7. 3 – COUNTING SORT
• Idea generale. contare quanti sono gli interi con valore 1, 2,….k. Copiare in un
nuovo vettore tanti 1, 2, … k quanti si sono prima contati.
Esempio
Vettore da ordinare (k=6)
3 6 4 1 3 4 1 4
48
A.Laurentini-Analisi degli algoritmi
1 2 3 4 5 6
2 0 2 3 0 1
• Analisi
- costruzione vettore ausiliario: Θ(n),
- lettura vettore ausiliario e costruzione vettore ordinato (sul vettore originale per
risparmiare spazio: Θ(n+k).
In pratica, si usa il counting SORT quando k è O(n), per cui l’intero algoritmo è Θ(n)
(sarebbe errato usare l’algoritmo per pochi interi molto lontani fra loro)
7. 8 – ALTRI SORT
• HEAP-SORT.
Usa una struttura dati detta HEAP, che vedremo più avanti. Appartiene alla classe
degli algoritmi che fanno confronti, ed è ottimo in questa classe (Θ(nlgn) nel caso
pessimo).
• EXTERNAL SORTING
Si suppone che il numero di chiavi sia tale da non poter risiedere
contemporaneamente in memoria centrale. In questi casi predominano i tempi di
trasferimento con i dischi o nastri. Il problema, con le memorie centrali attuali, ha
ridotta importanza.
ESERCIZI E PROBLEMI
• Un algoritmo di sort viene detto stabile se chiavi uguali rimangono nella stessa
posizione relativa nella lista ordinata. Quale dei seguenti algoritmi è stabile? Per tutti
i SORT non stabili, dare un esempio in cui due chiavi uguali cambiano posizione
relativa.
- Insertion Sort
- Maxsort
- Bubblesort
- Radix Sort
- Quicksort
49
A.Laurentini-Analisi degli algoritmi
• Si abbia una lista di migliaia di elementi in cui solo poche elementi non sono in
ordine, spostati di poche posizioni da quella corretta. Quale, o quali degli algoritmi di
Sort studiati è più adatto?
• Studiare un algoritmo efficiente in place che riordini una lista in modo che tutte le
chiavi negative precedano tutte le chiavi positive.
50
A.Laurentini-Analisi degli algoritmi
• Approccio generale
- divido un problema di dimensione n in a sottoproblemi ciascuno di dimensioni n/b
- ricombino le soluzioni dei sottoproblemi
• Analisi
In generale è:
51
A.Laurentini-Analisi degli algoritmi
lg a
O(n b ) a>bk
W(n)= O (n k lg n) a=bk
O(n k ) a<bk
Esempio
52
A.Laurentini-Analisi degli algoritmi
Problema: colorazione di un grafo G (si colorano i nodi senza che due nodi adiacenti
abbiano lo stesso colore)
Ovviamente è facile passare dalla soluzione dei problemi più difficili alla soluzione
di quelli più facili.
Tutti (salvo uno) i problemi seguenti hanno grande importanza pratica, o sono
varianti e/o semplificazioni di problemi di grande importanza pratica. Come
vedremo, tutti i problemi seguenti hanno una caratteristica comune.
• Bin Packing
E’ il più semplice problema di impaccamento (monodimensionale). Si hanno dei
recipienti (bin) di capacità unitaria , ed n oggetti di dimensioni s1, s2, …. sn, con
0 < si≤ 1.
• Knapsack
Si suppone di avere uno zaino (knapsack) di capacità C (intero positivo), ed n oggetti
di dimensioni s1, s2, …. sn, ciascuno con associato un “guadagno” g1, g2, …. gn (tutti
interi positivi).
53
A.Laurentini-Analisi degli algoritmi
massima disponibile C, vari investimenti possibili di costi si, ciascuno con varie
redditività gi)
• Colorazione di un grafo
Il problema è già stato definito. Qui osserviamo che la colorazione di un grafo
schematizza numerosi problemi di assegnazione, in cui vi siano incompatibilità.
• Job scheduling
Si hanno n job ( lavori) da eseguire uno per volta. Abbiamo dei tempi di esecuzione
t1, t2, …. tn, e delle penalità p1, p2, …. pn, se i job non vengono eseguiti entro le
deadlines d1, d2, …. dn.
Una schedule è una permutazione degli indici, che stabilisce l’ordine di esecuzione
dei job. Per ogni schedule c’è una penalità totale, somma delle penalità dei job che
oltrepassano la deadline.
Problema di ottimizzazione: determinare la penalità totale minima, e trovare la
permutazione ottima.
Problema di decisione. E’ possibile avere una penalità totale ≤k?
54
A.Laurentini-Analisi degli algoritmi
E’ la classe dei problemi di decisione che ammettono nel caso pessimo algoritmi
polinomiali .
Per nessuno dei problemi del paragrafo precedente si conosce un algoritmo
polinomiale.
N.B.
- Gli algoritmi in P rimangono in P se combinati insieme
- La classe P e’ indipendente dal modello computazionale
- La dimensione dell’INPUT e’ la lunghezza minima in caratteri di una stringa che
lo definisce. In alcuni casi si può essere indotti in errore. Esempio:
Problema: un dato intero n è un numero primo? Algoritmo: divido per tutti gli
interi da 2 a n/2. Analisi: si fanno Θ(n) divisioni, ma l’algoritmo non è Θ(n)! La
dimensione dell’ingresso è misurata dal numero di cifre, che sono Θ(logbn)!
Quindi l’algoritmo è in realtà Θ(bn). L’algoritmo descritto è migliorabile, ma tutti
gli algoritmi conosciuti sono esponenziali.
9.3 – LA CLASSE NP
Esempio
Per determinare se un grafo e’ colorabile con k colori, si produce in qualche modo
(anche a caso) una stringa contenente varie occorrenze di k caratteri lunga V. La
stringa descrive una possibile colorazione. Se la colorazione sia valida o no, può
essere facilmente verificato in tempo polinomiale.
55
A.Laurentini-Analisi degli algoritmi
Sono i più difficili della classe NP: se ci fosse un algoritmo polinomiale per uno di
questi problemi, ci sarebbe per tutti i problemi in NP.
Algoritmo per Π1
Esempio.
Π1:= date n variabili booleane xi , almeno una di queste è TRUE?
56
A.Laurentini-Analisi degli algoritmi
la classe di problemi NP Π’ Π
trasformazione polinomiale
57
A.Laurentini-Analisi degli algoritmi
58
A.Laurentini-Analisi degli algoritmi
• Esempio 1
- Esempio:
s=(0.8, 0.5, 0.4, 0.4, 0.3, 0.2, 0.2, 0.2)
0.2
0.2
0.4
0.3
0.8
0.5
0.4
0.2
• Esempio 2
- Problema. Set covering. Dati un set ed alcuni subset: S={x1, x2… xn }; SS1={xp,
… xq }, ..SSm={xl, … xr }; i costi c1, c2… cm; trovare l’insieme di subset che copre
S (ogni elemento di S è in almeno un subset) con somma dei costi minima.
Esempio:
Elementi: a, b, c, d
Insiemi: (a,b), (a,b,d), (b,c), (c,d),
Variabili rappresentanti un insieme nella copertura: q1, q2, q3, q4
Presence function: (q1+ q2)( q1+ q2 + q3 ) )( q3 + q4 ) q4 =……= q1 q3 q4+ q1 q4+…
Soluzioni ottime (costi uguali degli insiemi) q1 q4, q2 q4
Soluzione ottima(costi =dimensione insieme) q1 q4
- Algoritmo approssimato. Usa una tecnica greedy (avida). Ad ogni passo si sceglie
il sottoinsieme che ha il minimo costo (a parità di costi, si sceglie quello che copre
più elementi). Si può dimostrare che:
costo della soluzione data dall’alg. approssimato
≤ 1 + lg r
costo della soluzione data dall’algoritmo esatto
60
A.Laurentini-Analisi degli algoritmi
Esempio:
Caso precedente, costi uguali.
Prima scelta: q2 (copre 3 elementi)
Seconda scelta: q4 (copre l’unico elemento scoperto)
Soluzione: q2 q4
• Backtracking
E’ una tecnica per esplorare l’albero della possibilità in modo da ridurre i casi da
verificare.
- Esempio.
Problema 3-coloring di un grafo (vedere se è possibile colorare un grafo con 3
colori).
Algoritmo esatto: produco tutte le possibili 3-colorazioni (3V), e verifico se sono
ammissibili.
Algoritmo che tende a ridurre i casi. Iniziamo a colorare un vertice in modo
arbitrario, e procediamo colorando gli altri vertici in tutti i modi compatibili con i
colori già assegnati. La procedura può essere rappresentata con l’attraversamento di
un albero: la radice corrisponde allo stato iniziale, ogni lato corrisponde ad una
decisione per un nuovo vertice. Se si raggiunge un nodo non colorabile, si torna
indietro (backtracking) e si esplora un altro ramo.
1B, 2G
4 3B
3R
3 4G 4B 4B
4R
5
2
5R
La soluzione è: 1B, 2G, 3B, 4G, 5R. (sono possibili altre colorazioni equivalenti).
Per un grafo generico, l’albero ha un numero esponenziale di nodi. Con questa
tecnica posso ridurre di molto quelli esplorati.
• Branch-and-bound
E’ un’idea simile per il caso in cui si cerca il massimo o il minimo di una funzione
obiettivo.
Esempio.
61
A.Laurentini-Analisi degli algoritmi
• Gli algoritmi descritti sinora sono seriali ( 1 processore, un’operazione per volta).
Si stanno diffondendo macchine a più processori, capaci di effettuare più operazioni
per volta, e quindi di eseguire algoritmi paralleli. Per sfruttare al meglio le capacità
delle nuove macchine è necessario:
- parallelizzare algoritmi esistenti
- studiare nuovi algoritmi paralleli
62
A.Laurentini-Analisi degli algoritmi
Si supponga di aver trovato un certo T(n,p) con p processori. Usandone solo p/k noi
vorremmo la massima efficienza, e quindi:
T(n,p/k)≅kT(n,p)
Questo si può realizzare, in linea di massima, eseguendo su di un solo processore k
passi consecutivi dell’algoritmo (parallelism folding principle, ). L’idea potrebbe non
applicarsi in alcuni casi.
63
A.Laurentini-Analisi degli algoritmi
n
n/2
n/4 lgn
2
1
T(n, n/2)=lgn
E=(n-1)/((n/2)lgn)≅2/lgn
In totale:
E’ possibile migliorare l’efficienza con una tecnica simile a quella del torneo
modificato. Supponiamo di avere n processori (n potenza di 2). Considero gruppi da 2
elementi. Con n/2 processori trovo n/2 massimi. Di questi faccio gruppi da 4 (n/8
gruppi). Per trovare il massimo in due passi, mi bastano 4x3/2 =6 processori per
gruppo (ne ho 8 disponibili per gruppo)………Si dimostra che, agendo su gruppi da
2, 4, 16, 256 …..elementi i processori sono sempre sufficienti, e quindi il tempo e
l’efficienza sono:
T(n,n)= 2lglgn → E≅n/(n2lglgn)
È importante il diametro del grafo (lunghezza del massimo cammino tra 2 nodi).
65
A.Laurentini-Analisi degli algoritmi
Ad es., per un grafo fatto a griglia nxn, il diametro è 2n. Per un albero di profondità p,
il diametro è 2p.
• Esempio di algoritmo.
Sort di n numeri. Ci sono n processori Pi, ciascuno collegato ad un predecessore e
successore, e contenente un numero. L’algoritmo dispone il numero più piccolo in P1,
..il più grande in Pn. Ogni processore confronta il proprio numero con il vicino di
sinistra, e lo scambia se fuori ordine, e poi con il suo vicino di destra, scambiandoli se
necessario. È facile vedere che: in al massimo n-1 passi l’algoritmo termina.
Ovviamente non si può fare di meglio se si possono solo fare scambi tra elementi
contigui.
a44
a43 a34
a21 a12
a11
66
A.Laurentini-Analisi degli algoritmi
b1 b2 b3 b4 x4 x3 x2 x1
ESERCIZI E PROBLEMI
• Si trovi un algoritmo per verificare se un grafo G(V,E) è 2-colorabile. L’algoritmo
dovrebbe essere lineare Θ(Max(V,E))
• Mostrare che ciascuno dei seguenti problemi di decisione è in NP. A questo scopo,
indicare per ogni problema quello che può essere un’ipotesi di soluzione, e mostrare
come si può verificare in tempo polinomiale che l’ipotesi è corretta.
- bin packing
- circuito hamiltoniano
- CNF-SAT
• Mostrare che il problema della 3-colorabilità può essere ridotto polinomialmente a
CNF-SAT
• Trovare un algoritmo che determini il numero cromatico di un grafo con grado dei
nodi ≤ 2.(l’algoritmo dovrebbe essere lineare in V)
• Trovare un algoritmo polinomiale per determinare se una forma CNF con
esattamente due lettere per ogni somma ha valore TRUE.
• Trovare un algoritmo polinomiale per determinare se un grafo ha una 4-clique
• Dimostrare che, se un problema di decisione bin-packing potesse essere risolto in
tempo polinomiale, allora anche il problema di ottimizzazione potrebbe essere risolto
in tempo polinomiale
• Costruire un esempio di problema Bin Packing in cui l’algoritmo approssimato usa
3 bin mentre ne bastano 2
• Qual è l’efficienza di un algoritmo parallelo per cui T(n, 1) = 100 e T(n, 10)=30?
• Qual è l’efficienza di un algoritmo per trovare il massimo di una lista di 256
elementi col metodo del torneo?
• Se i processori per il caso precedente sono solo 32, per cui i primi confronti non si
possono svolgere tutti in parallelo, quanto vale l’efficienza?
67
A.Laurentini-Analisi degli algoritmi
11- GRAFI
68
A.Laurentini-Analisi degli algoritmi
Rappresentato spesso graficamente con simboli come pallini, circoletti, per i vertici, e
segmenti rettilinei o curvi che congiungono 2 vertici per i lati.
Vi possono essere informazioni associate ai nodi, ai lati, ad ambedue.
Esempio 1:
B E G
A D
C
H
F
V={A, B, C, D, E, F, G, H}
E={(A,A), (A,B), (A,D), (A,C), (C,D), (C,F), (E,G)}
• Se un lato inizia e termina nello stesso vertice, come (A,A) nell’esempio, viene
detto cappio (self-loop)
• Se vi sono più lati tra gli stessi due nodi, si parla di multigrafo, altrimenti il grafo
viene detto semplice (l’aggettivo viene di solito omesso).
Nel seguito, salvo esplicito diverso avviso, ci occuperemo di grafi semplici e senza
self-loops
E ≤V(V-1)/2 ⇒ E∈O(V2)
K3 K4
Esempio 2
69
A.Laurentini-Analisi degli algoritmi
B E G
A D
C
H
F
V={A, B, C, D, E, F, G, H}
E={ 〈A,B〉, 〈A,D〉, 〈A,C〉, 〈C,D〉, 〈C,F〉, 〈E,G〉}
• I lati che hanno un nodo come estremo si dicono incidenti nel nodo. Il numero di
lati incidenti in un nodo si dice grado (degree) del nodo ( nell’Esempio 1, C ha grado
3).
Per un grafo orientato, si distinguono un grado in uscita (out-degree) ed un grado in
ingresso(in-degree) (nell’Esempio 2, C ha out-degree=2, in-degree=1 )
Due nodi si dicono adiacenti (o contigui) se un lato li collega direttamente.
• Un grafo è pesato (weighted) se ci sono dei valori, detti pesi, associati ai lati.
• La distanza tra due nodi è la lunghezza del percorso più breve tra i due nodi.
• Un grafo non diretto è connesso se esiste un percorso tra due nodi qualsiasi. Per
grafi diretti, si esamina il grafo trasformato in non diretto. I grafi di Esempio 1 ed
Esempio 2 non sono connessi.
• Un grafo è detto una foresta se è aciclico (senza cicli) nella sua forma non diretta.
70
A.Laurentini-Analisi degli algoritmi
B E G
A D
C
H
Esempio di foresta F
A D
Esempio di albero F
radice C D
livello 1 C
A D F
livello 2 B A F
livello 3 B
71
A.Laurentini-Analisi degli algoritmi
• Matrice di adiacenza
Se il grafo è pesato:
W(Vi Vj) se Vi e Vj sono collegati (positivo o negativo se grafo orientato)
aij=
una costante se non collegati (ed es, 0 se pesi=capacità, ∞ se tempi di
percorrenza
B E G
A D
C
H
A B C D E F G H
A 1 1 1 1 0 0 0 0
B 1 0 0 0 0 0 0 0
C 1 0 0 1 0 1 0 0
D 1 0 1 0 0 0 0 0
E 0 0 0 0 0 0 1 0
F 0 0 1 0 0 0 0 0
G 0 0 0 0 1 0 0 0
H 0 0 0 0 0 0 0 0
72
A.Laurentini-Analisi degli algoritmi
• Liste di adiacenza
Per ogni nodo si memorizza la lista dei nodi adiacenti. Ad esempio, per il grafo
precedente:
A A B C D Nil
B A Nil
C A D Nil
A
D A CD Nil
E G Nil
A
F C Nil
G E Nil
H Nil
1 2 25 Nil 2 6
25
14 Nil 14
2 3 10 4
1 10 4
3 1 5 Nil
5 18
3
4
A 2 6 3 18 Nil
73
A.Laurentini-Analisi degli algoritmi
- E’ contiene il lato ViVj se nel grafo G(V, E) esiste un percorso dal nodo Vi al
nodo Vj .
Esempio:
B C B C
A E A E
D D
G(V, E) CT(V’, E’)
A2=AxA
a2ij=( ai1 AND a1j)OR( ai2 AND a2j)OR……….
A B C D E A B C D E
A 0 0 1 1 0 A 0 0 0 1 1
B 0 0 1 0 0 B 0 0 0 1 1
2
A= C 0 0 0 1 1 A= C 0 0 0 1 1
D 0 0 0 0 1 D 0 0 0 1 0
E 0 0 0 1 0 E 0 0 0 0 1
Per avere la matrice CT, i cui elementi sono 1 se esiste un percorso di lunghezza
qualunque tra due nodi, e’ sufficiente sommare logicamente (OR) tutte le matrici:
74
A.Laurentini-Analisi degli algoritmi
A B C D E
A 0 0 1 1 1
B 0 0 1 1 1
CT= C 0 0 0 1 1
D 0 0 0 1 1
E 0 0 0 1 1
N.B. Se ci sono n nodi, I cammini sono lunghi al max. n-1 se tra due nodi Vi e Vj di
indici i≠j, sono lunghi al max. n se i=j.
Molti algoritmi richiedono di visitare tutti i vertici di un grafo (una sola volta)
passando da un vertice all’altro tramite lati del grafo. La visita serve anche a trovare
componenti connessi. Esistono due algoritmi principali:
- visita in profondità( depth first)
- visita in ampiezza (breadth first)
Si tratta di costruire un albero ricoprente.
75
A.Laurentini-Analisi degli algoritmi
• Visita in profondità
Descrizione informale dell’algoritmo. Parto da un nodo qualsiasi, lo contrassegno
come visitato, vado ad uno qualunque dei nodi adiacenti, lo contrassegno….
- mi fermo se tutti i nodi sono contrassegnati
- se non ho finito, e tutti i nodi adiacenti sono contrassegnati, torno al nodo
precedentemente visitato e controllo i nodi adiacenti.
Il risultato (ordine di visita) dipende da scelte arbitrarie (in pratica legate alla struttura
dati). Esempio:
A 1 A
2 8
B C D B 11
C D
E I M 3 7 I
H E 6 M 10
H
F L 4 F L
G 9
5 G
a) Realizzazione recursiva:
procedure DepthFirst(V)
begin
visita e marca V
while c’è un vertice W adiacente non marcato do
DepthFirst(W)
end{while}
end{ DepthFirst }
76
A.Laurentini-Analisi degli algoritmi
L’algoritmo tocca tutti i vertici di un grafo connesso. Per assurdo, supponiamo che
uno o più vertici non siano marcati. Poiché il grafo è connesso, almeno uno dei vertici
non marcati è adiacente ad uno marcato: questo però è impossibile.
N.B. Se il grafo non è connesso, bisogna applicare l’algoritmo tante volte quanti sono
i pezzi non connessi
Complessità. Uso la rappresentazione del grafo liste di adiacenza. Per ogni sottolista
dei nodi connessi ad un nodo dato metto un puntatore che mi indica a che punto sono
arrivato nella scansione. Ogni lista è percorsa al massimo una volta, ed ogni lato al
massimo due volte (è presente in due sottoliste). Quindi l’algoritmo è O(V+E).
Se uso invece la matrice di adiacenza, l’algoritmo è O(V2).
procedure BreadthFirst(V)
begin
inizializza la Coda(vuota)
visita e marca V, metti V in coda
while la coda non è vuota do
rimuovi il primo elemento X della coda
for ogni vertice W adiacente a X e non marcato, do
visita e marca W, metti W in coda
end{for}
end{ while }
end{ BreadthFirst }
77
A.Laurentini-Analisi degli algoritmi
A
D C B
E D C
I E D
L M I E
F G H L M I
F G H L M
F G H L
F G H
F G
Definizione. Dato un grafo pesato non orientato, MST:= albero ricoprente i cui lati
hanno somma dei pesi minima.
I pesi debbono essere ≥0, altrimenti si può far tendere a -∞ la somma dei pesi
percorrendo più volte un loop. Nei casi pratici, in cui i pesi rappresentano costi, tempi
di percorrenza, etc., sono sempre positivi
N.B. Possono esserci più MST per uno stesso grafo:
2 1 2 1 1
2
2
• Algoritmo di Dijkstra/Prim
78
A.Laurentini-Analisi degli algoritmi
Esempio:
MST
A 2 B 2
A B
7 4 7
3 6 Scelgo il 3
F G 3 nodo A ⇒ F G
5 1 2 C
H
6 I 4 2
8
2
E D
1
MST
A 2 B
Scelgo il Scelgo il
7 4
nodo B ⇒ 3 6 nodo G ⇒
F G
C
MST
A 2 B Scelgo il nodo I ⇒
7 4 …….. 79
3
F G 3
1 C
A.Laurentini-Analisi degli algoritmi
A 2 B
3
F G 2
5 1 C
H
I 2
2
E D
1
w
v
p q
Possibile realizzazione:
procedure MST(Radice, G)
begin
for tutti i nodi V≠Radice, do
dist(W)=peso(V, Radice), oppure dist(v)=∞ se V non collegato a Radice
end{for}
while ci sono nodi da inserire in MST do
scegli il nodo W con dist(W) minima, aggiungilo a MST
for tutti i nodiV restanti do
80
A.Laurentini-Analisi degli algoritmi
Algoritmo più efficiente. Usa una struttura dati detta coda prioritaria (si veda più
avanti). E’ O(V+ElgV)
Ad ogni passo ho tre insiemi di nodi : quelli già scelti, quelli adiacenti ad uno di
quelli già scelti, gli altri.
a) Inizializzo l’albero SP inserendo V0. Attribuisco a tutti gli altri una distanza da V0
nel modo seguente:
d(V0Vi) = peso (V0Vi) se Vi è adiacente a V0,
d(V0Vi) =∞ altrimenti
b) scelgo ed aggiungo a SP ad ogni passo il nodo Vx per cui d(V0Vx) è minima.
Aggiorno le distanze per i nodi adiacenti a Vx :
d(V0Vi) =min{ d(V0Vi) ; d(V0Vx)+peso(VxVi) }
Continuo con b) finché ci sono nodi
81
A.Laurentini-Analisi degli algoritmi
Vy Vx
SP
Vp
V0
Vq
Vr
Per induzione, suppongo che SP sia un albero di cammini minimi, e dimostriamo che
lo resta aggiungendo Vx al percorso minimo V0 ……Vy . Infatti ho scelto Vx perché
d(V0Vx) è minore di quelle da altri nodi adiacenti a SP, come d(V0Vp), d(V0Vq),
d(V0Vr). Il percorso V0 ……VyVx ha costo minimo perché qualunque altro percorso
da V0 a Vx deve includere d(V0Vp) oppure d(V0Vq) oppure d(V0Vr).
A 2 B 2
A B
7 4 7
3 6 3
F G 3 F G
5 1 2 C
H
6 I 4 2 d(AB)=2
8
2 d(AG)=3
E D
1 d(AF)=7
scelgo AB
A 2 B
7 d(AG)=Min{d(AG);d(AB)+peso(BG)}=3
3 6 4
d(AC)= Min{∞;d(AB)+peso(BC)}=6
F G d(AF)=7
C
scelgo AG
A 2 B
82
7 4
3
F G 3
C
A.Laurentini-Analisi degli algoritmi
d(AF)=7
d(AC)=6
d(AI)=MIN{∞;d(AG)+peso(GI)}=4
d(AH)= MIN{∞;d(AG)+peso(GH)}=6
scelgo GI
d(AE)= MIN{∞;d(AI)+peso(IE)}=6
d(AF)= MIN{d(AF);d(AI)+peso(IF)}=7
d(AH)= MIN{d(AH);d(AI)+peso(IH)}=6
d(AC)=6
scelgo E…………..
A 2 B
7 4
3
F G 3 L’albero dei cammini minimi
1 C
H
I
2
E D
1
11.7 – ALCUNE DEFINIZIONI
• Grafo bipartito := i suoi vertici possono essere divisi in due sottoinsiemi tali che
ogni vertice di un sottoinsieme è adiacente ad un vertice dell’altro.
83
A.Laurentini-Analisi degli algoritmi
A⇔1
A C 1 3
B⇔2
C⇔3 4
D⇔4 D
Esistono in questo caso altri isomorfismi (4x3x2=24 in tutto).
Non si sa se il problema della verifica dell’isomorfismo sia in P o in NP.
Se il grafo è planare, il problema è in P.
⇒ ⇒
K4
K4 è planare, e può essere disegnato con tutti i lati rettilinei, come tutti i grafi planari.
I grafi completi da K5 in poi non sono planari. E’ facile verificare che K5 non è
planare:
2 2 2
1 3 1 3 1 3 84
5 4 5 4 5 4
A.Laurentini-Analisi degli algoritmi
Qualunque grafo Kn per n>5 contiene K5 come sottografo e quindi non è planare.
Esistono algoritmi lineari, e quindi ottimi, per la verifica della planarità (ad. es,
Hopcroft e Tarjan, 1974). La loro descrizione è assai complicata.
Esistono anche algoritmi lineari per il planar embedding, cioè per disegnare senza
intersezioni sul piano un grafo planare.
f3
f1 f2 f2
f4 : faccia f1 f3 85
esterna
infinita
A.Laurentini-Analisi degli algoritmi
• Teorema di Eulero(1752)
Se un grafo planare connesso ha V nodi, E lati e F facce, si ha:
V+F=E+2
N.B. Il teorema si applica a poliedri convessi o isomorfi a poliedri convessi. Infatti gli
spigoli ed i vertici di un poliedro convesso si possono proiettare su di una superficie
sferica formando un grafo planare.
V+F=E+K+1
86
A.Laurentini-Analisi degli algoritmi
• Genus di un grafo
Un grafo non planare potrebbe essere disegnato senza lati che si incrociano su
superfici diverse dal piano o dalla sfera.
Ad esempio, K5 e K33 possono essere disegnati senza incroci sulla superficie di un
toro circolare, o di una sfera con manico (vedi figura successiva).
Si dice che K5 e K33 hanno genus G= 1: sono disegnabili senza incroci su di una
superficie con un foro passante. Se sul piano ci fossero almeno 2 incroci, sarebbe
necessaria una superficie con 2 fori passanti.
V+F=E+(2-2G)
87
A.Laurentini-Analisi degli algoritmi
Due grafi non planari di genus 1 si possono disegnare senza intersezioni su di un toro.
E’ possibile trovare un percorso che attraversi i sette ponti una sola volta e torni al
punto di partenza? A destra il grafo che modella il problema.
• CicloEuleriano di un grafo G:= un cammino chiuso che contiene una sola volta tutti
i lati del grafo
Che la condizione sia necessaria è ovvio: da ogni nodo in cui entro, incluso il nodo di
partenza, dovrò uscire. Ne consegue che il problema dei ponti di Koenigsberg non ha
soluzione.
Per dimostrare che la condizione è sufficiente, diamo una prova costruttiva, che
definisce un algoritmo per la costruzione del ciclo Euleriano.
a) Passo 1. Si decompone il grafo in una serie di cicli semplici. A questo scopo uso
alternativamente due algoritmi:
- Algoritmo 1. Determina un ciclo, usando una visita in profondità arrestata non
appena si torna al primo nodo ( si può dimostrare che almeno un ciclo esiste
sempre in un grafo con nodi di grado≥2 ). Elimino i lati visitati ed eventuali nodi
isolati. Tutti i nodi rimasti sono ancora di grado pari.
- Algoritmo 2. Se ci sono ancora lati, determino i componenti connessi del grafo (
eliminando i lati il grafo può diventare non connesso). La determinazione dei
componenti connessi si può fare applicando una visita (in ampiezza o profondità)
tante volte quanti sono i componenti connessi. Si torna quindi ad applicare
l’Algoritmo 1 ai componenti connessi trovati.
b) Passo 2. Si crea il ciclo Euleriano percorrendo i cicli trovati in a) con la regola
seguente. Si inizia a percorrere un ciclo qualunque, eliminando ogni volta lati già
percorsi. Se da un nodo parte un lato di un altro ciclo, si percorre questo. Se nel
percorrere il nuovo ciclo ne incontro un altro nuovo, si inizia a percorrerlo. Se si
incontra un ciclo vecchio (già percorso parzialmente), si continua con il ciclo
corrente.
89
A.Laurentini-Analisi degli algoritmi
Complessità. L’Algoritmo 1 considera una sola volta tutti i lati ed è lineare. Lo stesso
è per l’Algoritmo 2, e quindi per il passo 1. Anche il passo 2 considera ogni lato una
sola volta, ed è lineare. Nel passo 2, i nuovi cicli incontrati vanno messi in uno
STACK, da cui si estraggono ogni volta che il ciclo corrente è terminato.
Esempio
Costruzione dei cicli:
1 3 1
2 3
2
4 5
Ciclo 1
8 7
8
6 6
1 3
2 1
3
Ciclo 2
4 5
4 5
8 7
6
2 2
4 5 4 5 Ciclo 3
8 7
6 6
4 4
Ciclo 4
8 7 7
8
Costruzione del ciclo Euleriano.
Si parta (arbitrariamente) dal nodo 1 del Ciclo 1. Si incontra subito il Ciclo 2 che si
inizia a percorrere in una direzione arbitraria, ad es. 13. In 3 si incontra il Ciclo 1
(vecchio) e si continua percorrendo il lato 35 del Ciclo 2. In 5 si incontra il nuovo
Ciclo 3, e si percorre 56. In 6 non si incontrano cicli nuovi, e si percorre 64. In 4 si
incontra il nuovo Ciclo 4, che si percorre completamente (lati 47,78,84). Completato
il Ciclo 4, si torna a percorrere il Ciclo 3 (lati 42, 25). In 5 si torna sul Ciclo 2(lati
54,41), ed in 1 si torna a percorrere il Ciclo 1 (lati 12, 23, 36, 68, 81).
In conclusione, la sequenza dei lati è :
13, 35, 56, 64, 47, 78, 84, 42, 25, 54, 41, 12, 23, 36, 68, 81
90
A.Laurentini-Analisi degli algoritmi
N. B. Esistono numerosi cicli Euleriani, per le decisioni arbitrarie prese sia nel Passo
1 che nel Passo 2.
N.B. Se il grafo è orientato, la condizione è che per ogni nodo sia: in-degre=out-
degree
Infatti, aggiungendo un lato tra i due nodi A e B di grado dispari, si ottiene un grafo
Euleriano con almeno un ciclo Euleriano. Se si sopprime questo lato, il ciclo diventa
un cammino che inizia in A e finisce in B (e viceversa, se il grafo non è orientato).
Esempi.
91
A.Laurentini-Analisi degli algoritmi
Grafo non Euleriano(tutti nodi di grado dispari) Grafo Semi-Euleriano (due soli
nodi dispari)
I percorsi Hamiltoniani riguardano la visita dei vertici, e non dei lati. Il problema fu
introdotto dal matematico irlandese Hamilton(1859).
• Cammino Hamiltoniano:=un percorso (di V-1 lati)che passa per tutti i vertici del
grafo una sola volta
• Ciclo Hamiltoniano:=un ciclo (di V lati)che passa per tutti i vertici del grafo una
sola volta
Esempi
92
A.Laurentini-Analisi degli algoritmi
Se il grafo è Euclideo (le distanze tra i vertici del grafo disegnato sono uguali ai pesi),
o almeno vale la disuguaglianza tringolare peso(AB)≥ peso(AC)+ peso(CB), esiste un
algoritmo polinomiale ad approssimazione garantita (costo≤1.5costoMin)
11.11– CLIQUE
11.12– COLORABILITA’
Come già accennato, la colorazione di un grafo deve avvenire con colori diversi per
nodi adiacenti, ed il numero cromatico χ(G) è il minimo numero di colori richiesti per
colorare un grafo.
Evidentemente:
χ(G)≥clique number
93
A.Laurentini-Analisi degli algoritmi
11.13– DOMINANZA
Insieme dominante: = insieme di vertici di un grafo G non orientato tale che ogni
altro vertice è adiacente ad un vertice dell’insieme.
a
Esempio.
{adef},{adeb} : insiemi dominanti d
{fde}{abc}: insiemi dominanti minimali f e
c b
94
A.Laurentini-Analisi degli algoritmi
Un insieme dominante deve contenere, per ogni nodo: o il nodo stesso, o uno dei soi
adiacenti. Per costruire tutti gli insiemi dominanti minimali, tra cui scegliere il
minimo, si può usare il seguente algoritmo (simile a quello per il set covering).
- Si costruisce un’espressione logica formata dal prodotto logico (AND) di tante
somme logiche(OR) quanti sono i nodi. Ogni somma contiene le variabili che
rappresentano la presenza di un nodo e dei suoi adiacenti
- Si minimizza l’espressione logica come somma di prodotti, riducendola agli
implicanti principali. Ognuno di essi rappresenta un insieme dominante minimale.
Il problema è NP-completo
Esempio
(a+b)(b+a+c+d)(d+b+c)(c+d+b)=b+ac+ad a d
b c
Il primo termine indica che, per coprire a, ci vuole a stesso o b,
il secondo che per coprire b ci vogliono a oppure b oppure c oppure d, etc.
In questo caso il numero di dominanza è 1.
Ad ogni casella si associa il nodo di un grafo. Due nodi sono collegati da un lato se
una regina su uno tiene sotto scacco l’altro (le caselle corrispondenti ai nodi devono
stare sulla stessa linea orizzontale, verticale o diagonale). Il problema è così ridotto al
trovare un insieme dominante minimo del grafo
95
A.Laurentini-Analisi degli algoritmi
11.14– INDIPENDENZA
96
A.Laurentini-Analisi degli algoritmi
f
e g
c
a h
Si cercano di solito gli insiemi indipendenti massimi, quelli cioè con il massimo
numero di nodi (nell’esempio sono massimi (b,d,e,g) , (a,c,f,h)). Il loro numero di
nodi è il numero di indipendenza β(G).
Il problema è NP-completo
Esempio 1
Esempio 2
Sia data una scacchiera (8X8 caselle). Si vuol disporre il numero massimo di regine
che non si danno scacco a vicenda.
Ad ogni casella si associa il nodo di un grafo. Due nodi sono collegati da un lato se
una regina su uno tiene sotto scacco l’altro (le caselle corrispondenti ai nodi devono
stare sulla stessa linea orizzontale, verticale o diagonale). Il problema ridotto al
trovare un insieme indipendente massimo del grafo
97
A.Laurentini-Analisi degli algoritmi
ESERCIZI E PROBLEMI
98
A.Laurentini-Analisi degli algoritmi
99
A.Laurentini-Analisi degli algoritmi
• L’algoritmo di Dijkstra per il cammino più breve funziona anche se alcuni pesi
sono negativi? Giustificare la risposta positiva o dare un controesempio se la risposta
è negativa
• Determinare i grafi duali dei grafi seguenti
• Verificare il teorema di Eulero, o una delle sue varianti, sui due grafi seguenti
G2
G1
• Verificare il teorema di Eulero, o una della sue varianti, sui poliedri seguenti
100
A.Laurentini-Analisi degli algoritmi
G2
G1
• Per il problema della colorazione di un grafo esistono varie euristiche. Una molto
semplice si chiama sequential coloring. Essa consiste semplicemente, su di un grafo
101
A.Laurentini-Analisi degli algoritmi
102
A.Laurentini-Analisi degli algoritmi
Key Data
Ad esempio:
Chiave Informazione associata
Codice cliente Anagrafica cliente
Codice prodotto Anagrafica prodotto
Vocabolo italiano Vocabolo inglese
Nome Numero di telefono
Nome variabile Indirizzo in memoria
Codice c.c Movimenti
103
A.Laurentini-Analisi degli algoritmi
12.1– VETTORI
• Alberi binari di ricerca (Binary Search Tree-BST):= alberi binari in cui ogni nodo
corrisponde ad un elemento dell’insieme.
Ogni nodo contiene: chiave, dati associati, puntatori al figlio a sinistra(LEFT), al
figlio a destra(RIGHT), al nodo genitore(PARENT).
Le chiavi di ogni nodo LEFT sono minori della chiave di PARENT.
Le chiavi di ogni nodo RIGHT sono maggiori della chiave di PARENT.
5 14 5 18
7 12 18 10
7 12
15
104
A.Laurentini-Analisi degli algoritmi
3 10 10 11
5 14 5 14
3 7 12 18 7 12 18
15 11 15
1) Il nodo non ha figli. Lo si cancella (si cambia il puntatore nel nodo padre).
10 10
5 14 cancello 18 ⇒ 5 14
7 12 3 7 12
3 18
105
A.Laurentini-Analisi degli algoritmi
2) Il nodo ha 1 figlio. Faccio risalire il sottoalbero che ha il figlio per radice (il
puntatore del nodo padre viene fatto puntare al figlio).
10 10
5 14 cancello 14 ⇒ 5 12
3 7 12 3 7
3) Il nodo ha due figli. Ci sono due possibilità. Posso sostituire il nodo da cancellare
con il nodo più a sinistra del sottoalbero a destra, o con il nodo più a destra del
sottoalbero a sinistra. Se ad esempio cancello la radice 10 dell’albero a sinistra,
ottengo uno dei due alberi a destra:
10 7 12
5 14 ⇒ 5 14 oppure 5 14
3 7 12 3 12 3 7
Si può facilmente verificare che sono semplici anche altre operazioni come Min,
Max, Next, Previous.
106
A.Laurentini-Analisi degli algoritmi
E’ la tecnica di gran lunga più usata per grandi valori di n. Le tabelle hash
permettono ricerche in tempo medio circa costante.
Indirizzo=K+HxChiave
Ciò è possibile in alcuni casi pratici, quando se si può assegnare la chiave di ricerca
ad.esempio: codice prodotto di magazzino:= interi successivi.
In molti casi però la chiavi sono assegnate e non modificabili (dizionari, elenchi
telefonici, …). Si potrebbe pensare di trasformare direttamente le chiavi in numeri.
Si consideri un dizionario italiano-inglese: per trasformarlo in una tabella ad accesso
diretto, si potrebbe trasformare le parole italiane in chiavi binarie sostituendo ad ogni
carattere il suo codice ASCII su 6 bit. Considerando parole fino a 15 lettere, le chiavi
risulterebbero numeri binari con 90 cifre, vale a dire decimali con circa 30 cifre.
Ma una memoria con un tali numeri di locazioni è assolutamente al di fuori delle
possibilità pratiche, per una ventina di ordini di grandezza.
Poiché non è praticamente possibile costruire in questo modo gli indirizzi, o gli indici
di tabelle ad accesso diretto, si deve modificare in qualche modo la costruzione .
Esempio
L’esempio che segue non è realistico, ma rivolto a mostrare i problemi da risolvere
per costruire una tabella con questa tecnica.
107
A.Laurentini-Analisi degli algoritmi
Si supponga di inserire, una dopo l’altra, una serie di parole italiane con la
corrispondente inglese nella tabella:
cane-dog 1 dovere-duty 1
2 2
4 dovere duty
2 2
5 cavallo horse
2 2
6 6 età age
- la funzione hash scelta non è molto buona: provoca sempre collisioni se le parole
iniziano con la stessa lettera. E’ bene scegliere funzioni che tengano conto di tutta
la chiave, e tendano a sparpagliare in modo circa uniforme le chiavi nella tabella.
- se non ci sono collisioni, l’accesso alla tabella(sia per inserire che per leggere) è in
tempo costante
- è bene lasciar vuota una parte della tabella per ridurre la probabilità di collisione e
rendere più brevi le ricerche dopo le collisioni
• Funzioni hash
Se gli indici della tabella sono m (0, 1,…..m-1), si vorrebbe che le chiavi si
mappassero sugli indici con probabilità uguali:
1
∑ p(k ) =
m
∀j
k :h ( k ) = j
- Metodo di divisione
h(k)=k mod m (resto della divisione per la dimensione m della tabella)
Se fosse m=1000, il resto dipenderebbe solo dalle ultime tre cifre. E’ consigliabile
usare per m solo numeri primi.
X A x2w
p bit
109
A.Laurentini-Analisi degli algoritmi
- Numeri pseudocasuali.
Si usa per h(k) un generatore di numeri pseudocasuali, inizializzato con k. Se ci sono
collisioni, si usano i successivi numeri generati .
- Ripiegatura (folding)
Da usare per ridurre le dimensioni di chiavi lunghe.
XOR
E’ una tecnica veloce, che può essere usata più volte.
Prendendo p bit del risultato, si possono direttamente
indirizzare tabelle di dimensione 2p.
Vanno bene anche altre funzioni logiche che abbiano una tabella di verità contenenti
due zeri e due uni (quindi non AND e OR).
Per ogni posizione, c’è una lista di elementi che collidono. Se le collisioni sono state
n, le liste sono lunghe mediamente n/m. Se la distribuzione delle collisioni è
uniforme, sia Search che Insert e Delete sono O (n/m).
Se il coefficiente di riempimento (detto anche di carico) α= n/m aumenta troppo, si
può ristrutturare la tabella.
110
A.Laurentini-Analisi degli algoritmi
Si può dimostrare che, sotto l’ipotesi di funzione hash ottima, il numero medio di
accessi (tentativi per il Search o Insert) dipende solo da α:
ki ki
kg kg
ki kg
kg ki
6 accessi 5 accessi
- Scansione pseudocasuale.
Usa i numeri prodotti da un generatore pseudocasuale di interi con la chiave come
seme. Sotto ipotesi abbastanza generali si trova che:
- Scansione quadratica
hr(ki)= [ h(ki)+r2 ] mod m
111
A.Laurentini-Analisi degli algoritmi
q(ki) è una funzione semplice della chiave. I numeri medi di accessi diventano:
Per le ricerche senza successo, le medie degli accessi possono variare notevolmente.
Il caso peggiore è quello della scansione lineare:
• Cancellazioni
Le tecniche descritte servono sia per Search che per Insert. La cancellazione è in
pratica un’operazione più rara, e in diversi casi non è necessaria (ad es. per le tabelle
dei simboli costruite dal compilatore o dall’assembler).
Se non si prendono opportune precauzioni possono dar luogo a problemi. Ad
esempio, facendo riferimento all’esempio all’inizio del capitolo:
Una soluzione: un campo aggiuntivo con tre valori: libero, occupato, cancellato.
In fase di ricerca, cancellato=occupato. In fase di inserzione, cancellato=libero.
112
A.Laurentini-Analisi degli algoritmi
9 50
5 7 24 30
1 4 3 6 20 21 18 3
12 5 6
9 5 7 1 4 3 6 50 24 30 20 21 18 3 12 5 6
113
A.Laurentini-Analisi degli algoritmi
Esempio
Inserisco 36
50
24 30 24 36
20 21 18 3 20 36 20 24
12 5 6 36 12 5 6 21 12 5 6 21
- Delete-Min (Delete-Max)
Devo sempre mantenere il bilanciamento. Si sostituisce la radice con l’ultimo nodo
dell’ultimo livello. Si confronta la sua chiave con quelle dei figli, e, se non è minore
(maggiore), lo scambio con il più piccolo(il più grande) dei due. Continuo così finchè
non è disceso al posto giusto. Evidentemente anche Delete è O(p)=O(lgn).
Esempio
50 6 30 30
24 30 6 18
20 21 18 3 18 3 6 3
12 5
L’uso di questa tecnica è utile in vari casi, ad esempio negli algoritmi del minimo
albero ricoprente e dei percorsi minimi, dove ad ogni passo si deve scegliere una
distanza minima (Delete), ed aggiornare (Insert) le distanze dei nodi
114
A.Laurentini-Analisi degli algoritmi
• Heap SORT
Lo Heap può essere usato per costruire un SORT asintoticamente ottimo (O(nlgn))
nella classe dei SORT che fanno confronti.
Algoritmo HEAPSORT:
- costruisco lo Heap
- prelevo tutte le chiavi una dopo l’altra con Delete
In questo modo ottengo le chiavi ordinate in modo crescente o decrescente a seconda
di come ho costruito lo heap.
Analisi
- Costruzione dello Heap. Si può usare n volte Insert. Questo porta a dire che la
costruzione è O(nlgn). Un esame più accurato mostra che l’algoritmo è lineare.
- Il prelievo delle n chiavi con Delete è O(nlgn).
In conclusione l’algoritmo è O(nlgn), e quindi asintoticamente ottimo.
115
A.Laurentini-Analisi degli algoritmi
• Tecniche di enumerazione
Tipiche di problemi di soluzione ottima. Consistono , in linea di massima, in:
- generare tutte le soluzioni
- confrontarle per scegliere quella ottima
Generano algoritmi esponenziali.
Esempio 2 La tecnica usata per l’algoritmo esatto(sezione 9.5) per il set covering è
enumerativa, genera tutte le soluzioni , ed è (mn)
116
A.Laurentini-Analisi degli algoritmi
Esempio 2: Ricerca dicotomica, ricerca su albero di ricerca (O(lgn) perché non c’è
ricombinazione)
Esempio 4. Algoritmo per il problema del resto. Si tratta di dare il resto con il numero
minimo di monete, supponendo di avere monete di alcuni valori assegnati.
L’algoritmo è questo, K è il resto da dare:
a) si sceglie la moneta di valore più alto M inferire a K (scelta greedy)
b) K-M→K; se K=0, HALT; altrimenti vai ad a)
117
A.Laurentini-Analisi degli algoritmi
• Algoritmi euristici
Contengono gli algoritmi greedy. Usano qualche tecnica semplice, che mediamente si
rivela abbastanza efficace.
118
A.Laurentini-Analisi degli algoritmi
• Determinazione max-clique.
Caratteristiche:
- di tipo backtracking (non esplora tutto l’albero delle possibilità);
- segue la tecnica euristica di esplorare prima i casi in cui sono coinvolti nodi di
grado (numero nodi adiacente) elevato(e quindi + probabili membri di max-clique)
- semplice da realizzare
Osservazione: condizione necessaria perché ci sia una clique di k nodi è che ci siano
almeno k nodi di grado k.
Algoritmo:
1) ordino i vertici in gruppi di grado(numero nodi adiacente) decrescente almeno n,
n-1, n-2 … n-p ……. . Presumibilmente i primi insiemi sono vuoti
2) Siano k0 , k1 , …. kp , ….. le cardinalità degli insiemi. Si inizia ad esaminare,
partendo dall’alto, il primo insieme di vertici per cui è …. kp ≥ n-p ( e quindi
potrebbe contenere i nodi di max-clique)
3) Si ordinano i nodi dell’insieme in modo decrescente secondo il grado
4) Si generano tutte le combinazioni dei kp nodi a gruppi di p con la regola
lessicografica esemplificata sotto, che mantiene l’ordinamento e quindi privilegia
sempre la scelta del nodo di grado più alto.
Esempio: Insieme 123456 Sottoinsiemi da 4 elementi (sono 15= (6x5x4x3)/(4x3x2)):
1234, 1235, 1236, 1245, 1246, 1256, 1345, 1346, 1356, 1456, 2345, 2346, 2356,
2456, 3456
Sono state studiate molte variazioni del problema (poligoni con lati ortogonali,
limitati angoli visivi dei sensori,……) e trovati altri bound per questi casi. (vedi T.
Shermer, “Recent results in art galleries”, IEEE Proc. Vol. 80, pp. 1384-1399, 1992)
Tuttavia non si conoscono algoritmi finiti, neppure esponenziali, per trovare una
soluzione ottima, e si devono quindi usare algoritmi euristici.
Per valutare la bontà di questi algoritmi, sarebbe utile conoscere un lower bound
LB(P) specifico del poligono P. Se l’algoritmo fornisce un numero di guardie uguale
G al lower bound, la soluzione è ottima. Se G-LB(P) è piccolo, lo è anche la
differenza tra G ed il valore ottimo, compreso tra G e LB(P)
Consideriamo il caso dell’osservazione dei soli lati del poligono (boundary covering)
che corrisponde al problema pratico dell’ispezione.
120
A.Laurentini-Analisi degli algoritmi
Si noti che boundary covering è diverso da interior covering che può richiedere molte
più guardie, e dall’eventuale soluzione ottima di un problema non si sa come trovare
la soluzione ottima dell’altro.
121
A.Laurentini-Analisi degli algoritmi
E’ evidente che in ogni week visibility polygon c’è almeno una guardia. Ne consegue
che :
Un lower bound LB(P) è il massimo insieme di week visibility polygons che non si
intersecano
Esempio
122
A.Laurentini-Analisi degli algoritmi
Problema. Dato un insieme di punti nel piano, determinare la coppia di punti più
vicina. Alla base di numerosi altri algoritmi.
Se i punti non sono allineati, sembra che si debbano calcolare n(n-1)/2 distanze,e
quindi costruire algoritmi quadratici.
Si trovi quindi le coppia più vicine, a distanza d1 e d2 nei due insiemi. Sia d la
distanza più piccole tra le 2.
123
A.Laurentini-Analisi degli algoritmi
Se fossimo sicuri che non ci sono distanze più piccole tra coppie di punti con un
punto in S1 e l’altro in S2, potremmo dividere recursivamente gli insiemi , ed
ottenere un algoritmo O(nlgn), (ricombinazione lineare, lgn passi).
Questo però non è vero: potrebbero esserci punti di questo genere, uno nella fascia P1
e l’altro nella fascia P2. larga ciascuna d. Dobbiamo quindi verificare che per ogni
punto nella fascia P1 non ce ne sia uno in P2 a distanza minore di d. Apparentemente
questa verifica è O(n2).
d l d
Prova per assurdo E’ chiaro che in ognuno dei 16 quadrati di lato d/2 può esserci (al
massimo) un solo punto, altrimenti sarebbero più vicini di d. Supponiamo, per
assurdo che due punti abbiano distanza < d, e siano separati da 16 o più posizioni.
Per la seconda condizione, devono essere separati da almeno 3 file di quadratini. Ma
tre file corrispondono ad un distanza di almeno 3X d/2 > d, contraddicendo la prima
condizione.
Nota. Con una dimostrazione più complessa, si può vedere che la distanza è al
massimo 5 . Agli effetti dell’algoritmo, questo non cambia la complessità asintotica.
Algoritmo.
1) Ordina in x e y i punti
2) Dividi i punti in 2 con la linea l
3) Applica recursivamente l’algoritmo alle due metà
4) Ricombina, trovando il massimo tra le due distanze ed esaminando che non ci
siano distanze minori nella fascia.
124
A.Laurentini-Analisi degli algoritmi
Analisi complessità
1) O(nlgn),
2) lineare
3) Sono lgn passi della recursione,
4) ciascuna divisione e ricombinazione lineare ( la ricombinazione richiede
trovare il minimo tra, al livello più basso, n/2 distanze, trovare e verificare i
punti nella fascia, che sono lineari)
In totale O(nlgn).
125
A.Laurentini-Analisi degli algoritmi
Ottimalità
Gli algoritmi descritti sono asintoticamente ottimi.
Per verificarlo, occorre trovare un lower bound. A questo scopo, si usa il problema
dell’unicità di un elemento (element uniqueness problem), che chiede, data una lista,
di trovare se vi sono due elementi uguali.
E’ stato dimostrato che il problema è Θ(nlgn)
Il problema può essere ridotto (trasformato) nel problema della coppia più vicina.
Infatti, se trovo due elementi a distanza 0, significa che due elementi sono
coincidenti. Quindi il problema della coppia più vicina ha lo stesso lower bound (non
può essere più semplice)
126
A.Laurentini-Analisi degli algoritmi
INDICE
127
A.Laurentini-Analisi degli algoritmi
128