analisi degli algoritmi22[1]

128
A.Laurentini-Analisi degli algoritmi 1 Dean Karnopp, Donald L. Margolis, Ronald C. Rosenberg System dynamics: modeling and simulation of mechatronic systems Lezioni: Aldo Laurentini, Dip. Automatica ed Informatica [email protected] Esercitazioni: Alberto Tonda (in aula Giovedì 8.30-10) [email protected] 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 Esame: Prova di Teoria + Prova di Programmazione . Voto : media dei due voti

Upload: dforce1000

Post on 01-Jul-2015

3.108 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

1

Dean Karnopp, Donald L. Margolis, Ronald C. Rosenberg

System dynamics: modeling and simulation of mechatronic systems

Lezioni: Aldo Laurentini, Dip. Automatica ed Informatica [email protected]

Esercitazioni: Alberto Tonda (in aula Giovedì 8.30-10)

[email protected]

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

Esame: Prova di Teoria + Prova di Programmazione . Voto : media dei due voti

Page 2: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

2

ANALISI DEGLI ALGORITMI 1 - INTRODUZIONE •••• Algoritmo:= Insieme di regole per ottenere dati di OUTPUT da dati di INPUT. 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.)

Esempio:

…………..

Problema

Alg.1 Alg.2 Alg.n

Real. 1 Real. 2

A I O

Page 3: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

3

Si noti che sia in ingresso che in uscita un algoritmo può avere pochi o molti dati.

•••• 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

• Computability theory (CT) Studio teorico degli algoritmi, iniziato negli anni ’30, prima dei calcolatori. Uno strumento è un calcolatore ideale, detto MACCHINA DI TOURING (Alan Touring). La macchina ha una memoria infinita, costituita da caselle adiacenti. Ogni casella contiene un simbolo. Una testa di lettura /scrittura lo legge, ed, in base ad uno stato precedente: -produce un nuovo stato -scrive un nuovo simbolo nella casella -si sposta su un’altra casella

Verifica se n è primo

n si/no

Calcola l’instradamento ottimo su Internet di un pacchetto

Topologia rete,……..

instradamento

…………..

Problema: dato n, è primo?

Alg.1: dividi n per 2, 3, 4…… n/2

Alg.2: riduci le divisioni: se hai già diviso per 2 e 3, non dividere per 6…..(Crivello di Eratostene)

Page 4: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

4

Memoria Testa di lettura/scrittura La CT si è occupata principalmente di comprendere quali problemi possono essere risolti in modo algoritmico. Ad es., un risultato è la non resolubilità dell’Halting Problem ( Esiste un algoritmo generale(o programma) che, dati: a)un algoritmo A; b) dei dati di input ; è in grado di dire se l’algoritmo A si fermerà in un numero finito di passi?) Conoscere l’esistenza teorica di un algoritmo per risolvere un problema non è sufficiente a dirci come fare in pratica. Per esempio, è facile dare un algoritmo (scrivere un programma) per giocare in modo ottimo a filetto, che esamina tutte le possibilità. E’ possibile teoricamente costruire allo stesso modo un programma ottimo per giocare a scacchi. In pratica la cosa non è fattibile (si stima che ci siano 1050 diverse combinazioni di pezzi sulla scacchiera!) E’ quindi importante avere algoritmi praticamente realizzabili, sia per quanto riguarda il tempo di elaborazione che lo spazio (memoria) occupata. Di questi problemi si occupa la branca della computer science detta computational complexity. • Esempi di problemi comuni trattati nella teoria degli algoritmi

- calcolare se un numero è primo (importante nella teoria dei codici segreti, basati su numeri prodotto di primi molto grandi) - moltiplicare due matrici (operazione comunissima, ad es. nella computer graphics) - trovare il percorso più breve tra due nodi di un grafo pesato (modella numerosi problemi pratici) - dato un grafo, trovare il percorso (se c’è) che mi fa passare da tutti i lati una volta sola ( modella numerosi problemi pratici, dall’ispezione di reti tecnologiche alla raccolta rifiuti) - ordinare una lista di nomi o numeri (problema comunissimo, dalle applicazioni gestionali alla computer graphics) - dati : un insieme di lavorazioni da effettuare, un ordinamento parziale, un insieme di centri di lavoro, trovare lo scheduling ottimo (si pensi alla costruzione di un’auto) - dato un volume (contenitore), ed un insieme di forme 3D, ciascuna con un coefficiente di merito, inserire le forme 3D nel contenitore in modo da massimizzare la somma dei coefficienti di merito

Page 5: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

5

- data la pianta polygonale di un ambiente, qual’ è il numero minimo di telecamere di sorveglianza richieste per coprire l’ambiente e dove devo installarle? (Il problema detto ART GALLERY Problem). Si può estendere in 3D. • Un esempio competo di problema e di algoritmo: L’accoppiamento stabile (Stable Matching Problem)

Problema: Sono dati due insiemi M=(m1, m2, ……mn) e W= (w1, w2, …..wn) di uomini e donne. Ciascun uomo ha una lista di preferenze per le donne, e viceversa. Vogliamo trovare un accoppiamento stabile (se esiste), cioè tale che: - l’accoppiamento sia perfetto (ogni uomo ed ogni donna sono sistemati in una ed

una sola coppia) - non ci siano instabilità. Un’instabilità si ha se m↔w e m’↔w’, ma m preferisce w’

al suo partner corrente w e w’ preferisce m al suo partner corrente m’. Evidentemente in tal caso due coppie si disfano e se ne riforma una sola.

Algoritmo di Gale-Shapley per determinare un accoppiamento stabile.

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

m m'

w w'

Page 6: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

6

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!!

L’algoritmo si applica a numerosi problemi pratici

2 – ANALISI DEI PROBLEMI E DEGLI ALGORITMI 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 Due possibili significati:

Page 7: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

7

a) correttezza dell’algoritmo ideale b) aderenza all’algoritmo ideale della sua realizzazione(programma) 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. 2.2 - LAVORO FATTO (TEMPO IMPIEGATO) E’ ovviamente importantissimo. Molti programmi sono totalmente inutili se impiegano più di un certo tempo (controllo di processi, previsioni meteo,….). Il tempo di esecuzione di un programma dipende da molti complessi fattori : - algoritmo ideale - sua implementazione - software usato (linguaggio di programmazione, compilatore, sistema operativo….) - hardware (memoria, unità aritmetica,….) Non è in pratica possibile tenere conto di tutto a priori. Si cerca allora una misura del lavoro fatto che: - sia semplice e generale - dipenda solo dall’algoritmo ideale Il tempo effettivo di elaborazione sarà circa proporzionale a questa misura. La costante di proporzionalità potrà essere misurata su di un certo sistema di elaborazione. Sono necessarie varie approssimazioni. Un programma è fatto di: - istruzioni di inizializzazioni - uno o più loop

Si trascurano le inizializzazioni, si considerano solo i loop. Si può: - contare quante volte si esegue il loop più interno oppure - individuare un’operazione fondamentale e contare quante volte si esegue.

Le due scelte sono in pratica coincidenti.

Page 8: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

8

Esempi

Problema Operazione fondamentale

-Trovare x in una lista di elementi xi -Confronto tra x e xi -moltiplicare due matrici -prodotto di due numeri -ordinare una lista -confronto tra due elementi della lista -attraversare un albero binario -attraversare un lato -trovare se un numero è primo -divisione Vantaggi di una scelta corretta dell’operazione fondamentale (OF): - l’OF potrebbe essere la più onerosa (es. op. floating point in confronto a op. fixed

point) - il numero complessivo di operazioni è circa proporzionale al numero di OF - contare OF risponde alla domanda: come cresce il lavoro fatto con il crescere dell’imput - è una scelta flessibile, può essere più o meno dettagliata La scelta dell’operazione fondamentale determina una classe di algoritmi.

Definiamo: COMPLESSITA’=LAVORO FATTO=N

0 OPERAZIONI FONDAMENTALI

2.2.1 - CASO MEDIO E CASO PESSIMO Il lavoro fatto W dipende da: a) algoritmo b) INPUT. Evidentemente: - 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…)

Esempi di dimensione dell’INPUT

Problema Dim. INPUT

- Trovare x in una lista - n0 elementi n della lista - Moltiplicare due matrici - dim. Matrici - ordinare una lista - n0 elementi n della lista - traversare albero binario - n0 nodi - risolvere sist. di equaz. lineari - n0 equazioni - risolvere un problema relativo ad un grafo - n0 nodi; n0 lati; ambedue

Page 9: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

9

•••• Complessità nel caso pessimo (worst case) W(n):=Maxw(I)|I∈Dn w(I):= num. OF per un INPUT I Dn := insieme degli INPUT di dimensione n

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

•••• Complessità nel caso medio (average behavior)

∑∈

=nDI

IwIpnA )()()(

Richiede per tutti gli I: -calcolo di w(I) -conoscenza delle probabilità p(I) Esempio 1 - Problema: ricerca di x in una lista di n elementi - Algoritmo: scansione della lista dall’inizio - OF: confronto (=, ≠ ) Complessità nel caso pessimo Il caso pessimo si ha se : a) x e’ l’ultimo; b) non c’è. W(n)=n Complessità nel caso medio

Occorre formulare delle ipotesi: a) Ipotesi: x presente nella lista(una sola volta), posizioni equiprobabili.

Page 10: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

10

22

1

2

)1(11)()()(

)(

11

nnnn

nin

IwIpnA

iIw

n

i

i

n

i

i ≅+

=+

===

=

∑∑==

b) Altra ipotesi: x presente nella lista una sola volta con probabilità q; se presente, posizioni equiprobabili

nnn

nAq

nnAq

nqnq

nqnn

n

qnqi

n

qnA

n

i

4

3

24

1)(

2

12

1)(1

)1(2

)1()1(

2

)1()1()(

1

≅++

=⇒=

+=⇒=

−++

=−++

=−+= ∑=

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

- Problema: prodotto di due matrici quadrate AB=C

- Algoritmo: kj

n

k

ikij bac ∑=

=1

- OF= moltiplicazione Complessità nel caso pessimo=complessita’ nel caso medio

W(n)=A(n)=n3

2.3- SPAZIO USATO (MEMORIA) 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:

Page 11: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

11

- lo spazio usato dal programma - lo spazio usato dai dati di INPUT Si considera solo lo spazio aggiuntivo (extra space). Se l’algoritmo non ne richiede, si dice che funziona in space. 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. 2.5.1- CASO PESSIMO(WORST CASE)

• 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. • Come dimostrare che un algoritmo A è ottimo (in una classe)? 1) Si trova un LOWER BOUND F(n), lavoro minimo (minimo n0 di OF) che

debbono fare tutti gli algoritmi della classe) 2) Si trova la complessità WA(n) di A e si confronta con il LOWER BOUND: - se F(n) =WA(n), A e ottimo F(n) WA(n)

- se F(n) <WA(n), non si può dire niente. Potrebbe esistere un LOWER BOUND

F’(n)> F(n). WA(n) F(n)

Page 12: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

12

Esempio 1 di analisi ottimalità nel caso pessimo

- Problema: trovare il max di una lista - Classe algoritmi: che usano il confronto a tre vie tra due elementi(<=>) - Algoritmo: confronto il primo col secondo, il maggiore con il terzo, il maggiore

con il quarto…. Max:=L[1] For index:=2 to n do If Max<L[index] then Max:=L[index] endif endfor

(pseudo-codice simile al PASCAL)

- 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.

Conclusione: W(n)= F(n) ⇒ L’algoritmo è ottimo.

Esempio 2 di analisi ottimalità nel caso pessimo

- Problema: prodotto di due matrici quadrate AB=C - Classe di algoritmi: quelli che ammettono le 4 operazioni aritmetiche - OF:= moltiplicazione

- Algoritmo: applica la definizione: kj

n

k

ikij bac ∑=

=1

Complessità dall’algoritmo: W(n)=n3 ( numero addizioni: (n-1) n2≅ n3 ) LOWER BOUND: n2 (si omette la dimostrazione) Conclusione: non possiamo dire se l’algoritmo sia o no ottimo. 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

Page 13: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

13

Non sappiamo però se quest’ultimo algoritmo è ottimo (non sono stati trovati altri LOWER BOUNDS) 2.5.2 - OTTIMALITÀ NEL CASO MEDIO Approccio identico a quello del caso pessimo. Ricordiamo ancora che, quando non si dice esplicitamente, si tratta di ottimalità nel caso pessimo. 2.6 - OTTIMALITÀ NELL’USO DELLO SPAZIO Approccio identico a quello del lavoro fatto.

Page 14: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

14

3. CLASSIFICAZIONE ASINTOTICA •••• Motivazioni Si supponga di avere due algoritmi: - Alg. A’ , complessità W’(n)=2n - Alg. A” , complessità W”(n)=5n 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: - Alg. A’ , complessità W’(n)=c’n3

- Alg. A” , complessità W”(n)=c”n2 c’<c” Per n piccoli, A’ è migliore, ma per n>n0=c”/c’, A” è sempre migliore. Inoltre:

∞=∞→ )("

)('lim

nW

nW

n

Dobbiamo quindi considerare A’ ed A” in classi di complessità asintotiche diverse.

c’n3

c”n2

n0=c”/c’

Page 15: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

15

3.1-NOTAZIONI Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω

•••• Notazione O( o grande, big ho) Siano f(n), g(n) funzioni di interi positivi n.

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 . - Equivale a dire che: g è asintoticamente limitata da un multiplo di f

- Una definizione equivalente è : g∈ O(f) se cf

g

n=

∞→lim con c costante reale (anche 0).

- Correntemente si dice che O(f) è l’insieme delle funzioni che crescono al massimo come f. Se g appartiene a questo insieme, si dice che è O(f). Esempio: c, n, n/1000, n

2/2 sono tutte O(n2).

N.B. Se ∞==

∞→∞→gf

nnlimlim , si può usare il teorema di L’Hopital:

'

'limlim

g

f

g

f

nn ∞→∞→= se f’ e g’ esistono

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

b) 0)3424074

