• Non ci sono risultati.

Studieremo alcune tecniche per il progetto di algoritmi e di strutture dati:

N/A
N/A
Protected

Academic year: 2021

Condividi "Studieremo alcune tecniche per il progetto di algoritmi e di strutture dati:"

Copied!
194
0
0

Testo completo

(1)

Studieremo alcune tecniche per il progetto di algoritmi e di strutture dati:

Programmazione dinamica Algoritmi golosi

Analisi ammortizzata

Vedremo poi alcuni tipi di strutture dati importanti per le applicazioni:

B-alberi

Strutture dati per insiemi disgiunti

(2)

Esempio: taglio delle aste

Problema del taglio delle aste

E’ data un’asta metallica di lunghezza n che deve essere tagliata in pezzi di lunghezza intera (con un costo di

taglio trascurabile).

Per ogni lunghezza l = 1,…,n è dato il prezzo pl a cui si possono vendere i pezzi di quella lunghezza.

Si vuole decidere come tagliare l’asta in modo da rendere massimo il ricavo della vendita dei pezzi ottenuti.

Programmazione Dinamica

(3)

Esempio

l 1 2 3 4 5 6 7 8 9 10

pl 1 5 8 9 10 17 17 20 24 30

Lunghezza asta n Ricavo massimo rn Suddivisione ottima

1 1 1

2 5 2

3 8 3

4 10 2+2

5 13 2+3

6 17 6

7 18 1+6 o 2+2+3

8 22 2+6

9 25 3+6

10 30 10

(4)

Un’asta di lunghezza n può essere tagliata in 2n-1 modi distinti in quanto abbiamo una opzione tra tagliare o non tagliare in ogni posizione intera 1,

…,n-1.

Ad esempio per n = 4 abbiamo i seguenti 8 modi Suddivisioni

4 1+1+2

1+3 1+2+1

2+2 2+1+1

3+1 1+1+1+1

(5)

In generale il ricavo massimo rn o è il costo pn dell’asta intera oppure si ottiene effettuando un primo taglio in posizione i e quindi sommando i

ricavi massimi del primo e del secondo pezzo, ossia rn= ri + rn-i

Quindi

Osserviamo che la soluzione ottima del problema di ottiene da soluzioni ottime di sottoproblemi.

Diciamo che il problema ha sottostruttura ottima.

, ,..., ) se 1

, max(

1

se

1 1

2 2

1 1

1

n r

r r

r r

r p

n r p

n n

n n

n

(6)

Otteniamo una struttura ricorsiva più semplice se invece di scegliere la posizione i di un primo taglio intermedio scegliamo la lunghezza i del primo

pezzo per cui rn= pi + rn-i

Cut-Rod(p, n) if n == 0

return 0 q = -1

for i =1 to n

q = max(q, p[i]+Cut-Rod(p, n-i)) return q



( ) se 0

max

0

se 0

1 p r n

r n

i n n i

i n

n n j

j T n

T( ) 1 ( ) 2

1 0

(7)

Albero di ricorsione per n = 4

0

0 0

1

0

0 1

2

3

4

0 0

1

2 1 0

Lo stesso problema di dimensione 2 viene risolto due volte, quello di dimensione 1 quattro volte e quello di dimensione 0 otto volte.

Questo spiega la complessità 2n. Ripetizione dei sottoproblemi!!

(8)

Possiamo ridurre la complessità evitando di risolvere più volte gli stessi problemi.

Un primo modo per farlo è dotare l’algoritmo di un blocco note in cui ricordare le soluzioni dei

problemi già risolti: metodo top-down con annotazione.

Un secondo modo per farlo è calcolare prima i

problemi più piccoli memorizzandone le soluzioni e poi usare tali soluzioni per risolvere i problemi più grandi: metodo bottom-up.

(9)

Versione top-down con annotazione:

Memoized-Cut-Rod(p, n)

for i = 0 to n // inizializza il blocco note r[i] = -1

return Cut-Rod-Aux(p, n, r) Cut-Rod-Aux(p, j, r)

if r[j] ≥ 0 // il problema è già stato risolto return r[j]

if j == 0 q = 0 else q = -1

for i = 1 to j

q = max(q, p[i] + Cut-Rod-Aux(p, j-i, r)) r[j] = q

return q

) (

)

(n n2

T

(10)

Versione bottom-up:

Bottom-Up-Cut-Rod(p, n)

r[0] = 0 // il problema più semplice for j = 1 to n

q = -1

for i = 1 to j

q = max(q, p[i] + r[j-i]) r[ j] = q

return r[n]

T ( n )   ( n

2

)

(11)

Versione bottom-up estesa per calcolare la soluzione ottima e non solo il suo valore

Extended-Bottom-Up-Cut-Rod( p, n) r[0] = 0

for j = 1 to n q = -1

for i = 1 to j

if q < p[i]+r[ j - i]

q = p[i]+r[ j - i]

s[ j] = i // memorizzo il taglio ottimo r[ j] = q

return r ed s

(12)

La seguente procedura calcola e stampa la soluzione ottima:

Print-Cut-Rod-Solution( p, n)

(r, s) = Extended-Bottom-Up-Cut-Rod( p, n) j = n

while j > 0 print s[j]

j = j - s[j]

(13)

Moltiplicazione di matrici

Esso richiede pqr prodotti scalari

L’algoritmo per moltiplicare due matrici A e B di dimensioni pq e qr è:

Matrix-Multiply(A, B) for i = 1 to A.rows

for j = 1 to B.columns C[i, j] = 0

for k = 1 to A.columns

C[i, j] = C[i, j] + A[i, k] B[k, j]

return C

(14)

Problema della moltiplicazione di matrici Si deve calcolare il prodotto

A

1

A

2

... A

n

di n matrici di dimensioni

p

0

p

1

, p

1

p

2

, ... , p

n-1

p

n

Poiché il prodotto di matrici è associativo

possiamo calcolarlo in molti modi.

(15)

Esempio:

Per calcolare il prodotto A1 A2 A3 di 3 matrici di dimensioni 2005, 5100, 1005 possiamo:

a) moltiplicare A1 per A2 (100000 prodotti scalari) e poi moltiplicare per A3 la matrice 200100

ottenuta (100000 prodotti scalari).

In totale 200000 prodotti scalari.

b) moltiplicare A2 per A3 (2500 prodotti scalari) e poi moltiplicare A1 per la matrice 55 ottenuta

(5000 prodotti scalari).

In totale 7500 prodotti scalari.

(16)

Vogliamo trovare il modo per minimizzare il numero totale di prodotti scalari.

In quanti modi possiamo calcolare il prodotto?

Tanti quante sono le parentesizzazioni possibili del prodotto A1 A2 ... An .

Ad esempio per n = 4:

(A1 (A2 (A3 A4))) (A1 ((A2 A3) A4)) ((A1 A2) (A3 A4)) ((A1 (A2 A3)) A4) (((A1 A2) A3) A4)

(17)

Il numero P(n) di parentesizzazioni possibili del prodotto A1 A2 ... An di n matrici si esprime

ricorsivamente come segue:

Si può dimostrare che P(n) cresce in modo esponenziale.

Quindi, tranne per valori di n molto piccoli, non è possibile enumerare tutte le parentesizzazioni.





1 se

) (

) (

1 se

1 )

( 1

1

n k

n P k

P

n n

P n

k

(18)

Passo 1: struttura di una parentesizzazione ottima Supponiamo che una parentesizzazione ottima di A1 A2 ... An preveda come ultima operazione il

prodotto tra la matrice A1..k (prodotto delle prime k matrici A1 ... Ak) e la matrice Ak+1..n (prodotto delle ultime n-k matrici Ak+1 ... An).

Le parentesizzazioni di A1 ... Ak e di Ak+1 ... An sono parentesizzazioni ottime per il calcolo di A1..k e di Ak+1..n .

Perché?

(19)

Passo 2: soluzione ricorsiva

Di conseguenza la matrice prodotto parziale Ai..j è una matrice pi-1pj con lo stesso numero pi-1 di

righe della prima matrice Ai e lo stesso numero pj di colonne dell’ultima matrice Aj .

Prendiamo come sottoproblemi il calcolo dei prodotti parziali Ai..j delle matrici Ai ... Aj .

Ricordiamo che la generica matrice Ai ha dimensioni pi-1pi .

(20)

Se i = j allora Ai..j = Ai ed m[i,i] = 0.

Se i < j allora Ai..j = Ai ... Aj si può calcolare come prodotto delle due matrici Ai..k e Ak+1..j con k

compreso tra i e j-1.

Il costo di questo prodotto è pi-1 pk pj . Quindi





 

m i k m k j p p p i j

j j i

i m

j k j i

k

i ( [ , ] [ 1, ] ) se

min

se ] 0