(lim

2

1712037limlim

323

2

=++=++

=∞→∞→∞→ nnnn

nn

f

g

nnn

Esempio 2

f= n2 g=nlgn

Page 16: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

16

01

lg

limlg

limlg

limlim '

2== →===

∞→∞→∞→∞→

ne

n

n

n

nn

f

g

n

HopitalL

nnn

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∈ Ω (f) se cf

g

n=

∞→lim ≠0 oppure ∞=

∞→ f

g

nlim

E’ l’insieme delle funzioni che crescono almeno quanto f.

•••• Notazione ΘΘΘΘ Θ(f):=O(f)∩Ω(f) oppure

g∈ Θ (f) se cf

g

n=

∞→lim ≠0

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)

Page 17: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

17

O(f) o(f)

Esempi di complessità’ asintotica di algoritmi

- Ricerca di un elemento in una lista Θ(n) - Ricerca del max. in una lista Θ(n) - Prodotto di matrici (algoritmo normale) Θ(n3) 3.2 - PROPRIETÀ DI Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω Ο, Ω, Θ, ο, ω - Transitività: se f∈O(g) e g∈O(h) , ⇒ f∈O(h) - f∈O(g) se e solo se g∈Ω(f) - se f∈Θ (g), allora g∈Θ(f) - Θ stabilisce un’equivalenza tra funzioni. Ogni insieme Θ e’ una classe di

equivalenza che chiamiamo classe di complessità(asintotica) - O(f+g)=O(Max(f,g)) ; la proprietà vale anche per Θ e Ω ed è utile per analizzare

algoritmi fatti da più parti Θ(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. N.B. Si dimostra che: 1) lnn∈ο(nα) per α>0. Il logaritmo cresce meno di qualunque potenza (anche di n1/k)

2) nk∈ο(cn) per k>0 , c>1. Qualunque potenza di n cresce meno dell’esponenziale

N.B. Talvolta, invece di ∈ si usa =. Ad es., si scrive f=Θ (n)

Page 18: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

18

4444 - 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. Es. 2 = 2.5 = 2.99 = 2 ; 3 = 2.5 = 2.1 = 3 • Logaritmi. Sono largamente usati nell’analisi degli algoritmi, in particolare quelli in base 2 - logbb

a=a - funzione strettamente crescente e univoca - logb x1 x2 =logb x1 + logb x2 - logb x1/ x2 =logb x1 - logb x2 - logb1=0 - logbx

a=alogbx - conversione di basi :logb x =logc x logb c - N.B. Nell’analisi degli algoritmi spesso non si considerano le costanti

moltiplicative, quindi i logaritmi in basi diverse sono equivalenti - notazioni usuali: log2x = lg x; logex = ln x

- lg e ≅ 1.44; ln 2 ≅ 0.7; lg 10 ≅ 3.32 - n ≤ 2lgn < 2n - n/2 < 2 lgn ≤ n - d(lnx)/dx =1/x ⇒ d(lgx)/dx =lge /x • Fattoriali Ovviamente n!<nn . Una approssimazione piu’ stretta e’ data dalla formula di Stirling:

Θ+

=ne

nnn

n1

12! π

da cui si ottiene: n!∈o(nn) n!∈ω(2n) lg(n!) ∈ Θ(n lgn)

Page 19: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

19

ed anche:

+

≤≤

nnn

e

nnn

e

nn

12

1

2!2 ππ

• Sommatorie - Interi consecutivi:

2

)1(

1

+=∑

=

nni

n

i

- Quadrati consecutivi:

36

32 323

1

2 nnnni

n

i

≅++

=∑=

- Potenze di 2:

122 1

0

−= +

=∑ kk

i

i

- Somme geometriche

k

k

ii 2

12

2

1

0

−=∑=

1

11

0 −−

=+

=∑

a

aa

kk

i

i

• Approssimazione di somme mediante integrali - Funzioni decrescenti

∫ ∑ ∫+

=−

≤≤1

1)()()(

b

a

b

ai

b

adxxfifdxxf

a b b+1 a-1 a b

- Funzioni crescenti

∫ ∑ ∫−=

+≤≤

b

a

b

ai

b

adxxfifdxxf

1

1)()()(

Esempio 1

Funzione decrescente.

Page 20: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

20

[ ] nnxx

dx

i

nnn

i

ln1lnlnln1

112

=−==≤ ∫∑=

ed anche:

[ ] 2ln)1ln(ln1 1

2

1

22

−+==≥ ++

=∫∑ nx

x

dx

i

nnn

i

da cui

ni

ni

n

i

n

i

ln11

ln1

12

+≅⇒≅ ∑∑==

Esempio 2

Funzione crescente.

[ ]

nnneennn

nnnexxxexdxexdxii

nn

i

n nn

i

44.1lglglglg

)1ln(lglnlglnlglglglg11

1 12

−≥+−=

+−=−==≥=∑ ∫ ∫∑= =

Page 21: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

21

5 - STRUTTURE DATI ELEMENTARI •••• Abstract data type(ADT) Idea generale per svincolare gli algoritmi dai dati. ADT:= insieme di elementi di informazione con operatori associati. Es. : numeri interi + operazioni aritmetiche

Matrici +somme, prodotti, inversioni,trasposizioni

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.15.15.15.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

• Operazioni più comuni sulle liste - insert(x, p, L): inserisci l’elemento x in posizione p nella lista L - delete(x,L): cancella l’elemento x dalla lista L - delete(p,L): cancella l’elemento in posizione p dalla lista L - locate(x,p,L): se x∈L, ritorna la sua posizione, altrimenti eol(L) - retrieve(x,p,L): ritorna il valore x dell’elemento in posizione p della lista L

Page 22: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

22

- etc. (inserisci, togli, in fondo o in testa) • Realizzazione delle liste - Mediante vettori: gli elementi sono memorizzati in locazioni contigue. E’ la cosa

più semplice per elementi di lunghezza uguale. Necessari puntatori al primo ed ultimo elemento (o lunghezza lista). Varie operazioni sono O(n) (insert, delete) per la necessità di spostare gli elementi successivi. O(1) se si inserisce o toglie all’inizio o alla fine. Per elementi di lunghezza eguale (caso più comune), il posizionamento su di un elemento della lista dato il suo indice è O(1).

- Mediante puntatori (liste concatenate). Ogni elemento d’informazione ha un

campo aggiuntivo, un puntatore all’elemento successivo. E’ necessario avere un puntatore al primo elemento della lista (header).

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 • Altri tipi di liste concatenate: Liste doppiamente concatenate.

Page 23: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

23

Liste circolari. Liste multiple (ogni elemento può avere più puntatori). Esempio: • STACK (PILA, LIFO: Last In First Out) E’ una lista particolare. Le operazioni di inserimento(push) e cancellazione(pop) si svolgono solo ad una estremità della lista (TOS: Top Of Stack). Può essere realizzato con vettori o liste concatenate. Nelle CPU particolari istruzioni gestiscono lo STACK come un vettore. Un registro (Stack Pointer) contiene l’indirizzo di TOS. • QUEUE (CODA, FIFO: First In First Out) E’ un particolare tipo di lista. Si può inserire solo da una parte, e prelevare dall’altra.

Page 24: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

24

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). 5.2.1 – ALBERI BINARI • Costituiti da elementi detti nodi e da collegamenti tra nodi detti lati. • 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.

Radice

Albero binario

Albero binario

Albero binario

L R

Albero binario:=

1) vuoto oppure 2) nodo radice più due lati L(left) e

R(right) collegati a due alberi binari. La radice è detta anche nodo padre (parent) e le radici dei sotto-aberi di sinistra e destra nodi figli (child)

Liv.0

Liv.1

A

C B

D E F

G Foglie

Nodi interni

A

C B

D E F G

Albero completo

Page 25: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

25

• 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 Ne sono possibili diverse: -liste multiple -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: - 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 - i nodi del livello l hanno indici da 2l a 2l+1 –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

Liv.2

Liv.3

Liv.4

k

2k+1 2k

Page 26: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

26

- n0 nodi n ≤ 2p+1 –1 (= per albero completo) - p ≥ lg(n+1)-1⇒ p ≥ lgn

• Visita dell’albero(attraversamento, traversal) Esame di tutti i nodi in un certo ordine. Esistono tre tipi di ordinamento e quindi di attraversamento. v è la radice dell’albero o sotto-albero da visitare. Definizioni recursive dei tre tipi di visita: - preorder(v) =: Visita v; preorder( figlio sinistro di v); preorder( figlio destro di v);

- inorder(v) =: inorder( figlio sinistro di v); Visita v; inorder( figlio destro di v); - postorder(v) =: postorder( figlio sinistro di v); postorder( figlio destro di v);

Visita v; Dette anche visite in ordine anticipato, misto, differito.

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

5.2.2 – ALBERI (in generale) 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 nodi con fino a k figli: - il grado (degree) del nodo può variare da 0 a k - il grado dell’albero è k 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)

Page 27: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

27

Possono essere rappresentati con liste multiple (un puntatore per ciascuno dei figli)

ESERCIZI E PROBLEMI

• Un algoritmo richiede f(n)= 15 n2.5+17 n2 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 due numeri reali a e b 0 < a < b, dimostrare che na ∈ O( nb ) , ma nb ∉ O( na )

• Ordinare le funzioni seguenti in ordine di complessità asintotica crescente. Verificare quali appartengono alla stessa classe n 2n nlgn n-n

3+7 n5 lgn n1/2 e

n

n2 + lgn n

2 2n-1 lglgn n3

(lgn)2

n ! • 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?) • Scrivere un algoritmo che determini il massimo ed il minimo di una lista (Come nell’esercizio precedente, si potrebbe usare due volte lo stesso algoritmo (determina il max. o il minimo a seconda di quale elemento viene scelto dopo ogni confronto), e quindi 2n -3 confronti. Si può fare di meglio? ) • Problema delle torri di Hanoi http://en.wikipedia.org/wiki/Tower_of_Hanoi#Origins

Page 28: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

28

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?

• Visitare con le tecniche pre-order, in-order, post-order, l’ albero binario:

Page 29: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

29

• Quanti nodi ha un albero binario completo di profondità 6? E quanti un albero completo ternario di profondità 5?

• Sia dato un albero binario con 100 nodi. Quanto vale la sua profondità minima? Quante foglie ha al minimo ed al massimo?

• Risolvere lo stesso esercizio per albero ternario

• Gli alberi possono essere memorizzati in posizioni consecutive in un vettore. Se l’albero è binario, se il nodo padre ha indice 100, quali sono gli indici dei nodi figli? E se l’albero è ternario?

a

f

c

e

b

d

g h

i m l n

Page 30: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

30

6 – ANALISI DI ALGORITMI DI RICERCA

6 .1– RICERCA IN LISTE ORDINATE E’ un caso frequente in pratica. Supponiamo ordinamento non decrescente: 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): Operazione fondamentale: confronto 6.1.1– ALGORITMO 1 • Se non sfruttiamo l’ordinamento, la ricerca sequenziale è Θ(n)( vedi esempio in Par. 3.2.1). L’idea dell’ALG. 1 è quella di non fare confronti inutili: se x > xi, possiamo arrestare la ricerca. Si richiede un confronto a 3 vie (=, > , < )

• 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).

x Informazione associata

Page 31: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

31

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))