, [

1

(21)

Passo 3 Esempio

A1 3035 A2 3515 A3 155 A4 510 A5 1020 A6 2025

i

1 2 3 4 5 6

1 2 3 4 5 6

A1..1 0

A2..2 0

A3..3 0

A4..4 0

A5..5 0

A6..6 0

j

30 35 15 5 10 20

p

35 15 5 10 20 25 p

m k

m k

m k m

k m

k

m k A1..2

15750

1 A2..3 2625

2 A3..4 750

3 A4..5 1000

4 A5..6 5000

5 A1..3

7900

1 A2..4 4375

3 A3..5 1500

4 A4..6 3500

5 A1..4

9400

3 A2..5 7125

3 A3..6 5375

3 A1..5

11900

3 A2..6 10500

3 A1..6 15125

3

A1..1A2..2: 303515 = 15750 A2..2A3..3: 35155 = 2625

A1..1A2..3: 0+2650+30355 = 7900 A1..2A3..3: 15750+0+30155 = 18000 A3..3A4..4: 15510 = 750

A2..2A3..4: 0+750+351510 = 6000 A2..3A4..4: 2625+0+35510 = 4375 A1..1A2..4: 0+4375+303510 = 14875 A1..2A3..4: 15750+750+301510 = 21000 A1..3A4..4: 7900+0+30510 = 9400

A4..4A5..5: 0+0+51020 = 1000 A3..3A4..5: 0+1000+15520 = 2500 A3..4A5..5: 750+0+15105 = 1500 A2..2A3..5: 0+1500+351520 = 12000 A2..3A4..5: 2625+1000+35520 = 7125 A2..4A5..5: 4375+0+351020 = 11375 A1..1A2..5: 0+7125+303520 = 28125

A1..2A3..5: 15750+1500+301520 = 26250 A1..3A4..5: 7900+1000+30520 = 11900 A1..4A5..5: 9400+0+301020 = 15400 A5..5A6..6: 0+0+102025 = 5000

A4..4A5..6: 0+5000+51025 = 6250 A4..5A6..6: 1000+0+52025 = 3500 A3..3A4..6: 0+3500+15525 = 5375 A3..4A5..6: 750+5000+151025 = 9500 A3..5A6..6: 1500+0+152025 = 9000 A2..2A3..6: 0+5375+351525 = 18500 A2..3A4..6: 2625+3500+35525 = 10500 A2..4A5..6: 750+5000+351025 = 14500 A2..5A6..6: 1500+0+352025 = 19000 A1..1A2..6: 0+10500+303525 = 36750 A1..2A3..6: 15750+5375+301525 = 32375 A1..3A4..6: 7900+3500+30525 = 15150 A1..4A5..6: 9400+5000+301025 = 21900 A1..5A6..6: 11900+0+302025 = 26900

(22)

Matrix-Chain-Order(p, n) for i = 1 to n

m[i, i] = 0 for j = 2 to n

for i = j-1 downto 1 m[i, j] = 

for k = i to j-1

q = m[i, k] + m[k+1, j] + pi-1 pk pj if q < m[i, j]

m[i, j] = q s[i, j] = k return m, s

Passo 3: calcolo del costo minimo

Complessità: O(n3)

(23)

i

1 2 3 4 5 6

1 2 3 4 5 6

A1..1

A2..2

A3..3

A4..4

A5..5

A6..6

j

m s

m s

m s m

s m

s

m s

(A1..3 A4..6) A1..6

((A1 A2..3) (A4..5 A6))

((A1 (A2 A3 )) ((A4 A5) A6))

A1..2

1 A2..3

2 A3..4

3 A4..5

4 A5..6

5 A1..3

1 A2..4

3 A3..5

3 A4..6

5 A1..4

3 A2..5

3 A3..6

3 A1..5

3 A2..6

3 A1..6

3

Passo 4 Esempio

A1 3035 A2 3515 A3 155 A4 510 A5 1020 A6 2025

(24)

Print-Optimal-Parens(s, i, j) if i == j

print “Ai else

k = s[i, j]

print “(”

Print-Optimal-Parens(s, i, k) print “”

Print-Optimal-Parens(s, k+1, j) print “)”

Passo 4:

stampa della soluzione ottima

Complessità: O(n)

(25)

Matrix-Chain-Multiply(A1...An , i, j, s) if i == j

return Ai else

k = s[i, j]

A = Matrix-Chain-Multiply(A1...An, i, k, s) B = Matrix-Chain-Multiply(A1...An, k+1, j, s) return Matrix-Multiply(A, B)

Calcolo del prodotto di una sequenza di matrici

(26)

Si potrebbe anche usare direttamente la definizione ricorsiva del costo minimo per il prodotto di

matrici

per calcolarlo ricorsivamente senza usare le matrici m ed s.



m i k m k j p p p i j

j j i

i m

j k j i

k

i ( [ , ] [ 1, ] ) se

min

se ] 0