2)1(2

1

)1(2

1

2

1)(

11

nn

ni

nin

nAn

i

n

i

≅+

++

+= ∑∑==

Quindi la complessità nel caso medio e’ migliore, ma non si cambia la classe di complessità, che rimane Θ(n).

6.1.2 - ALGORITMO 2: RICERCA BINARIA (DICOTOMICA) • Idea Generale (recursiva). - Confronto x con l’elemento centrale xc della lista - Se uguali, STOP - Se x< xc, applica la stessa tecnica alla metà superiore, se , x> xc, applica la stessa

tecnica alla metà inferiore • Analisi dettagliata Worst Case La lista può avere un numero n pari o dispari di elementi. n=5 n=6

x1 x2 x3 xn

Int.1 Int.2 Int.3 Int.n+1

x∉lista, e’ in uno dei primi intervalli

x∈lista x∉lista, e’ nell’ultimo intervallo

Page 32: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

32

In ogni caso, confronto con (n+1)/2 (vedi esempi sopra) Se n è dispari, ho 2 sottoliste uguali da (n-1)/2 elementi ciascuna. Se n è pari, ho 2 sottoliste da n/2 e da (n/2 )–1 elementi ciascuna. In ogni caso, la lista esaminata nel passo successivo è lunga, nel worst case, n/2. Si ha quindi, nel worst case, la seguente relazione ricorrente (recurrence relation): W(n)= 1+W(n/2) che, insieme alla condizione al contorno W(1)=1 dà la soluzione W(n)= 1+ lgn Si può giungere a questo risultato in modo approssimato, senza conoscere la teoria esatta della equazioni alle differenze. W(n)= 1+W(n/2) = 1+1+W(n/4) = 1+1+1+W(n/8) = ………….. Se n è una potenza di 2, si ottiene: W(n)= 1+ lgn

Conclusione:l’ ALGORITMO 2 (dicotomico) è Θ(lgn). • Analisi caso medio Con le ipotesi: - x non ripetuto - posizioni equiprobabili - intervalli equiprobabili si dimostra che: A(n)≅1/2 + lgn∈Θ(lgn).

n lgn

1000 10

106 20

109 30

Page 33: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

33

6.1.3- OTTIMALITA’ DELLA RICERCA BINARIA (DICOTOMICA) • Classe di algoritmi: quelli che usano confronti a 3 vie • Determinazione Lower bound Qualunque algoritmo che faccia confronti può essere descritto con un’albero di decisione.

Albero di decisione:=albero binario che rappresenta, per un dato algoritmo, e una data dimensione n dell’INPUT, l’ordine in cui vengono fatti i confronti. La radice contiene il primo elemento con cui si fa il confronto. Nodo generico: Esempio: albero di decisione per la ricerca binaria su una lista di 10 elementi. 1 2 3 4 5 6 7 8 9 10

Si ha che: numero dei confronti fatti nel worst case = profondità dell’albero +1 Il lower bound per la profondità dell’albero binario è (vedi Par. 5.2.1): p≥ lg N N= numero nodi Dimostriamo che N≥n. Per assurdo: supponiamo che nell’albero non ci sia il nodo i. Evidentemente, se x è in posizione i non può essere trovato. Quindi deve esserci almeno un nodo per ogni posizione. Ne consegue che: p ≥ lg N ≥ lg n

i

x<xi x>xi

5

8 2

1 3 6

7

9

4 10

Page 34: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

34

e quindi LB(n)=1+ lg n = W(n) ⇒ l’algoritmo dicotomico è ottimo

Problema: mantenimento lista ordinata (se è un vettore, l’inserzione è O(n)

7 - ALGORITMI DI ORDINAMENTO (SORT) • Gli algoritmi di SORT sono: - molto usati in ogni tipo di applicazioni tecnica e gestionale - utili didatticamente - molto studiati, di analisi relativamente semplice • 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 • E’ in place (non richiede spazio aggiuntivo) • Descrizione algoritmo

Situazione iniziale Situazione ad un passo generico

Chiavi da ordinare Chiavi ordinate Chiavi da ordinare

Opero per scambi successivi. Ad ogni passo inserisco nel modo seguente una nuova chiave nella lista ordinata :

Page 35: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

35

- 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. • Analisi worst case E’ il caso in cui le chiavi sono ordinate al contrario.

Chiavi all’inizio del SORT Chiavi durante il SORT

Per ciascuna delle n-1 chiavi da inserire si fanno tutti i confronti possibili con quelle già ordinate.

∑=

−=−=

n

i

nninW

2 2

)1()1()( ⇒W(n)∈Θ(n2)

Il caso ottimo si ha per chiavi già ordinate: bastano n-1 confronti. Possiamo anche dire che l’algoritmo è O(n2) • Analisi caso medio Ipotesi: - chiavi distinte - permutazioni equiprobabili - intervalli di inserzione equiprobabili Situazione al passo di inserzione dell’elemento i. Probabilita’ per ogni intervallo 1/i. Confronti i-1 i-1 i-2 ………. 2 1

Page 36: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

36

1 2 3 i-1 i i+1 …….. Numero medio di confronti per ogni inserzione:

i

i

ij

ii

iji

i

j

i

j

1

2

111

1)1(

11 1

1

1

1−

+=−+=−+ ∑∑ −

=

=

per tutte le inserzioni:

nnn

i

nn

i

inA

n

i

n

i

ln14

3

4

11

4

3

4

1

2

1)(

2

2

2

2

−−+≅

=−−+=

−+

= ∑∑ ==

Trascurando i termini di ordine inferiore:

)(4

)( 22

nn

nA Θ∈≅

7. 2- ALTRI SORT O(n2) • SELECTION SORT (MAXSORT) Descrizione: costruisco la lista ordinata un elemento per volta, selezionando ogni volta la chiave più grande della lista delle chiavi non ordinate rimaste. Il SORT può essere in place se ogni volta la chiave selezionata si scambia con l’ultima delle chiavi non ordinate. Un SORT analogo si costruisce scegliendo ogni volta la chiave minima. Per ogni selezione su di una lista di i chiavi si fanno i-1 confronti. Quindi:

)(2

)1()1()(

2

nAnn

inWn

i

=−

=−= ∑=

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.

Page 37: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

37

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.

Anche il caso medio, con le ipotesi usuali, è Θ(n2). 7. 3- LOWER BOUND PER ALCUNI 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). Consideriamo i SORT della categoria seguente: - OF: confronto a 3 vie - rimuovono un’inversione (al max.) ad ogni confronto come bubble SORT, insertion SORT, e tutti i SORT che operano per scambi tra elementi contigui.

• 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.

Page 38: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

38

7. 4- QUICKSORT: APPROCCIO “DIVIDE AND CONQUER” •Divide and conquer E’ una tecnica generale per accelerare algoritmi di complessità più che lineare. E’ basata sul fatto che: (a+b+ …. q)k >ak + bk+ …. qk se k>1 Se e’ possibile: - applicare l’algoritmo a parti dell’INPUT - combinare i risultati con un algoritmo di complessità < k si riesce a ridurre il lavoro fatto dall’algoritmo. • 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

first splitpoint last

Quicksort Quicksort

Pseudocodice (recursivo):

Split

x

x ≤x ≥x

Page 39: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

39

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

splitpoint unknown

a metà first last

splitpoint unknown

al termine first last

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

x ?

x ? <x ≥x

x <x ≥x

Page 40: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

40

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

k

nnknW

2 2

)1()1()(

esattamente come l’insertion, il bubble ed il MAXSORT. • Caso medio Ipotesi: - permutazioni equiprobabili - chiavi distinte - ogni splitpoint è ugualmente probabile Si dimostra che A(n)≅1.4(n+1)lgn ∈Θ(nlgn)

• 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). 7. 5- MERGE (FUSIONE) DI LISTE ORDINATE E’ un’operazione sulle liste comune quanto il SORT. 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.

Page 41: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

41

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.

•••• Riduzione dello spazio usato Sembra che occorra uno spazio aggiuntivo di n+m per la lista C. Si può ridurre lo spazio aggiuntivo a Min[n,m]: 1 A n n+m 1 B m

C

7. 6- MERGE -SORT

•••• Idea generale: divido la lista in due parti uguali, applico a ciascuna recursivamente il MERGE-SORT, ricombino con MERGE L

sorted sorted

Page 42: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

42

first (first+last)/2 last

merge-sort merge-sort merge procedure Mergesort(first, last) begin if first<last then Mergesort(first, (first+last)/2) Mergesort((first+last)/2 +1, last) Merge(first, (first+last)/2, (first+last)/2 +1, last) end if end Mergesort •••• Analisi del caso pessimo

Relazione ricorrente e condizione al contorno: W(n) = W(n/2)+ W(n/2) + n- 1 sort merge W(1) =0 Soluzione per n potenza di 2. W(n) = 2W(n/2) + n- 1 = 2( 2W(n/4) + n/2- 1) + n- 1 = = n- 1+ n- 2+4W(n/4) = n- 1+ n- 2+ n- 4+8W(n/8)=……. ≅ nlgn – n ∈ Θ(nlgn). •••• Una visione non recursiva del MERGE-SORT

Page 43: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

43

Partendo dal basso, si fanno solo MERGE. Il numero di confronti è ancora W(n) = n- 1+ n- 2+ n- 4+…….n- n/2= nlgn – n 7. 6.1- OTTIMALITA’ DEL MERGE -SORT Abbiamo visto algoritmi Θ(n2) e Θ(nlgn). La differenza è grande: n n

2 nlgn n

2 /nlgn

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.

2

n/2

n/4 n/4 n/4 n/4

…………………………………………..

…………………………………………..

2

……………………………………………………………………..

n/2 Confronti

n-1

n-2

n-n/4

n-n/2

x1: x2

x2: x3 x1: x3

x1 x2 x3 x1: x3 x2: x3 x2 x1 x3

x1<x2 x1>x2

x1<x3 x1>x3

x2>x3 x <x

x2>x3 x2<x3

x <x x >x

Page 44: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

44

Il numero dei confronti nel caso pessimo è la profondità (3 nell’esempio). 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. Si può dimostrare che la profondità minima (e quindi il numero di confronti minimo) è : p = lg (n!) =LB(n) ma abbiamo anche: lg (n!) ≥ (n/2)lg(n/2) LB(n) ∈ Θ(nlgn). e quindi il merge-sort è ottimo nella sua classe di complessita’ asintotica. Piu’ in dettaglio(vedi richiami matematici:sommatorie):

nnnennnjnn

j44.1lglglglg!lg

1−≅−≥= ∑ =

Page 45: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

45

7. 7- ALGORITMI DI SORT LINEARI Usano altri tipi di operazioni fondamentali, non il semplice confronto a 3 vie. Tipicamente richiedono maggiori conoscenze sulle chiavi di ordinamento. 7. 7.1 – BUCKET SORT •••• 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: a) uso per le sottoliste un algoritmo Θ(nlgn).

nlgn> k((n/k)lg(n/k) = nlg(n/k) = n(lgn – lgk) b) uso per le sottoliste un algoritmo Θ(n2). n

2 > k(n/k)2 = n2/k

Page 46: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

46

In ambedue i casi, se k=n/a , i SORT diventano lineari ! Il SORT consiste di 3 fasi: - distribuzione in k buckets: Θ(n) - Sort di ciascun bucket: circa lineare se k ≅ n/a - Ricombinazione (se necessaria) O(n) L’intero algoritmo è tendenzialmente lineare.

Un’alternativa è l’applicazione recursiva del SORT alle sottoliste. •••• 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 di SORT usato già prima dell’avvento dei calcolatori da macchine a schede perforate. Elimina i problemi realizzativi del BUCKET SORT. Si applica in tutti i casi in cui la chiave è una stringa di k caratteri che possono assumere m valori. •••• 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, se c1< c’1, la stringa A è stato messa in un bucket prima di B nell’ultimo passo, e quindi è nella posizione corretta rispetto a B. Se fossero uguali i primi k-1 caratteri, e

Page 47: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

47

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

329 720 720 329 457 355 329 355 657 436 436 436 839 457 839 457 436 657 355 657 720 329 457 720 355 839 657 839

Lista iniziale terza cifra seconda cifra prima cifra, lista ordinata Struttura dati: lista multipla

E’ necessaria una nuova lista multipla ad ogni passo dell’algoritmo, costruita svuotando la vecchia (quella della cifra precedente)

Page 48: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

48

7. 7. 3 – COUNTING SORT Ordina in tempo lineare numeri interi da 1 a k (o elementi che si possono rappresentare in questo modo) •••• 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) Vettore ausiliario (contiene le occorrenze di 1, 2,….6)

0

1

2

3

Puntatore all’elemento corrente

3 6 4 1 3 4 1 4

Page 49: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

49

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

Page 50: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

50

• 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 degli algoritmi ragionevoli per i seguenti problemi, e determinare la loro complessità nel caso pessimo:

- Sia data una pila di migliaia di fatture, ed un’altra pila di pagamenti ( si supponga che su ogni documento di pagamento ci sia il riferimento al numero fattura). Si individuino le fatture non pagate (algoritmo banale: per ogni fattura esamino la lista dei pagamenti: complessità: O(numero fatture X numero pagamenti))

- Sia data una lista di schede di libri di una biblioteca, comprendente tra l’altro la casa editrice. Sia data anche una lista di 30 case editrici. Si determini quanti libri della biblioteca sono stati pubblicati da ogni casa editrice

- Siano date le schede dei prestiti di una biblioteca universitaria, ciascuna con la matricola dell’allievo che ha ricevuto il prestito. Si voglia determinare il numero di allievi che hanno preso in prestito almeno un libro.

• Studiare un algoritmo efficiente in place che riordini una lista in modo che tutte le chiavi negative precedano tutte le chiavi positive.

• Si consideri una lista in cui le chiavi hanno valore solo 0 oppure 1.

- Quanti confronti fa l’Insertion Sort nel caso pessimo?

- Quanti confronti fa il Quicksort nel caso pessimo?

- Esiste un algoritmo efficiente (lineare) per questo caso?

-

• Si consideri il caso di una lista con tre soli valori possibili per le chiavi. Se è ammesso solo il confronto e lo scambio tra chiavi, è possibile realizzare un algoritmo lineare?

• Si consideri il seguente problema pratico: sono da ordinare in ordine alfabetico 300 compiti d’esame cartacei, ed è a disposizione una grossa tavola. Cosa conviene fare?

Page 51: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

51

• Si abbia a disposizione un algoritmo efficiente per il sort di chiavi intere. E’ adatto ad ordinare dei numeri floating point? Se non lo è, cosa si può fare?

8 – ANALISI DELLA TECNICA “DIVIDE AND CONQUER” E’ usata da molti algoritmi di SORT(QUICKSORT, MERGESORT, etc.) e di altro genere. • Approccio generale - divido un problema di dimensione n in a sottoproblemi ciascuno di dimensioni n/b - ricombino le soluzioni dei sottoproblemi

• Analisi In generale è: W(n) = a W(n/b) +c nk (risoluzione dei sottoproblemi + ricombinazione)

Si dimostra che, se a e b sono interi, a≥1, b≥2, c e k sono positivi, :

Page 52: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

52

)(lgabnO a>b

k

W(n)= )lg( nnO k a=bk

)( knO a<b

k

Ad esempio, per SORT-MERGE a = b ⇒ W(n)∈O(nlgn)

9- PROBLEMI P, NP, NP-COMPLETI • Distinguiamo i problemi in due grandi categorie: - Quelli “trattabili”, che ammettono algoritmi polinomi O(nk) con k qualunque - Quelli “intrattabili, con algoritmi esponenziali O(bcn) con b, c qualunqui. • Distinguiamo tra 3 livelli di difficoltà (crescenti ) di un problema: - Problemi di decisione: c’è una soluzione migliore di un dato valore? (Risposta:

si/no) - Problemi di valore ottimo: quanto vale la soluzione ottima? - Problemi di soluzione ottima: qual è la soluzione che produce il valore ottimo? Esempio

Page 53: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

53

Problema: colorazione di un grafo G (si colorano i nodi senza che due nodi adiacenti abbiano lo stesso colore) - decisione: i G è colorabile con K colori? - valore ottimo: qual è il numero cromatico χ(G) del grafo? Il numero cromatico è il minimo numero di colori richiesti per colorare un grafo.

- soluzione ottima: una colorazione che usi il numero minimo di colori.

Ovviamente è facile passare dalla soluzione dei problemi più difficili alla soluzione di quelli più facili. La teoria dei problemi NP ed NP-completi si occupa della difficoltà dei problemi di decisione, (più facili da trattare). Gli altri sono almeno altrettanto difficili. 9.1 - ESEMPI DI PROBLEMI 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. Problema di ottimizzazione: determinare il numero minimo di bin che contengono gli n oggetti. Problema di decisione: sono sufficienti k bin per contenere gli n oggetti? • 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). Problema di ottimizzazione: determinare il massimo guadagno per oggetti che entrano nello zaino, e trovare il sottoinsieme di oggetti che forniscono il max. guadagno. Problema di decisione. E’ possibile avere un guadagno ≥k? N.B. Se si considera l’impaccamento oggetti fisici, il problema è 3D. Così come proposto, il problema ha senso, ad. es., per un investimento in danaro ( ho una cifra

Page 54: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

54

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? N.B. Esistono numerose varianti e complicazioni di questo problema. Ad es.,(si pensi alle operazioni di un’officina, o all’esecuzione di programmi) alcuni job potrebbero essere condotti in parallelo; l’esecuzione di alcuni job richiede che altri siano stati già terminati. • Cammino Hamiltoniano, circuito Hamiltoniano Un cammino (un ciclo) Hamiltoniano è un percorso (un ciclo) di un grafo che passa attraverso tutti i vertici una volta sola. Problema di decisione. Dato un grafo, esiste un cammino (un ciclo) Hamiltoniano? • Problema del commesso viaggiatore (traveling salesman)

Problema di ottimizzazione. Dato un grafo pesato (ogni lato ha associato un numero detto peso), trovare un cammino (un ciclo) Hamiltoniano di peso totale minimo. Problema di ottimizzazione. Dato un grafo pesato, esiste un cammino (un ciclo) Hamiltoniano di peso totale ≤k?

• CNF-SAT (conjunctive normal form satisfiability) Si abbia un’espressione booleana formata da prodotti logici(AND) di somme logiche di variabili booleane. Ad es.: (a+b+c)(a+e+f)(b+q)…..

Page 55: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

55

Problema di decisione. Esiste un’assegnazione di TRUE e di FALSE alle variabili booleane che renda TRUE l’espressione? 9.2 – LA CLASSE P (PROBLEMI DI DECISIONE) 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 E’ la classe dei problemi di decisione che ammette algoritmi Non deterministici Polinomiali. Questi algoritmi consistono di due fasi:

a) fase non deterministica. Viene prodotta una stringa di caratteri, che individua una possibile soluzione del problema

b) fase polinomiale. L’ipotesi di soluzione è verificata con un algoritmo (deterministico) polinomiale

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.

Page 56: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

56

•••• Tutti gli algoritmi presentati nel paragrafo 9.2 ∈NP

•••• P⊆NP ; infatti l’algoritmo deterministico è un caso particolare di algoritmo non deterministico. •••• P=NP ? Questo è un problema aperto. Evidentemente, per qualunque problema in NP si possono costruire algoritmi del tipo: a) genero tutte le possibili ipotesi di soluzione b) applico la verifica polinomiale per ciascuna ipotesi. Tali algoritmi sono però esponenziali. Un algoritmo di SORT che usa questa idea è il seguente: genero a caso una delle n! permutazioni degli indici delle chiavi. Verifico in tempo polinomiale se la permutazione è un ordinamento valido. La situazione (da diversi anni) è la seguente: - non è stato trovato un algoritmo polinomiale per nessuno dei problemi visti. - non è stato dimostrato un lower bound esponenziale per nessuno di questi

problemi (i lower bound trovati sono tutti polinomiali) 9.4 – LA CLASSE DI PROBLEMI NP-COMPLETI 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. ••••Trasformazione(riduzione) polinomiale T di un problema Π1 in un problema Π2 Si effettua nel modo seguente: T Algoritmo per Π2 si/no x (input di Π1) T(x) Algoritmo per Π1 Esempio. Π1:= date n variabili booleane xi , almeno una di queste è TRUE?

Page 57: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

57

Π2:= dati n interi, il loro massimo è positivo? Una trasformazione polinomiale adatta è la seguente: T(x1 , x2 ……..xn )= y1 , y2 ……..yn Dove , yi=1 se xi= TRUE, yi=0 se xi= FALSE •••• Definizione: Un problema Π è NP-completo se: a) Π ∈NP b) ogni altro problema Π‘∈NP è trasformabile polinomialmente in Π •••• Importanza della classe NP-completa. Dalla definizione precedente, se ci fosse un algoritmo polinomiale per un problema NP-completo, ci sarebbe per tutta la classe NP: sarebbe cioè P=NP. Che esistano problemi NP-completi è garantito dal teorema seguente. •••• Teorema di COOK(1971): Il problema CNF-SAT è NP-completo Successivamente si è dimostrato che molti altri problemi sono NP- completi. Sono NP- completi tutti i problemi del paragrafo 9.2. •••• Come si dimostra che un problemaΠ è NP-completo.

a) Si parte da un problema Π’ che sia NP-completo. Lo si trasforma polinomialmente in Π. Questo significa che tutti i problemi in NP sono trasformabili polinomialmente in Π:

la classe di problemi NP Π’ Π

trasformazione polinomiale b) Si dimostra che appartiene ad NP N.B. Per alcuni problemi si riesce a fare solo il passo a). In tal caso si dice che l’algoritmo è NP-hard: è almeno difficile come un problema NP-completo. Esempio. Problema: è possibile con k guardie (telecamere) sorvegliare un ambiente di area poligonale? Il problema è detto dell’Art Gallery. E’ stato dimostrato che il problema Art Gallery è NP-hard , trasformando il problema 3-SAT (CNF-SAT con 3 variabili per ogni prodotto) nel problema Art Gallery (O’Rourke and Supowit (1983), per poligoni con buchi poligonali, Lee and Lin (1986) per poligoni senza buchi). Non è stato invece dimostrato che il problema Art Gallery sia in NP. •••• La semplificazione può far passare da NP-completo a P - 3-SAT è NP-completo, 2-SAT è in P - non è noto se verificare l’isomorfismo di due grafi sia in P o in NP, ma per grafi

planari è in P

Page 58: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

58

- la 2-colorabilità è in P; la 3-colorabilità è NP-completo •••• Esempio di dimostrazione di NP-completezza.

Cook ha dimostrato che CNF-SAT è NP-completo. Dimostriamo che lo è anche 3-SAT, trasformando una qualunque forma CNF-SAT in una forma 3-SAT che è TRUE se e solo se lo è la prima. La forma CNF-SAT contiene termini con la somma di 1, 2, 3…variabili. Per ogni caso, devo trovare una regola per passare ad uno o più termini con tre variabili. - caso di una variabile: x1 ⇒ (x1+ y + z) (x1+ y + z) (x1+ y + z) (x1+ y + z) con y e z nuove variabili. La sottolineatura indica la complementazione. Il prodotto dei 4 termini da tre variabili è TRUE se e solo se x1=TRUE - caso di due variabili: (x1 + x2)⇒ (x1+ x2 + z) (x1+ x2 + z) con z nuova variabile. Il prodotto dei 2 termini da tre variabili è TRUE se e solo se x1=TRUE - caso di tre variabili: il termine rimane inalterato - caso di k≥4 variabili. Una sostituzione che soddisfa le condizioni volute è la seguente: (x1 + x2 ….. + xk)⇒ (x1+ x2 + y1) (x3+ y1 + y2) (x4+ y2 + y3)…….. (xk-1+ xk + yk-3) Infatti: se il primo termine è TRUE, almeno una variabile xi è TRUE. Il secondo termine (quello con i prodotti di 3 variabili) è TRUE con i seguenti valori delle nuove variabili: y1= y2= y3=…….. yi-2= TRUE ; yi-1= yi= yi+1=……. yk-3= FALSE Viceversa, dobbiamo provare che se il secondo termine è TRUE, lo è anche almeno una variabile xi del primo termine. Per assurdo, supponiamo che tutte le variabili xi siano FALSE. In tal caso, la seconda espressione diventa: ( y1) ( y1 + y2) ( y2 + y3)…….. ( yk-4 + yk-3) ( yk-3) Sviluppando in somme(OR) di prodotti(AND), si ottengono tutti termini che contengono una variabile ed il suo complemento. Poiché yi yi= FALSE, tutti i termini sono FALSE, e lo è anche la seconda espressione, contraddicendo l’ipotesi. 9.5 – COME AFFRONTARE I PROBLEMI NP-COMPLETI Molti importanti problemi pratici sono NP-completi (o peggio). Si possono affrontare con due tipi di algoritmi: a) Algoritmi polinomiali approssimati. Anche se questi algoritmi non sempre

forniscono l’ottimo, possono essere utili per vari motivi: - potrebbero essere a prestazioni garantite

Page 59: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

59

- potrebbero avere un buon comportamento sperimentale

b) Algoritmi esatti, che abbiano un buon comportamento in media, anche esponenziali in improbabili casi pessimi

9.5.1 – ESEMPI DI ALGORITMI APPROSSIMATI •••• Esempio 1 - Problema: Bin packing - Esempio di algoritmo esatto: suddividere in tutti i modi possibili gli oggetti in k≤n

subset. E’ O(nn). - Algoritmo approssimato: Fist Fit Decreasing (FFD): 1) ordino le dimensioni si in

modo non crescente ; 2) esamino i bin dall’inizio e sistemo l’oggetto di dimensioni si nel primo bin che lo contiene.

- Analisi del caso pessimo di FFD: l’ordinamento è O(nlgn); se la sistemazione dell’oggetto i-esimo è nell’ i-esimo contenitore faccio n(n-1)/2 tentativi. In totale, l’algoritmo è Θ(n2).

- Esempio: s=(0.8, 0.5, 0.4, 0.4, 0.3, 0.2, 0.2, 0.2)

Non è ottimo(bastano 3 bin: (0.8, 0.2)(0.5, 0.3, 0.2)(0.4, 0.4, 0.2)).

- Analisi della qualità dell’algoritmo. Definiamo: r(I)= =

0.40.5

0.2

0.3

0.2 0.4

0.8

0.2

soluzione data da FFD con input I

soluzione ottima con input I

FFD(I)

OPT(I)

Page 60: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

60

R(m)=maxr(I)|I tale che OPT(I)=m Si può dimostrare che: R(m)≤(4/3)+1/3m Definendo: S(n)=maxr(I)|I di dimensione n si dimostra che: S(n)≤3/2 ; per n→∞, S(n)→3/2 Recentemente per R(m) è stato dimostrato un bound migliore: R(m)≤(11/9)+4/m Il bound sembra non migliorabile, poiché ci sono esempi in cui R(m)>11/9 L’algoritmo è stato anche studiato sperimentalmente con ottimi risultati medi. •••• 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.

- Algoritmo esatto. Si forma la presence function:

)(1

∑Π∈= I ji

i

n

j

q

qi= variabile logica TRUE se SSi fa parte della copertura Ij= insieme degli indici dei subset che contengono xj Si trasforma l’espressione in somme di prodotti. Ogni prodotto rappresenta una possibile copertura per cui calcolare la somma dei costi. Complessità: i prodotti sono O(mn), ciascuno con O(m) termini.

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:

≤ 1 + lg r

costo della soluzione data dall’alg. approssimato

costo della soluzione data dall’algoritmo esatto

Page 61: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

61

r= massimo numero di elementi in un sottoinsieme Complessità: O(m2n)

Esempio:

Caso precedente, costi uguali. Prima scelta: q2 (copre 3 elementi) Seconda scelta: q4 (copre l’unico elemento scoperto) Soluzione: q2 q4 9.5.2 – ESEMPI DI TECNICHE PER RIDURRE I CASI ESPLORATI •••• 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.

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.

1

2

3

4

5

1B, 2G

3R 3B

4G 4B 4R 4B

5R

Page 62: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

62

Problema: ricerca di una colorazione minima. Algoritmo: Costruiamo, come nell’esempio precedente, un albero di scelte. Ad ogni nuovo nodo usiamo, se possibile, uno dei colori già usati, altrimenti uno nuovo. Procediamo in profondità, senza esplorare le varie scelte possibili. Quando abbiamo colorato tutti i vertici, il numero di colori usati k serve da bound per l’esplorazione delle altre possibilità: non esploro più soluzioni che richiedano più di k colori ma effettuo un backtracking. Se trovo una nuova colorazione con k’<k colori, uso k’ come nuovo bound. Si noti che: - l’algoritmo potrebbe beneficiare di buone euristiche o tecniche approssimate per

trovare il bound - si può arrestare l’algoritmo prima dell’esplorazione completa dell’albero,

ottenendo soluzioni sub-ottime

10- ALGORITMI PARALLELI • 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 • Numerosi problemi ammettono algoritmi paralleli. Ad esempio: - prodotti di matrici, ricerche, ordinamenti - algoritmi di analisi segnali (FFT, convoluzioni, etc.) - problemi di ottimizzazione - algoritmi per la Computer Graphics - problemi relativi a grafi - problemi NP-completi (esame in parallelo di molti casi)

Page 63: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

63

• Gli algoritmi paralleli richiedono la soluzione di ulteriori problemi di comunicazione e sincronizzazione tra i processori. • Ci sono vari modelli di calcolo parallelo, ad esempio: -SIMD(Single-Instruction Multiple-Data) -MIMD(Multiple-Instruction Multiple-Data) Nel seguito, salvo diverso avviso, considereremo modelli MIMD. 10.1 – DEFINIZIONI GENERALI T(n,p):= tempo di esecuzione di un algoritmo, dipendente dalla dimensione n dell’INPUT e dal numero p di processori. S(p):= T(n,1)/T(n,p) è lo speedup (accellerazione) dovuta ai p processori. Evidentemente, al massimo per un utilizzo perfetto dei processori, si ha S(p)=p. E(n,p):= S(p)/p=T(n,1)/pT(n,p) e l’efficienza (al massimo uno) N.B. Per gli algoritmi paralleli importano le costanti moltiplicative. 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. L’idea è efficace solo per efficienza elevata. Ad esempio, supponiamo: T(n,1)=n ; T(n,n)=lgn; → S(n)=n/lng; E(n,n)=1/lgn Se n=1024 e abbiamo p=256 processori, secondo il parallelism folding principle si ha: T(1024,256)=4T(1024,1024)=4lg1024=40; → S(1024)=1024/40≅25 Se invece n=1024 e abbiamo solo p=16 processori: T(1024,16)=64T(1024,1024)=64lg1024=640; → S(1024)=1024/640<2 10.2 – ESEMPI DI ALGORITMI PARALLELI SHARED MEMORY Il modello shared memory (memoria condivisa) assume che tutti i processori possano accedere, con uguali tempi, ad una memoria comune (in pratica, aumentando il numero dei processori la cosa può non essere più vera). Il calcolo avviene in passi, di durata uguale per tutti i processori. Durante il passo, un processore effettua un’operazione, e legge (o scrive) nella memoria condivisa.

Page 64: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

64

Si distinguono vari sottocasi di disciplina degli accessi alla stessa locazione di memoria: - EREW(Exclusive-Read Exclusive-Write) - CREW(Concurrent-Read Exclusive-Write) - CRCW(Concurrent-Read Concurrent-Write) Nell’ultimo caso, supponiamo che la scrittura funzioni solo se i vari processori scrivono tutti la stessa cosa (sono possibili anche altri modelli). •••• Addizione parallela binaria - Algoritmo normale (seriale): sommo le cifre meno significative, produco il

riporto, sommo le cifre di una posizione più a destra con il riporto, …. L’algoritmo è O(n) a causa del riporto (n è il numero delle cifre). - Un possibile algoritmo parallelo: divido ciascuno degli addendi in due numeri da n/2 cifre; sommo le parti meno significative e contemporaneamente quelle più significative con e senza riporto (se le cifre non sono binarie, considero tutti i possibili riporti); quando giunge il riporto della parte meno significativa, scelgo la soluzione corretta per la parte più significativa. Si ha quindi, per un impiego recursivo dell’algoritmo: T(n,n)=T(n/2, n/2)→ T(n,n)∈O(lgn)

•••• Determinazione del massimo di n numeri - Algoritmo seriale: T(n,1)=n-1 - Algoritmo parallelo: metodo del torneo di tennis: n

n/2 n/4 lgn

2 1 T(n, n/2)=lgn E=(n-1)/((n/2)lgn)≅2/lgn Per migliorare l’efficienza, usiamo n/lgn processori, e dividiamo l’INPUT in n/lgn gruppi, ciascuno con lgn elementi. Algoritmo modificato:

Page 65: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

65

- ciascun processore esamina serialmente ciascun gruppo trovandone il massimo; T≅lgn

- si usa la tecnica del torneo per i vincitori(i processori sono all’inizio il doppio del necessario); T=lg(n/lgn)≅lgn

In totale: T(n, n/lgn)≅2lgn → E≅n/((n/lgn)2lgn) =1/2 Si dice statico un algoritmo in cui i processori hanno compiti predefiniti. L’algoritmo del torneo modificato è statico. L’idea dell’algoritmo per aumentare l’efficienza si può applicare a tutti gli algoritmi statici EREW. •••• Determinazione del massimo di n numeri, algoritmo CRCW Si può migliorare il tempo lgn per trovare il massimo? Sì, con tecnica CRCW. All’inizio ci sono n celle condivise contenenti 0. - n(n-1)/2 processori esaminano contemporaneamente tutte le n(n-1)/2 coppie, e

scrivono 1 nella cella corrispondente al numero vincente. Viene effettivamente scritto un solo uno nella cella corrispondente al massimo.

- n processori esaminano contemporaneamente le n celle; quello che trova un 1 scrive l’indice del vincitore in una apposita cella.

L’algoritmo funziona in tempo costante, ma con bassa efficienza: T(n, n(n-1)/2)=2→ E≅n/2n2 =1/2n 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) 10.3 – INTERCONNECTION NETWORKS Le interconnection networks sono modellate da grafi dove i nodi sono i processori, collegati da un lato se c’è una connessione diretta. Ogni processore ha una memoria locale e può accedere, tramite la rete, alla memoria di tutti gli altri processori. È importante il diametro del grafo (lunghezza del massimo cammino tra 2 nodi).