, [

1

(27)

Rec-Matrix-Chain-Cost(p, i, j) if i = j

return 0 else

cmin = 

for k = i to j-1

q = Rec-Matrix-Chain-Cost(p, i, k) +

Rec-Matrix-Chain-Cost(p, k+1, j) + pi-1 pk pj

if q < cmin cmin = q

return cmin Complessità T(n) con n = j-i+1

 



1 se

) (

) (

1 se

)

( 1

1

n b

h n

T h

T a

n a

n

T n

h

(28)

Per sostituzione si può dimostrare che

dove c = min(a,b).

Quindi T(n) = Ω(2n).

 



1 se

) (

) (

1 se

)

( 1

1

n b

h n

T h

T a

n a

n

T n

h

2 1

)

(nc n T

(29)

1,4

2,2

1,1 2,4

3,4 2,3

1,2 1,3

3,3 4,4 2,2 3,3

4,4 1,1 2,2

3,4

1,1 3,3

4,4

Causa della complessità esponenziale:

La complessità diventa esponenziale perché vengono risolti più volte gli stessi sottoproblemi.

Rec-Matrix-Chain-Cost(p,1,4)

k = 1 k = 2 k = 3

k = 2 k = 3 k = 1

3,3 4,4

k = 3 k = 1 k = 2

2,2 3,3 2,3 k = 2

1,1 2,2 1,2

k = 1 k = 3 k = 2

(30)

Possiamo evitare il ricalcolo dei costi minimi dei sottoproblemi dotando la procedura ricorsiva di un blocco notes (una matrice m di dimensione nn) in cui annotare i costi minimi dei sottoproblemi già risolti.

Memoized-Matrix-Chain-Order(p, n) for i = 1 to n

for j = i to n do m[i, j] = 

return Memoized-Chain-Cost(p,1, n, m)

(31)

Memoized-Chain-Cost(p, i, j, m) if m[i, j] = 

if i == j

m[i, j] = 0 else

for k = i to j-1

q = Memoized-Chain-Cost(p, i, k, M)

+ Memoized-Chain-Cost(p, k+1, j, M) + pi-1 pk pj

if q < m[i, j]

m[i, j] = q return m[i, j]

Complessità: O(n3)

(32)

Problemi risolvibili con la programmazione dinamica

Abbiamo usato la programmazione dinamica per risolvere due problemi.

Cerchiamo ora di capire quali problemi si possono risolvere con questa tecnica.

Sono dei problemi di ottimizzazione in cui da un insieme (generalmente molto grande) di soluzioni possibili vogliamo estrarre una soluzione ottima rispetto ad una determinata misura.

(33)

a) una soluzione ottima si possa costruire a partire da soluzioni ottime di sottoproblemi:

Proprietà di sottostruttura ottima .

Per poter applicare vantaggiosamente la programmazione dinamica bisogna che:

(34)

b) che il numero di sottoproblemi distinti sia molto minore del numero di soluzioni possibili tra cui

cercare quella ottima.

Altrimenti una enumerazione di tutte le soluzioni può risultare più conveniente.

Se ciò è vero significa che uno stesso problema

deve comparire molte volte come sottoproblema di altri sottoproblemi.

Proprietà della ripetizione dei sottoproblemi .

(35)

Supposto che le condizioni a) e b) siano verificate, occorre scegliere l’ordine in cui calcolare le

soluzioni dei sottoproblemi.

Tale ordine ci deve assicurare che nel momento in cui si risolve un sottoproblema le soluzioni dei

sottoproblemi da cui esso dipende siano già state calcolate.

Ordine bottom-up.

(36)

Alternativamente si può usare una procedura ricorsiva top-down che esprima direttamente la soluzione di un sottoproblema in termini delle soluzioni dei sottoproblemi da cui essa dipende.

In questo caso occorre però memorizzare le

soluzioni trovate in modo che esse non vengano ricalcolate più volte.

(37)

Confronto tra algoritmo iterativo bottom-up ed algoritmo ricorsivo top-down con memoria:

Se per il calcolo della soluzione globale servono le soluzioni di tutti i sottoproblemi l’algoritmo

bottom-up è migliore di quello top-down.

Entrambi gli algoritmi calcolano una sola volta le soluzioni dei sottoproblemi ma il secondo è

ricorsivo ed inoltre effettua un controllo in più.

(38)

Se per il calcolo della soluzione globale servono soltanto alcune delle soluzioni dei sottoproblemi l’algoritmo bottom-up le calcola comunque tutte mentre quello top-down calcola soltanto quelle che servono effettivamente.

In questo caso l’algoritmo top-down può risultare migliore di quello bottom-up.

Il prossimo problema è un esempio di questa situazione.

(39)

Massima sottosequenza comune

In questo problema sono date due sequenze X = x1x2...xm e Y = y1y2...yn

e si chiede di trovare la più lunga sequenza Z = z1z2...zk

che è sottosequenza sia di X che di Y

Ricordiamo che una sottosequenza di una sequenza X è una qualsiasi sequenza ottenuta da X

cancellando alcuni elementi.

(40)

Il problema della massima sottosequenza ha molte applicazioni.

Per citarne solo alcune:

- individuare le parti comuni di due versioni dello stesso file (sequenze di caratteri ASCII).

- valutare la similitudine tra due segmenti di DNA (sequenze di simboli A,C,G,T).

(41)

Passo 1: Struttura di una massima sottosequenza comune (LCS)

Sia Z = z1...zk una LCS di

X = x1...xm e Y = y1...yn

La sottostruttura ottima di Z discende dalle seguenti proprietà:

1. se xm = yn allora zk = xm = yn e Zk-1 è una LCS di Xm-1 e Yn-1

2. altrimenti se zk ≠ xm allora Z è LCS di Xm-1 e Y 3. altrimenti zk ≠ yn e Z è una LCS di X e Yn-1

(42)

Dimostrazione:

1. Supponiamo xm = yn

Se zk ≠ xm = yn potremmo aggiungere il simbolo xm = yn in coda a Z ottenendo una sottosequenza comune più lunga contro l’ipotesi che Z sia una LCS.

Quindi zk = xm = yn e Zk-1 è sottosequenza comune di Xm-1 e Yn-1 .

2. Se zk ≠ xm allora Z è sottosequenza di Xm-1 e Y Essendo Z una LCS di X e Y essa è anche una

LCS di Xm-1 e Y.

3. il caso zk ≠ yn è simmetrico.

(43)

Data una sequenza

X = x1x2...xm indicheremo con

Xi = x1x2...xi il prefisso di X di lunghezza i.

L’insieme dei sottoproblemi è costituito quindi dalla ricerca delle LCS di tutte le coppie di

prefissi (Xi ,Yj ), per i = 0,…,m e j = 0,…,n.

Totale (m+1)(n+1) = Θ(mn) sottoproblemi.

(44)

Passo 2: soluzione ricorsiva

Siano X = x1...xm e Y = y1...yn le due sequenze di cui vogliamo calcolare una LCS e per i = 0,1,...,m e j = 0,1,...,n sia ci,j la lunghezza di una LCS dei due prefissi Xi e Yj .

Usando le proprietà che abbiamo appena dimostrato possiamo scrivere:

 

 

j i

j i

j i

j i

j i

j i

y x

j i c

c

y x

j i c

j i

c

e 0 se

e 0 se

1

0 o

0 se

0

1 1

1 1

, )