Page 66: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

66

Ad es., per un grafo fatto a griglia nxn, il diametro è 2n. Per un albero di profondità p, il diametro è 2p. Una struttura regolare per i collegamenti tra processori è l’ipercubo, un cubo d-dimensionale con i processori ai vertici. I processori sono n=2d, gli indirizzi o indici dei processori sono numeri binari con d bit da 0 a 2d – 1. La distanza tra due processori è il numero di bit diversi (distanza di Hamming), il diametro è d. • 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. 10.4 – ARCHITETTURA SISTOLICA E’ simile ad una linea di assemblaggio: ciascun processore opera in modo cadenzato sui dati che gli giungono dal processore precedente. Esempio: prodotto di una matrice A per un vettore X. a xin b xout = xin +ab a44 a43 a34 a42 a33 a24 a41 a32 a23 a14 a31 a22 a13 a21 a12

a11

Page 67: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

67

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?

Page 68: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

68

11- GRAFI Servono a modellare molti problemi pratici relativi a: - trasporti - reti tecnologiche - comunicazioni, protocolli - reti elettriche, di calcolatori - esecuzione di task, programmazione - relazioni tra elementi d’informazione, compatibilità - etc. 11.1 – DEFINIZIONI E PROPRIETA’ • Un grafo (graph) è un’entità astratta, una coppia di insiemi. G(V,E):= insieme V di elementi detti nodi o vertici + insieme E di coppie di vertici, dette anche lati.

Page 69: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

69

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: 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

• Indicheremo con V e con E il numero di vertici e di lati di un grafo (o anche con V e con E). Per i grafi di cui ci occupiamo (semplici e senza self-loops): E ≤V(V-1)/2 ⇒ E∈O(V2) • Se esistono tutti i possibili lati, il grafo si dice completo, ed è E = V(V-1)/2. Kn indica il grafo completo con n vertici. Ad es.: K3 K4 • Grafi orientati (di(rected)-graphs):= grafi in cui i sottoinsiemi di ordine 2 (lati) sono ordinati. Esempio 2

A

B

D

C

F

E G

H

Page 70: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

70

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. • Un percorso (cammino, path) di lunghezza k è una sequenza di k+1 vertici adiacenti. La lunghezza è il numero di lati del percorso. In Esempio 1, ACD, ACF, ADCF, sono percorsi di lunghezze 2,2,3. Un percorso è semplice se un nodo non figura 2 o più volte. Spesso l’aggettivo si omette. Se il grafo è orientato, i lati possono essere percorsi solo in una direzione. Un nodo è raggiungibile da un altro se esiste un cammino che dal primo giunge al secondo. • La distanza tra due nodi è la lunghezza del percorso più breve tra i due nodi. • Il diametro di un grafo è la massima distanza tra due nodi del grafo. • Un circuito (circuit) è un percorso con nodo iniziale uguale al nodo finale. Il circuito è semplice se non ci sono altri vertici che compaiono più di una volta ed in tal caso si dice ciclo(cycle). In Esempio 1, ADC e ACD sono cicli. In Esempio 2 non esistono cicli. • 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.

A

B

D

C

F

E G

H

Page 71: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

71

• Un grafo è detto un albero se è una foresta connessa (aciclico e connesso) Esempio di foresta Esempio di albero • Un albero è radicato ( rooted tree) se si sceglie un suo nodo qualunque come radice. Gli altri nodi possono essere organizzati in livelli a seconda della distanza dalla radice. Dall’albero dell’esempio precedente si possono estrarre 5 alberi radicati a seconda della scelta del nodo radice, ad esempio, scegliendo come radici C e D: radice livello 1 livello 2 livello 3 • G(V’, E’) è un sottografo di G(V, E) se V’⊆V ed E’⊆E • G(V’, E’) è un albero ricoprente (spanning tree) se è un sottografo aciclico con tutti i nodi: V’=V ed E’⊆E. Esempi:

A

B

D

C

F

E G

H

A

B

D

C

F

C

A D F

B

D

C

A F

B

Page 72: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

72

Grafo Albero ricoprente 1 Albero ricoprente 2

• La foresta ricoprente (spanning forest) è un sottografo con tutti i nodi di un grafo non connesso. 11.2 – RAPPRESENTAZIONE DEI GRAFI • Matrice di adiacenza 1 se (Vi Vj)∈E (se grafo orientato, +1 o -1 a seconda della direzione) aij= 0 se (Vi Vj)∉E E’ simmetrica per un grafo non orientato. Lo spazio occupato, per grafi orientati o non, è Θ(V2). 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

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

A

B

D

C

F

E G

H

Page 73: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

73

• Liste di adiacenza Per ogni nodo si memorizza la lista dei nodi adiacenti. Ad esempio, per il grafo precedente:

Altro es. per grafo pesato: Lo spazio occupato, per grafi orientati o non, è Θ(V+E). 11.3 – CHIUSURA TRANSITIVA Si abbia un grafo G(V, E) orientato o non orientato. Si definisce chiusura transitiva (transitive closure) del grafo dato il grafo CT(V’, E’) che ha le proprietà seguenti: - V’=V

A A B C D

B A

Nil

Nil

C A D

Nil

Nil

A A

Nil

D D A C

G E

H

A

E G

Nil F C Nil

Nil

Nil

1 2

2

3

A 4

Nil 25

3 2

1

3 4

18

14 10

5

6 Nil

Nil

Nil 1

2

4

3

25

5

10

6

14

18

Page 74: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

74

- E’ contiene il lato ViVj se nel grafo G(V, E) esiste un percorso dal nodo Vi al nodo Vj .

Esempio:

G(V, E) CT(V’, E’)

Lo stesso nome (chiusura transitiva) prende la matrice di adiacenza di CT(V’, E’) • Algoritmo 1 per la costruzione di CT Consideriamo la matrice di adiacenza A di G(V, E) come una matrice Booleana (1=TRUE, 0=FALSE). Moltiplichiamo la matrice per se stessa usando somma e prodotto Booleani (OR ed AND) al posto di somma e prodotti normali: A2=AxA a2

ij=( ai1 AND a1j)OR( ai2 AND a2j)OR………. a2

ij=1 se esiste un percorso di lunghezza 2 tra il nodo Vi e il nodo Vj. Analogamente si definiscono le matrici A3=AxA2 ; A4=AxA3 etc. i cui elementi sono 1 se esistono percorsi di lunghezza 3, 4, etc., tra il nodo Vi e il nodo Vj. Ad esempio, per il grafo precedente si ha: 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 A= C 0 0 0 1 1 A2= 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: CT= A+A2 + A3+……… An

A

B

D

C

E A

B

D

C

E

Page 75: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

75

Per il grafo dell’esempio: 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. L’algoritmo richiede Θ(V3) operazioni per ogni matrice, ed e’ quindi Θ(V4). Algoritmo 2 (di Warshall) Definiamo le matrici Pk : 1 logico (TRUE) se se esiste un cammino tra Vi e Vj con indici ≤k, esclusi i e j pk

ij:= 0 logico (FALSE) altrimenti Vk+1 Da Pk si puo’ ottenere Pk+1 nel modo seguente: pk

ij = 1, oppure pk+1

ij = 1 se Vi Vj pk

i,k+1 = 1 e pkk+1,j = 1 ≤k

Si puo’ costruire CT= Pn a partire da P0 = A con un algoritmo che usa: - un loop esterno con indice k eseguito Θ(V) volte - un loop interno con indici i e j da eseguire Θ(V2) volte: pk+1

ij = (pkij )OR((pk

i,k+1)AND(pkk+1,j))

In totale l’algoritmo e’ Θ(V3) 11.4 – VISITA DI UN GRAFO (GRAPH TRAVERSAL) 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.

Page 76: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

76

• 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) Realizzazione recursiva: procedure DepthFirst(V) begin visita e marca V

while c’è un vertice W adiacente non marcato do DepthFirst(W)

endwhile end DepthFirst

b) Realizzazione non recursiva(usa uno stack): procedure DepthFirst(V) begin inizializza lo Stack(vuoto)

visita, marca, PUSH V

while Stack non è vuoto do while c’è un vertice W adiacente a TopofStack non marcato do

visita, marca, PUSH W

endwhile Pop TopofStack

end while end DepthFirst

A

B C D

E H

I

L

M

F G

A

B C D

E H

L

M

F G

1

2

3

4 5

6 7

8

9

11

10I

Page 77: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

77

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). • Visita in ampiezza (larghezza) Descrizione informale dell’algoritmo. Parto da un nodo qualsiasi, e visito prima tutti quelli a distanza 1, poi quelli a distanza 2, etc. Esempio: Possibile realizzazione (uso una coda). 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

endfor end while

end BreadthFirst L’ordine di visita è legato alla struttura dati usata. Complessità. Usando liste di adiacenza, l’algoritmo è O(V+E). Se uso invece la matrice di adiacenza, l’algoritmo è O(V2).

10

A

B C D

H I

L

M

F G

A

B C D

E H

L

M

F G

1

2 3 4

5 6

7

8

9

11

I E

Page 78: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

78

Evoluzione della coda dell’esempio 11.5 – ALBERO RICOPRENTE MINIMO (MINIMUM SPANNING TREE)

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: •••• Algoritmo di Dijkstra/Prim

A

C B

I

E

D

M

C D

E D

I E L

M I L G H F

M L G H F

L G H F

G H F

G F

F

2

2 1 2 1 1

2

Page 79: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

79

Descrizione. E’ un algoritmo greedy(avido), che ricerca un ottimo locale ed ottiene l’ottimo globale. 1) parto da un vertice arbitrario, che inserisco nell’albero 2) Ad ogni passo aggiungo all’albero un nuovo vertice. I vertici sono di tre categorie: - quelli già scelti - quelli a distanza 1 da quelli già scelti - tutti gli altri Scelgo tra i vertici a distanza 1 quello che ha il collegamento con uno già scelto di costo minimo. Esempio:

1

A B

C

D E

H

F G

2

7 3 4

5

6 3

2 I 6 2

4 8

1 2

A B

F G

2

7 3 Scelgo il

nodo A ⇒

MST

Scelgo il nodo B ⇒

A B

C F G

2

7 3 4 6

MST

Scelgo il nodo G ⇒

A B

C H

F G

2

7 3 4

3 1

MST Scelgo il nodo I ⇒ ……..

Page 80: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

80

L’algoritmo porta all’ottimo globale. Dimostrazione. Per induzione: supponiamo di avere ad ogni passo un sotto-insieme connesso di lati del MST. Dimostriamo che, aggiungendo un nuovo lato vw secondo la regola dell’algoritmo, ottengo ancora un sotto-insieme connesso di lati del MST. Per assurdo, se aggiungendo vw non ho costruito un MST, deve esistere un collegamento tra v e w di costo minore . Ma ciò è impossibile, perché qualunque altro collegamento deve includere un lato come pq, che da solo ha peso maggiore di vw. 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 endfor

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

1

A B

C

D E

H

F G

2

3

5 2

2 I

2

1

v

w

p q

Page 81: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

81

dist(V)=min dist(V), peso(V,W endfor

end while end MST

Complessità. Il primo loop di inizializzazione deve essere eseguito V-1 volte. Il secondo loop ha un loop più interno. Quello esterno si esegue V-1 volte, quello interno richiede nel caso peggiore di esaminare tutti i nodi non ancora aggiunti a MST. In totale, l’algoritmo è O(V2). Algoritmo più efficiente. Usa una struttura dati detta coda prioritaria (si veda più avanti). E’ O(V+ElgV) 11.6 – ALBERO DEI CAMMINI MINIMI (SHORTEST PATH) Si abbia un grafo pesato, con pesi positivi, eventualmente orientato. Problema: Trovare il percorso tra due nodi con somma dei pesi minima. Si parla anche di percorso più breve tra due nodi. In pratica i pesi possono essere lunghezze, tempi, costi. Si potrebbe pensare che un MST sia un albero di cammini minimi, ma non è vero in generale: nell’esempio precedente il cammino AC sull’MST ha costo 9, ma il percorso ABC sul grafo originale costa 6 •••• Algoritmo (di Dijkstra)

Descrizione. E’ un algoritmo che aggiunge un nodo per volta, e determina i cammini minimi tra un nodo V0 e tutti gli altri costruendo un albero SP a partire da V0. 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

L’algoritmo produce l’albero dei cammini minimi per il motivo seguente.

Page 82: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

82

SP 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). Complessità. Un’implementazione banale, del tipo visto per l’albero ricoprente minimo, è O(V2). Si possono migliorare le cose usando una coda prioritaria, ottenendo un algoritmo O(V+ElgV). Esempio: costruzione dell’albero dei cammini minimi a partire dal nodo A. d(AB)=2 d(AG)=3 d(AF)=7 scelgo AB d(AG)=Mind(AG);d(AB)+peso(BG)=3 d(AC)= Min∞;d(AB)+peso(BC)=6 d(AF)=7 scelgo AG

Vx

V0

Vq

Vp

Vr

Vy

A B

C F G

2

7 3 4 6

1

A B

C

D E

H

F G

2

7 3 4

5

6 3

2 I 6 2

4 8

1 2

A B

F G

2

7 3

A B

C H

F G

2

7 3 4

3 1

Page 83: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

83

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)= MINd(AF);d(AI)+peso(IF)=7 d(AH)= MINd(AH);d(AI)+peso(IH)=6 d(AC)=6

scelgo E………….. ………..ed alla fine: L’albero dei cammini minimi 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. I grafi bipartiti completi (ogni nodo di un sottoinsieme è collegato ad un nodo dell’altro) si indicano con Knm, dove n e m sono i numeri di nodi nei due insiemi. Ad esempio, questo è K33:

1

A B

C

D E

H

F G

2

7 3 4

3

I

2

1

Page 84: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

84

•••• Isomorfismo . Due grafi G(V,E) e G(V’,E’) sono isomorfi se i vertici di V possono essere messi in corrispondenza biunivoca con i vertici di V’ in modo tale che anche i lati di E siano in corrispondenza biunivoca con quelli di E’. In sostanza i due grafi sono uguali, hanno solo nomi diversi per i nodi. Esempio: A⇔1 B⇔2 C⇔3 D⇔4 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. •••• Automorfismo:= è l’isomorfismo di un grafo con se stesso. Ad esempio, il grafo precedente (K4) è automorfo in tutti i modi possibili (3x2=6) •••• Omeomorfismo. Due grafi sono omeomorfi se isomorfi a meno di nodi di grado 2. Esempio di grafi omeomorfi: 11.8– GRAFI PLANARI Un grafo è una struttura astratta, ma talora i lati hanno significato di collegamento fisico ( strada, percorso, collegamento elettrico….). In questi casi è importante la planarità. Definizione. Un grafo è planare se si può disegnare su di un piano (o sulla superficie topologicamente equivalente di una sfera) senza che due lati si intersechino. Esempio. ⇒ ⇒ 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:

D

B

A C

4

1

2

3

K4

1 2

3

4 5

1 2

3

4 5

1 2

3

4 5

Page 85: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

85

Qualunque grafo Kn per n>5 contiene K5 come sottografo e quindi non è planare. • Teorema di Kuratowski(1930). Un grafo è planare se e solo se non contiene alcun sottografo omeomorfo a K5 o K33. Questo teorema suggerisce un algoritmo di verifica della planarità: è sufficiente considerare tutti gli insiemi di 5 vertici per vedere se formano K5 , e tutte le coppie di insiemi di tre vertici per vedere se formano K33. L’algoritmo è O(n6). 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. • Faccia di un grafo planare E’ un insieme di punti connessi sul piano dove ho disegnato il grafo planare. Esempi:

f1 f2

f3

f4 : faccia esterna infinita

f1

f2 f3

Page 86: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

86

• Teorema di Eulero(1752) Se un grafo planare connesso ha V nodi, E lati e F facce, si ha: V+F=E+2

Dimostrazione. Per induzione su E. Il teorema è vero per E=0, V=1, F=1. Supposto vero per un certo valore di E, dimostriamolo per E+1. Ci sono 3 possibilità per il nuovo lato: - è un cappio. In tal caso, F+1→F, E+1→E, l’eguaglianza rimane verificata - congiunge due nodi già esistenti. In tal caso, F+1→F, E+1→E, l’eguaglianza

rimane verificata - congiunge un nodo vecchio ed uno nuovo. F rimane invariato, V+1→V, E+1→E,

l’eguaglianza rimane verificata 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. • Teorema di Eulero per grafi planari non connessi Se il grafo ha K componenti non connessi, si ha: V+F=E+K+1 N.B. Per un grafo planare semplice connesso: se )(633 VOEVEV ∈⇒−≤⇒≥ Dato che si ha anche:

1−≥VE abbiamo che, per un grafo planare semplice:

)(VE Θ∈ • Grafo duale di un grafo planare Il grafo duale G* di un grafo planare G ha: - un vertice per ogni faccia di G - una faccia per ogni vertice di G - un lato per ogni lato di G, che collega I due vertici corrispondenti alle 2 facce

separate dal lato di G

Page 87: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

87

Il duale del grafo duale è il grafo di partenza: G** =G Esempio:

• 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. Il teorema di Eulero si può generalizzare: V+F=E+(2-2G)

Page 88: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

88

Due grafi non planari di genus 1 si possono disegnare senza intersezioni su di un toro. 11.9– GRAFI EULERIANI E SEMI-EULERIANI Il problema fu posto e risolto da Eulero (1707-1783) con riferimento ai ponti di Koenigsberg sul fiume Pregal( Prussia, 1736).

Page 89: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

89

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 • Grafo Euleriano :=un grafo che contiene (almeno) un ciclo Euleriano • Teorema: Condizione necessaria e sufficiente perché un grafo connesso sia Euleriano è che i gradi di tutti i nodi siano pari. 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.

Page 90: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

90

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:

Ciclo 1 Ciclo 2 Ciclo 3 Ciclo 4 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

1 2

3

4 5

6

7 8

1 2

3

8 6

1 2

3

4 5

6

7 8

3

4 5

1

2

4 5

6

7 8

4

7 8

4

7 8

2

5

6

4

Page 91: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

91

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

• Algoritmo di Fleury. Per trovare un ciclo Euleriano si può anche applicare l’algoritmo di Fleury: si parte da un nodo arbitrario, e si visita il grafo in profondità scegliendo ogni volta un lato non percorso, non percorrendo istmi del grafo costituito dai lati non ancora percorsi se ci sono altre scelte. Un istmo è un lato che, se soppresso, rende non connesso un grafo connesso. • Cammino Euleriano di un grafo G:= un cammino(vertice iniziale e finale diversi) che contiene una sola volta tutti i lati del grafo • Grafo Semi-Euleriano :=un grafo che contiene (almeno) un cammino Euleriano • Teorema. Condizione necessaria e sufficiente perché un grafo connesso sia Semi-Euleriano è che i gradi di tutti i nodi siano pari, tranne due. 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.

Page 92: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

92

Grafo non Euleriano(tutti nodi di grado dispari) Grafo Semi-Euleriano (due soli

nodi dispari) 11.10– GRAFI HAMILTONIANI E SEMI-HAMILTONIANI 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 Si definiscono grafi Hamiltoniani o semi-Hamiltoniani quelli che contengono un ciclo o un cammino di Hamilton. Esempi

Grafo non Hamiltoniano Grafo semi-Hamiltoniano Grafo Hamiltoniano Si consideri il problema di decisione: dato G(V, E), esiste un cammino Hamiltoniano? E’ stato dimostrato che il problema è NP-completo. Dirac dimostrò una condizione sufficiente: -un grafo è Hamiltoniano se V≥3 ed il grado di tutti i nodi è ≥ V/2 • Problema del commesso viaggiatore(traveling salesperson): trovare, per un grafo pesato, il ciclo Hamiltoniano di costo totale (somma dei pesi) minimo. Il problema è difficile almeno quanto quello di trovare un ciclo Hamiltoniano.

Page 93: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

93

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 Si dice clique (cricca)di un grafo un suo sottografo massimo completo. Massimo significa non contenuto in un altro sottografo completo. Si dice clique number di un grafo la massima cardinalità (massimo numero di nodi) di una clique. Per indicare il numero di vertici si usa anche il termine k-clique N.B. Su alcuni testi il termine clique indica un qualunque sottografo completo. Il problema :dato un grafo, trovare se contiene un sottografo completo di k o più vertici è NP-completo 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 Il problema della k-colorabilità per k ≥3 e del numero cromatico sono NP-completi . Esistono vari algoritmi approssimati polinomiali di colorazione, ma nessuno ad approssimazione garantita, e non è probabile che esistano. E’ stato dimostrato che , se esistesse un algoritmo di colorazione approssimato che usasse al massimo il doppio del numero cromatico, si potrebbe ottenere il numero cromatico in tempo polinomiale (e quindi sarebbe P=NP). •••• Esempio di problema schematizzabile come ricerca di numero cromatico Problema degli esami: si abbia un periodo destinato ad e esami con un certo numero t di time slots assegnabili ai vari esami da mettere a calendario (ad es. 3 periodi al giorno per una settimana, t=15). Alcuni esami non sono compatibili tra di loro, e devono essere posti in slots diversi. E’ possibile calendarizzare gli esami negli slots disponibili rispettando le incompatibilità? Si costruisca un grafo con e vertici, collegati da un lato se incompatibili. Un’assegnazione degli esami agli slots corrisponde ad una colorazione: esami

Page 94: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

94

incompatibili devono corrispondere a nodi con colori diversi. L’assegnazione è possibile se il numero cromatico è ≤ t. •••• Colorabilità di grafi planari.Per più di un secolo, nessuno è stato in grado di dimostrare, o di smentire con un esempio, la congettura dei 4 colori, secondo cui 4 colori bastano a colorare qualunque grafo planare(proposta da Guthrie nel 1852). E’ chiaro che 3 colori non sono sufficienti (per esempio, a colorare K4 che è planare); da tempo era stato dimostrato che 5 colori sono sempre sufficienti. Si noti che il problema della colorazione di un grafo planare è equivalente al problema della colorazione di una carta geografica. La carta geografica può essere trasformata in un grafo planare facendo corrispondere ad ogni stato un nodo e ad ogni confine un lato. Solo recentemente(Appel e Haken 1976) è stata trovata una dimostrazione. Tuttavia la dimostrazione è molto complessa, e ha richiesto l’uso di un calcolatore per esaminare un gran numero di alternative. Sono state proposte varianti sempre assai complesse. Manca tuttora una dimostrazione semplice. 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. Insieme dominante minimale (non riducibile): = insieme di vertici di un grafo G non orientato tale che: - ogni altro vertice è adiacente ad un vertice dell’insieme - nessun suo sottoinsieme è un insieme dominante. Insieme dominante minimo: = insieme dominante di cardinalità minima di un grafo G. Il numero di nodi α(G) dell’insieme dominante minimo è detto numero di dominanza. Esempio. adef,adeb : insiemi dominanti fdeabc: insiemi dominanti minimali

a

c

d

f e

b

d

Page 95: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

95

fbfa: insiemi dominanti minimi Diversi problemi si possono schematizzare con gli insiemi dominanti. Ad esempio, il grafo può schematizzare una rete di trasmissione, e l’insieme dominante minimo le stazioni di trasmissione che coprono tutta la rete. 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

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. Esempii di problemi schematizzabili mediante insiemi dominanti Problema della scacchiera

Sia data una scacchiera (8X8 caselle). Si vuol disporre il numero minimo di regine che copre ( tiene sotto scacco) tutte le caselle. 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 Una delle soluzioni:

d

b c

a

Page 96: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

96

Problema dei parcheggi

Si supponga di avere la pianta di una rete stradale urbana con parcheggio a pagamento. Vogliamo sistemare agli incroci il minimo numero di macchinette distributrici di biglietti per il parcheggio in modo tale che almeno una sia visibile da ogni punto delle strade. Soluzione: Se le strade sono rettilinee tra un incrocio e un altro, la zona si può schematizzare con un grafo, dove i nodi sono gli incroci e le strade i lati. Gli incroci dove sistemare il minimo numero di macchinette corrispondono ad un insieme minimo dominante. Un teorema sugli insiemi dominanti

Dato un grafo senza nodi isolati, se l’insieme di nodi ID è un insieme dominante minimo, anche l’insieme complementare V-ID è un insieme dominante minimo. Ne segue: - ogni grafo senza nodi isolati ha almeno due insiemi dominanti

- l’insieme dominante minimo contiene V/2 nodi 11.14– INDIPENDENZA Un insieme di vertici di un grafo G è un insieme indipendente(independent set) se non comprende vertici adiacenti. Un insieme è dipendente se almeno due nodi sono adiacenti. Un insieme indipendente è massimale se diventa dipendente aggiungendo un nodo qualunque.

Page 97: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

97

Esempio. (b,e,g) è un insieme indipendente, ma non massimale (si può aggiungere d) (b,d,e,g) , (b,h), (e,c) sono massimali (non si può aggiungere nessun nodo). 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 Esempii di problemi schematizzabili come ricerca di insiemi indipendenti massimi

Esempio 1

Si vogliono invitare ad una festa il massimo numero di amici. Qualcuno di loro ha però litigato con qualcun altro, e non si possono invitare insieme. Si associa ad ogni amico un nodo, ed un lato ad ogni coppia di amici che ha litigato. Il problema è ridotto alla ricerca dell’insieme indipendente massimo. 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 Una delle soluzioni:

b

c

d

e

f

g

h

b

a

Page 98: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

98

ESERCIZI E PROBLEMI

Page 99: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

99

• Determinare la chiusura transitiva per il grafo

• Scrivere un programma C che determina i componenti connessi di un grafo G(V,E)

• Progettare un algoritmo di visita depth first per un grafo che fornisca una lista dei nodi nell’ordine in cui sono stati incontrati la prima volta.

• Determinare un albero ricoprente minimo per il grafo pesato

• Progettare e scrivere un programma C per determinare l’albero ricoprente minimo di un grafo G(V,E)

• Determinare l’albero dei cammini minimi a partire dal nodo più a sinistra per il grafo del terzo esercizio • Progettare un algoritmo che verifica se un grafo è un albero. Determinarne la complessità. • Progettare un algoritmo per verificare se un grafo è bi-connesso, e determinarne la complessità (un grafo è bi-connesso se, in qualunque modo si raggruppino i nodi in due insiemi, esistono sempre almeno due lati che congiungono nodi del primo e del secondo insieme). • Progettare un algoritmo per verificare se un grafo è tri-connesso, e determinarne la complessità • Progettare un algoritmo per verificare se un grafo è bipartito. Determinarne la complessità. • Si voglia trovare il cammino più breve tra un nodo e un altro con lati tutti dello stesso peso (ad es., il problema è pianificare un viaggio aereo col minor numero di soste). Si può usare un algoritmo più semplice di quello di Dijkstra, magari lineare?

2

1

3

3

2 6

5

2

3 1

4

4

1 2 5

2

Page 100: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

100

• 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 • Determinare i grafi duali di K4 e K23

• E’ possibile determinare i grafi duali di K6 e K43 ?

• Verificare il teorema di Eulero, o una delle sue varianti, sui due grafi seguenti • Verificare il teorema di Eulero, o una della sue varianti, sui poliedri seguenti

• Verificare se i grafi seguenti sono Euleriani o Semi-euleriani. Nel caso lo siano, determinare un ciclo o un cammino Euleriano

G1

G2

Page 101: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

101

• Progettare un programma C per la determinazione dei cicli o cammini Euleriani. Analizzarne la complessità • Dire se sono Euleriani o Semieuleriani i grafi K43 , K6 , K5 . • Verificare se sono Hamiltoniani o Semi-hamiltoniani i grafi seguenti:

• Trovare una Maxclique dei grafi seguenti

• Determinare Maxclique per i grafi K4,3 , K7 , K2,2

• Quanto vale al max. il clique number di un grafo planare?

• Determinare se il grafo seguente e tre-colorabile, e se lo è trovare una colorazione

• Determinare, per il grafo precedente, un insieme dominante minimo ed un insieme indipendente massimo

• 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

G1

G2

Page 102: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

102

con n vertici, nel numerare i colori progressivamente, e nell’assegnare ad ogni vertice non ancora colorato il colore più basso nella lista dei colori non assegnato a vicini del vertice.

- Si trovi la complessità dell’algoritmo

- Si dimostri che il numero di colori usato è non superiore a (Max. grado di un vertice del grafo)+1

Si trovino degli esempi in cui il metodo è inefficiente • Si determini un ciclo Hamiltoniano sul grafo seguente

12- ALGORITMI DI RICERCA

Page 103: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

103

La ricerca dell’informazione collegata ad una chiave di ricerca (key) è un’operazione frequentissima in tutte le applicazioni tecniche e gestionali. 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 Gli ADT relativi alle ricerche si distinguono in: - Dizionari, in cui si cerca un elemento qualunque - Code prioritarie, in cui si cerca l’elemento con chiave massima o minima • Dizionari: operazioni tipiche L’argomentodi INPUT è sottolineato. -Search(Key, Data) operazione fondamentale -Insert(Key, Data)

-Delete(Key)

Assai meno importanti operazioni come : -Min(Key,Data)

-Max(Key,Data)

………

• Dizionari statici: ammessa solo Search (Es. dati storici) • Dizionari dinamici: ammesse anche Insert e Delete (Es.Elenco telefonico corrente) • Dizionari semi-dinamici: ammesse Search e Insert (Es. movimenti correnti c.c., magazzino, tabella dei simboli compilatore o assembler) • Dizionari: possibili realizzazioni: - Vettori - Liste - Alberi di ricerca - Hash table

Page 104: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

104

12.1– VETTORI Adatti a liste statiche di piccole dimensioni. • Vettori non ordinati - Search, Delete, Min, Max: O(n) - Insert: O(1) in coda • Vettori ordinati - Search: O(lgn) con ricerca dicotomica - Insert, Delete: O(n) (O(lgn) per posizionarsi, O(n) per far posto o compattare) - Min, Max: O(1)

12.2– LISTE CONCATENATE Sono adatte a dizionari dinamici o semi-dinamici di piccole dimensioni. Nelle realizzazioni semplici, tutte le operazioni sono tendenzialmente O(n), tranne le inserzioni in testa o in fondo. 12.3– ALBERI DI RICERCA • 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. Esempi (nei nodi sono indicate solo le chiavi) I due BST hanno le stesse chiavi

10

15

14

18 12 7

5

15

7

18

12

5

14

10

Page 105: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

105

• La visita IN-ORDER del BST fornisce le chiavi in ordine crescente • Proprietà fondamentale dei BST: tutte le operazioni principali sono O(p). Se l’albero è bilanciato (le foglie si trovano tutte al livello p o p-1), le operazioni sono O(lgn). Supponiamo chiavi distinte. - Search. Confronto la chiave k con quella della radice kr . Se coincide, la ricerca è

terminata. Se k ≤ kr , applico recursivamente la procedura al sotto-albero di sinistra, altrimenti a quello di destra. Mi arresto se: 1) trovo k ; 2) il sottoalbero è vuoto. Il numero di confronti max è p+1.