, max(

,

, ,

, ,

(45)

Passo 3 Esempio

X=ABCBDAB Y=BDCABA

i

0 2 3 4 5 6

0 1 2 3 4 5 6 7

0

j C

D B

c s

1

B

A A

0

0 0 0 0 0 0

0 0 0 0 0 0

A B C B D A B

c s c s c s c s c s c s c s

Y X

0

1

1

1

1

1

1

0

1

1

1

2

2

2

0

1

2

2

2

2

2

1

1

2

2

2

3

3

1

2

2

3

3

3

4

1

2

2

3

3

4

4

(46)

Terzo passo: lunghezza di una LCS

LCS-Length(X, Y, m, n) for i = 0 to m

c[i, 0] = 0 for j = 1 to n c[0, j] = 0 for j = 1 to n

for i = 1 to m if xi == yj

c[i, j] = c[i-1, j-1]+1, s[i, j] = “”

elseif c[i-1, j] ≥ c[i, j-1]

c[i, j] = c[i-1, j], s[i, j] = “”

else c[i, j] = c[i, j-1], s[i, j] = “”

return c,s

(47)

Quarto passo Esempio

X=ABCBDAB Y=BDCABA

i

0 2 3 4 5 6

0 1 2 3 4 5 6 7

0

j C

D B

c s

1

B

A A

0

0 0 0 0 0 0

0 0 0 0 0 0

A B C B D A B

c s c s c s c s c s c s c s

Y X

0

1

1

1

1

1

1

0

1

1

1

2

2

2

0

1

2

2

2

2

2

1

1

2

2

2

3

3

1

2

2

3

3

3

4

1

2

2

3

3

4

4

4

4

LCS=....

LCS=...A

3

3

LCS=..BA

2

2

LCS=.CBA

1

1

LCS=BCBA

c 0 s

(48)

Quarto passo: Stampa della LCS

Print-LCS(X, s, i, j) if i > 0 and j > 0 if s[i, j] == “”

Print-LCS(X, s, i-1, j-1) print X[i]

elseif s[i, j] == “”

Print-LCS(X, s, i-1, j) else Print-LCS(X, s, i, j-1)

(49)

0

1

0 0

0 0

0

0

1

1

1

1

2

2

3

0 1

1

1

1

2

2

2

2

2

3

4

3

4

4

Metodo top-down Esempio

X=ABCBDAB Y=BDCABA

i

0 2 3 4 5 6

0 1 2 3 4 5 6 7

j C

D B

1

B

A A

A B C B D A B

Y X

c s

c s

c s c s c s c s c s

c s

(50)

Triangolazione ottima

Una triangolazione di un poligono convesso è una suddivisione del poligono in triangoli ottenuta

tracciando delle diagonali che non si intersecano . Vi sono più triangolazioni possibili dello

stesso poligono

(51)

In questo problema sono dati i vertici q1,q2,…,qn di un poligono convesso P presi in ordine antiorario.

Ad ogni triangolo T è attribuito un costo c(T).

Ad esempio c(T) potrebbe essere la lunghezza del perimetro, la somma delle altezze, il prodotto delle lunghezze dei lati, (l’area ?), ecc.

Si vuole trovare una triangolazione del poligono P tale che la somma dei costi dei triangoli sia

minima.

(52)

In quanti modi possiamo suddividere in triangoli un poligono convesso di n vertici?

Ogni lato del poligono P appartiene ad un solo triangolo della triangolazione .

q1 qn

qk T

Siano q1qkqn i vertici del triangolo T a cui appartiene il lato q1qn

(53)

Il vertice qk può essere scelto in n-2 modi diversi e i due poligoni hanno rispettivamente n1 = k ed n2 = n-k+1 vertici.

q1 qn

qk P1

P2 T

Il triangolo T suddivide il poligono P nel triangolo T stesso e nei due poligoni P1 e P2 di vertici q1,

…,qk e qk,…,qn

P1 quando k = 2 e P2 quando k = n-1 sono poligoni degeneri, ossia sono un segmento.

Riferimenti

Documenti correlati

• Problemi polinomiali non deterministici = problemi risolvibili in tempo polinomiale da un algoritmo non deterministico, ma per i quali non è ancora stata trovata una.

a) un puntatore T.table alla memoria allocata. b) due campi interi T.num e T.size, rispettivamente il numero di elementi presenti nella tavola e la dimensione della

Scrivere la funzione ricorsiva printGreaterKeys(u, a) che, data la radice u di un BST, un valore a di chiave, stampa in ordine tutte le chiavi dell’albero maggiori della chiave

Si progettino un algoritmo euristico di tipo greedy ed uno di ricerca locale per risolvere il problema della BISEZIONE in forma di ottimizzazione: “Dato un grafo non orientato G =

Si scriva una procedura Pascal, basata sulla ricerca binaria, per individuare in tempo O(log n) l’unico intero dell’intervallo 1..n+1 che non compare in V.... Si scriva

L Insertion Sort è uno algoritmo di ordinamento molto efficiente per ordinare un piccolo numero di elementi. L idea di ordinamento è quella che potrebbe usare un persona

L’array A[p…r] viene “suddiviso” in due sotto- array “non vuoti” A[p… q] e A[q+1…r] in cui ogni elemento di A[p…q] è minore o uguale ad ogni elemento

Lo scopo del gioco è quello di trasferire i dischi dal paletto di sinistra a quello di destra, senza mai mettere un disco su un altro di dimensione più piccola4. Regole