- Insert. Applico Search. Mi arresto ad un certo nodo, perché il sottoalbero corrispondente a k è vuoto. Inserisco il nuovo nodo con chiave k come figlio sinistro o destro. Il numero di confronti max è p+1. Esempi:

- Delete. Si trova l’elemento da cancellare con Search. Dopo di che l’algoritmo ha

vari casi. 1) Il nodo non ha figli. Lo si cancella (si cambia il puntatore nel nodo padre).

cancello 18 ⇒

10

15

14

18 12 7

5

10

15

14

18 12 7

5

3

3

11

11

10

14

18 12 7

5

3

10

14

12 7

5

3

Page 106: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

106

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).

cancello 14 ⇒

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:

⇒ oppure In ogni caso la cancellazione è O(p) Si può facilmente verificare che sono semplici anche altre operazioni come Min, Max, Next, Previous.

• Bilanciamento. Affinchè sia O(p)= O(lgn), è necessario che l’albero rimanga bilanciato. E’ chiaro che le operazioni di cancellazione e di inserzione potrebbero portare nel caso peggiore a p=n-1. A questo fine esistono varie tecniche: - Usare dei particolari alberi detti HBT( Height Balanced Tree), che garantiscono

operazioni O(lgn), sia pure con costanti un po’ peggiori. - Usare molte altre varianti di alberi semi-bilanciati - Non fare niente: si dimostra che, con valori equiprobabili di chiavi e intervalli tra

le chiavi, la ricerca media è ≅ 1.4 lgn. Il caso pessimo però è Θ(n)

10

14

12 7

5

3

10

12

7

5

3

10

14

12 7

5

3

7

14

12

5

3

12

14

7

5

3

Page 107: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

107

12.4– HASH TABLE E’ la tecnica di gran lunga più usata per grandi valori di n. Le tabelle hash permettono ricerche in tempo medio circa costante. • Tabelle ad accesso diretto. Si definisce tabella ad accesso diretto quella in cui la chiave di accesso è, o fornisce in tempo costante O(1), l’indirizzo in memoria dell’elemento cercato. Ad esempio, se

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 . • Hashing function: idea generale Per costruire in modo semplice (cioè in tempo costante) dalla chiave k l’indice (posizione) nella tabella senza gli inconvenienti visti prima, si rinuncia all’univocità. Indice= h(k)

h(•) : funzione detta hash, semplice ma non necessariamente univoca. Può accadere che sia: h(ki)= h(kj) per ki≠ kj Questo caso viene detto collisione.

Esempio

L’esempio che segue non è realistico, ma rivolto a mostrare i problemi da risolvere per costruire una tabella con questa tecnica. - Tabella: dizionario Italiano/Inglese di 20 parole - Hashing function h(ki) := posizione nell’alfabeto della prima lettera(a→1, b→2,..)

Page 108: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

108

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 3 cane dog 3 cane dog 4 dovere duty albero-tree 1 albero tree cavallo-horse 1 albero tree 2 2 3 cane dog 3 cane dog 4 dovere duty 4 dovere duty 5 cavallo horse gomma-gum 1 albero tree età-age 1 albero tree 2 2 3 cane dog 3 cane dog 4 dovere duty 4 dovere duty

5 cavallo horse 5 cavallo horse 6 6 età age

7 gomma gum 7 gomma gum Nel caso di collisioni, si è seguita la semplice regola di cercare la prima posizione libera. La stessa regola deve essere seguita nella ricerca. Dall’esempio si vede che:

Page 109: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

109

- 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

Nel seguito si vedranno: - varie funzioni hash - le tecniche per il trattamento delle collisioni - le tecniche per la cancellazione di elementi •••• 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:

∑=

=jkhk mkp

)(:

1)( j∀

dove p(k) è la probabilità della chiave k. Ad esempio, se la chiave fosse un numero qualunque tra 0 e 1 con densità di probabilità costante, una hashing function adatta è h(k)=km . Tuttavia, di solito non conosciamo le probabilità delle chiavi, e si usa una delle tecniche euristiche seguenti. Si suppone che le chiavi siano numeri, o stringhe alfanumeriche riducibili a numeri (ad es. sostituendo i caratteri con i codici ASCII). - 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. - Metodo di moltiplicazione w bit Si suppone m=2p . Sia 0<A<1

k

X A x2w

p bit I p bit dipendono da tutta la chiave. Per A, Knuth suggerisce 0.6180339887.

Page 110: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

110

- 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). •••• Trattamento collisioni. Metodo 1: concatenazione (chaining)

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. •••• Trattamento collisioni. Metodo 2: indirizzamento aperto (open addressing) In questo caso la tabella deve contenere tutti gli elementi del dizionario (n<m, α<1). Se vi è collisione, sono possibili varie tecniche per la scansione della tabella, vale a dire per trovare posizione successive. - Scansione lineare(linear probing) E’ la generalizzazione della semplice tecnica di esaminare l’elemento successivo usata nell’esempio all’inizio del capitolo. hr(ki)= [ h(ki)+qr ] mod m

con : r=1, 2…..; q= costante intera; q ed m primi tra loro (tutte le posizioni vengono visitate).

Page 111: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

111

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 α: numero medio di accessi = S(α) = (1-α/2)/ (1-α) α 0.1 0.25 0.5 0.75 0.9 0.95 S(α) 1.06 1.17 1.5 2.5 5.5 10.5 Si vede che, se non si supera un fattore di riempimento di ≅2/3, il numero medio di tentativi è molto piccolo (circa minore di 2). In teoria, tuttavia il caso pessimo è O(n), ma in pratica con una buona scelta della funzione hash è impossibile raggiungerlo. Un problema della scansione lineare è l’agglomerazione primaria. Se si forma una sequenza di posizioni contigue piene, ogni chiave che genera un indirizzo nell’agglomerazione si comporta come se ci fosse una collisione di chiavi. Se, nella formula della scansione si pone q=1, l’agglomerazione si forma in posizioni contigue, altrimenti in posizione a distanza q, senza cambiare la sostanza del fenomeno. Un rimedio consiste nell’usare un passo non costante. Esempio: ki ki

kg kg

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: α 0.1 0.5 0.75 0.9 S(α) 1.05 1.44 1.99 2.79 - Scansione quadratica hr(ki)= [ h(ki)+r

2 ] mod m

ki

kg

kg

ki

kg

Page 112: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

112

con : r=1, 2…..; In generale non tocca tutte le posizioni (solo (m-1)/2). Una condizione perché tocchi tutte le posizioni è che sia m=4j+3 (m primo, j intero). Se due chiavi collidono, la ricerca successiva si sovrappone. Per migliorare ulteriormente i risultati si può usare la: - Scansione quadratica a coefficienti pesati Chiavi diverse che collidono hanno percorsi di ricerca diversi: hr(ki)= [ h(ki)+q(ki)r

2 ] mod m

q(ki) è una funzione semplice della chiave. I numeri medi di accessi diventano: α 0.1 0.5 0.75 0.9 S(α) 1.05 1.38 1.83 2.55 In conclusione, per tabelle con occupazioni inferiori al 60-70% il numero di accessi medio varia poco con la tecnica di scansione. Per le ricerche senza successo, le medie degli accessi possono variare notevolmente. Il caso peggiore è quello della scansione lineare: α 0.25 0.5 0.75 0.9 0.95 S(α) 1.39 2.5 7.5 50.5 200.5 •••• 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:

Cavallo Se cancello Cane, e poi Cavallo Cane cerco Data, - Data trovo una casella vuota Data Una soluzione: un campo aggiuntivo con tre valori: libero, occupato, cancellato. In fase di ricerca, cancellato=occupato. In fase di inserzione, cancellato=libero.

Page 113: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

113

13- CODE PRIORITARIE Una coda prioritaria P è un insieme di elementi x, ciascuno costituito da una chiave e da dati associati, che ammette le seguenti operazioni. - Insert(x,P) - Find-Min(P) ritorna l’elemento con chiave minima - Delete-Min cancella l’elemento con chiave minima

N.B. La coda prioritaria potrebbe essere strutturata per ricerca e cancellazione dell’elemento con chiave massima. •••• Realizzazione code prioritarie. La struttura più adatta è l’heap (mucchio). L’heap è un albero binario in cui: 1) la chiave di ogni nodo è minore (o maggiore) di quella dei figli 2) vi sono tutti i nodi a tutti i livelli, salvo l’ultimo dove possono mancare alcuni

nodi a sinistra. N.B. In alcuni testi viene detto heap l’albero binario che soddisfa alla prima proprietà, e heap bilanciato quello che soddisfa anche alla seconda. Gli heap si possono memorizzare in un vettore per livelli. Esempi: 9 5 7 1 4 3 6 50 24 30 20 21 18 3 12 5 6 •••• Complessità della varie operazioni - Find-Min(P) (Find-Max(P)) è ovviamente O(1) - Insert(x,P) Deve essere effettuata mantenendo il bilanciamento. Si inserisce x al livello più basso nella prima posizione vuota. Lo si confronta con il padre, e se non è maggiore (minore) lo si scambia con questo. Si procede così per scambi finché la proprietà dello heap non è soddisfatta. Evidentemente Insert(x,P) è O(lgn).

9

1

7

6 3 4

5

50

20

30

3 18 21

24

12 5 6

Page 114: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

114

Esempio

Inserisco 36 - 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

In conclusione, mantenendo il bilanciamento, le operazioni sono O(1) (Find) oppure O(lgn)(Insert e Delete) 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

50

20

30

3 18 21

24

12 5 6 36

20

21

24

12 5 6

36 20

21

24

12 5 6

36

20

30

3 18 21

24

12 5

6 50 30

3 18

6

30

3

18

6

Page 115: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

115

•••• 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.

Page 116: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

116

14- CONSIDERAZIONI CONCLUSIVE Tecniche generali per la costruzione di 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 1. Un algoritmo enumerativo per ordinare una lista è il seguente: a)si generano le n! permutazioni possibili; b) si cerca quella ordinata. Ovviamente l’algoritmo è esponenziale (vedi formule di Sterling). Fortunatamente conosciamo algoritmi assai migliori !!

Esempio 2 La tecnica usata per l’algoritmo esatto(sezione 9.5) per il set covering è enumerativa, genera tutte le soluzioni , ed è (mn) Esempio 3 e problema L’algoritmo per la ricerca di un insieme indipendente massimo di nodi di un grafo (sezione 10.13) è enumerativo. E’ equivalente al problema NP completo di trovare la max-clique del grafo complementare. Tuttavia, in alcuni casi si possono fare semplificazioni notevoli nelle enumerazioni. Si consideri il problema di disporre il massimo numero di regine che non si danno scacco sulla scacchiera, modellabile come un problema di ricerca di una max-clique.

Sembrerebbe che il problema richiedesse l’esame di

8

64 possibili combinazioni

(un numero grandissimo, circa 4x109). Se però osserviamo che per ogni riga (o colonna) ci può essere solo una regina, le combinazioni possibili si riducono a 8!=40420. Se poi consideriamo le simmetrie (la scacchiera può essere avvolta su di un cilindro, o anche su di un toro circolare) i casi si riducono ulteriormente (come?) Esempio 4 e problema. Si consideri il seguente problema: data una scacchiera dieciXdieci, e 50 pezzi da domino, ciascuno dei quali copre 2 caselle, si chiede se è possibile ( in tal caso dire come) o non è possibile (in tal caso dimostrare l’impossibilità) con 49 pezzi da domino coprire tutta la scacchiera tranne due caselle agli angoli opposti. Sembra che si debbano enumerare una gran quantità di soluzioni, ma esistono metodi molto sintetici per risolvere il problema. •••• Divide and Conquer (Divide et Impera) Una tecnica che consiste in : - suddividere un problema in sottoproblemi - combinare le soluzioni dei sottoproblemi

Page 117: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

117

In generale, la convenienza della tecnica è stata discussa nella sezione 8. Conviene in generale per algoritmi che crescono più che linearmente, e per cui la ricombinazione cresca meno dell’algoritmo. Si prestano ad esecuzioni recursive. In tal caso, se ogni volta si suddivide in problemi di dimensione metà, e la ricombinazione è lineare, gli algoritmi sono O(nlgn) Esempio 1: Merge-Sort (O(nlgn)) Esempio 2: Ricerca dicotomica, ricerca su albero di ricerca (O(lgn) perché non c’è ricombinazione) •••• Algoritmi avidi (greedy) Cercano un ottimo locale. Di solito sono semplici. Possono talora dare l’ottimo globale.

Esempio 1. Algoritmo di Kruskal per il minimum spanning tree . Dà l’ottimo globale Esempio 2. Algoritmo di Dijkstra per l’albero dei cammini minimi. Dà l’ottimo globale. Esempio 3. Algoritmo approssimato per bin-packing. Ha approssimazione garantita. 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)

Page 118: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

118

L’algoritmo non funziona sempre. Se infatti i valori delle monete fossero 11, 5, 1 con K=15 si avrebbe un resto fatto da 11, 1, 1, 1 (quattro monete), ma ovviamente il numero minimo di monete è 3 (15=5+5+5) •••• Algoritmi euristici Contengono gli algoritmi greedy. Usano qualche tecnica semplice, che mediamente si rivela abbastanza efficace.

Page 119: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

119

15- ESEMPI DI ALGORITMI COMPLESSI • 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 Ogni volta che si genera un elemento della permutazione, si verifica se è connesso ai precedenti (se ce ne sono). Basta esaminare la sua lista di adiacenza . Se non è connesso, non si generano più le permutazioni figlie (Riferendosi all’esempio precedente, se dopo 1 si trova che 2 non è connesso, non si devono più generare 1234, 1235, 1236, 1245, 1246, 1256, ma si passa direttamente a 1345). Se tutti gli elementi di una combinazione sono connessi, ho finito e trovato la max-clique, o meglio il clique number. Nel caso disgraziato nessuna combinazione dell’insieme fosse buona, si passa ad esaminare l’insieme successivo dei nodi con grado (almeno) inferiore di uno. L’algoritmo sarà molto rapido se l’insieme contiene una clique, e l’euristica funziona, o anche se il numero di elementi dell’insieme è poco maggiore del clique number previsto (poche combinazioni), o infine se il backtracking funziona abbastanza bene ed elimina un bel po’ di combinazioni. Possibile miglioramento. Quando si esaminano le liste di adiacenza, è inutile esaminare quelle del grafo originale, che comprendono tutti i nodi e quindi anche

Page 120: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

120

quelli con grado inferiore. Conviene generare le liste di adiacenza di un sottografo che comprende solo i nodi dell’insieme sotto esame. • Calcolo di un lower bound per il problema ART GALLERY Il problema: data una pianta poligonale, posizionare il numero minimo di guardie (sensori rotanti o omnidirezionali) capaci di vedere, o coprire, ogni punto della pianta. - Tipo del problema: NPcompleto - Risultati noti (upper bounds):

- Se il poligono ha n lati , nel caso più sfortunato ci vogliono n/3 guardie - Se ci sono n lati ed h buchi poligonali, ci vogliono al massimo (n+h)/3

guardie

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.

Page 121: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

121

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.

Nel caso superiore, il rapporto tra interior guards e boundary guards tende a 2. Nel caso a fianco può assumere un valore qualunque.

Page 122: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

122

Come costruire un lower bound LB(P) specifico di un poligono per le boundary guards? Consideriamo la definizione di integer visibility polygon (poligono dei punti che vedono interamente un lato) e week visibility polygon (poligono dei punti che vedono almeno un punto del lato)

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

Algoritmo per il calcolo del LB(P)

1) Calcolo tutti i week visibility polygons W(li) per tutti i lati li (esistono algoritmi quadratici per farlo)

2) Associo un grafo con un nodo per ogni lato del poligono, e un lato tra due nodi se i corrispondenti week visibility polygons si intersecano.(la verifica dell’intersezione è polinomiale)

3) Il problema è ridotto a trovare il maximum independent set di nodi del grafo. Il problema è equivalente a quello della maxclique sul grafo complementare (stessi nodi, lati complementari) ( problema NP-completo). Esistono in pratica algoritmi branch-and-bound per maxclique che mediamente hanno tempi accettabili per grafi fino ad alcune centinaia di nodi.

Page 123: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

123

• Determinazione coppia di punti più vicina nel piano

Problema. Dato un insieme di punti nel piano, determinare la coppia di punti più vicina. Alla base di numerosi altri algoritmi. Se i punti fossero su una retta, si potrebbe: - ordinarli lungo la retta ( O(nlgn)) - calcolare le distanze tra un punto ed il successivo, e scegliere la minima (O(n)) In conclusione, O(nlgn). Se i punti non sono allineati, sembra che si debbano calcolare n(n-1)/2 distanze,e quindi costruire algoritmi quadratici. Si può invece (ed in vari modi ) costruire algoritmi O(nlgn)

Algoritmo Recursivo Divide and Conquer O(nlgn) http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairDQ.html Si ordinino i punti lungo x e lungo y. Si dividano i punti in due insiemi uguali S1 e S2 (a meno di un punto, se il numero è dispari)con una linea verticale l.

Si trovi quindi le coppia più vicine, a distanza d1 e d2 nei due insiemi. Sia d la distanza più piccole tra le 2.

Page 124: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

124

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). Si può invece dimostrare che: Teorema Se considero la lista dei punti inclusi nelle due fasce, e ordinata lungo y, la distanza nella lista tra due punti con distanza geometrica <d è al massimo di 15 posizioni

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.

d d l

Page 125: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

125

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). Algoritmo non recursivo

Esistono altri algoritmi O(nlgn), ad esempio quello del plane sweep mediante scan line http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html

In breve, la linea verticale si sposta da sinistra a destra (richiede un ordinamento iniziale lungo x. In ogni istante l’algoritmo ha: la minima distanza trovata d, la lista ordinata lungo y dei punti nella fascia di larghezza d. Per ogni nuovo punto incontrato

- si mantiene la lista ordinata, inserendo il punto e togliendo quelli usciti dalla fascia ( O(lgn) con albero di ricerca)

- si cerca il più vicino nella fascia. Non bisogna confrontarsi con tutti, ma solo con quelli distanti al max. 5 posizioni (tempo costante, usando l’albero di ricerca)

- si aggiorna eventualmente d Complessità O(nlgn)

Page 126: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

126

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) Estensione in spazi a d dimensioni

Vi sono algoritmi divide and conquer, o non recursivi, estensione di quelli 2D. Si dimostra che in generale la complessità è O(n(lgn)d-1) http://www.cs.ucsb.edu/~suri/cs235/ClosestPair.pdf

Page 127: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

127

INDICE DOCENTI, ORARI, DISPENSE ED ESAMI Pag. 1 1-INTRODUZIONE 2 2-ANALISI DEI PROBLEMI E DEGLI ALGORITMI 6 2,1-Correttezza 6 2.2-Lavoro fatto 7 2.2.1-Caso medio e caso pessimo 8 2.3-Spazio usato 10 2.4-Semplicità 11 2.5-Ottimalità 11 2.5.1- Caso pessimo 11 2.5.2- Caso medio 13 2.6- Ottimalità nell’uso dello spazio 13 3-CLASSIFICAZIONE ASINTOTICA 14 3.1- Notazioni O, Ω, ο, ω 15 3.2- Proprietà di O, Ω, ο, ω 17 4- RICHIAMI MATEMATICI 18 5- STRUTTURE DATI ELEMENTARI 21 5.1- Liste 21 5.2- Alberi 24 5.2.1- Alberi binari 24 5.2.2- Alberi in generale 26 ESERCIZI E PROBLEMI 27 6- ANALISI ALGORITMI DI RICERCA 30 6.1- Ricerca in liste ordinate 30 6.1.1- Algoritmo 1 30 6.1.2- Algoritmo 2- Ricerca binaria 31 6.1.3- Ottimalità della ricerca binaria 32 7- ALGORITMI DI ORDINAMENTO 34 7.1- Insertion SORT 34 7.2- Altri SORT O(n2) 36 7.3- Lower Bound per alcuni SORT 37 7.4- QUICKSORT: approccio “Divide and conquer” 38 7.5- MERGE (fusione) di liste ordinate 40 7.6- MERGE-SORT 41 7.6.1- Ottimalità del MERGE-SORT 43 7.7- Algoritmi di SORT lineari 45 7.7.1- BUCKET-SORT 45 7.7.2- RADIX-SORT 46 7.7.3- COUNTING-SORT 48 7.8- Altri SORT 48 ESERCIZI E PROBLEMI 49 8- ANALISI DELLA TECNICA “DIVIDE AND CONQUER” 51 9- PROBLEMI P, NP, NP-COMPLETI 52 9.1- Esempi di problemi 52 9.2- La classe P ( problemi di decisione) 54 9.3- La classe NP 55 9.4- La classe di problemi NP-completi 56 9.5- Come affrontare i problemi NP-completi 58

Page 128: analisi degli algoritmi22[1]

A.Laurentini-Analisi degli algoritmi

128

9.5.1- Esempi di algoritmi approssimati 58 9.5.2- Esempi di tecniche per ridurre i casi esplorati 60 10- ALGORITMI PARALLELI 62 10.1- Definizioni generali 62 10.2- Esempi di algoritmi “Shared memory” 63 10.3- Interconnection networks 65 10.4- Architettura sistolica 65 ESERCIZI E PROBLEMI 66 11- GRAFI 68 11.1- Definizioni e proprietà 68 11.2- Rappresentazione dei grafi 71 11.3- Chiusura transitiva 73 11.4- Visita di un grafo 75 11.5- Albero ricoprente minimo 78 11.6- Albero dei cammini minimi 80 11.7- Alcune definizioni 83 11.8- Grafi planari 84 11.9- Grafi Euleriani e semi-Euleriani 88 11.10- Grafi Hamiltoniani e semi- Hamiltoniani 91 11.11- Clique 92 11.12- Colorabilità 92 11.13- Dominanza 93 11. 14- Indipendenza 95 ESERCIZI E PROBLEMI 98 12- ALGORITMI DI RICERCA 102 12.1- Vettori 103 12.2- Liste concatenate 103 12.3- Alberi di ricerca 103 12.4- HASH table 106 13- CODE PRIORITARIE 112 14- CONSIDERAZIONI CONCLUSIVE 114 15- ESEMPI DI ALGORITMI COMPLESSI 118