FAQ
(questa pagina e' predisposta per raccogliere alcune domande
-frequently asked questions-
e risposte sul corso di Algoritmi)
 
 

D. Ho provato a fare gli esercizi di preparazione del secondo compitino
    (http://dna.bio.disco.unimib.it/~bonizzoni/modulo1.html) .
    I dubbi sono i seguenti:
    Es1. Come si costruisce la ricorrenza ?
    Es2 punto (b) Come faccio a spiegare che pur non essendo un matroide, greedy
        fornisce l'ottimo?
 

R. 1.  Per costruire la ricorrenza occorre innanzitutto riflettere (!). Provi a ragionare. L'insieme in questione
e' in realta' una mpla di elementi e ad ogni passo devo decidere se prendere un elemento oppure no.
Piu' precisamente, esiste un sottoinsieme di elementi tra i primi i elementi di costo totale k se e solo se
(1) prendo l'elemento in posizione i,  ma allora deve esistere un sottoinsieme di elementi tra i primi i-1 di costo
totale k - c(e_i), ossia di costo uguale a k meno il costo dell'elemento in posizione i (ma questo se il costo
dell'elemento in posizione i ha valore minore o uguale al costo k);
    oppure
(2) non prendo l'elemento i, ma allora deve esistere un sottoinsieme di elementi tra i primi i-1 di costo
totale k.
La ricorrenza, che ha valori booleani,  puo' essere descritta con

"S(j,t)=1 se esiste un sottoinsieme di elementi da e_1 a e_j di costo totale t"
(altrimenti  S(j,t)=0). Il valore viene ricorsivamente calcolato cosi':

    S(j,t)=S(j-1,t) OR  [S(j-1,t-c(e_j)) AND (c(e_j) <= t)]

La soluzione viene calcolata con S(i,k) e le condizioni alla base sono: S(1,t)=1 se c(e_1)=t, altrimenti
S(1,t)=0. Infine S(j,0)=1(esiste sempre un sottoinsieme dei primi j elementi - quello vuoto - con costo zero).

D (continua). Si segue un po' il ragionamento delle A-liste (elemento preso ed elemento non preso)?
R. Esatto!

R 1.(continua) Passiamo all'esercizio sui matroidi. Innanzitutto, deve convincersi che lo schema greedy
dia l'ottimo per quella funzione peso (provi!). La questione e' che il fatto che ESISTA UNA funzione peso
per cui l'algoritmo greedy dia l'ottimo su un sistema che non e' un matroide, non contraddice, cioe' non nega,
ossia non trova sbagliato, il Teorema di Rado. Infatti questo teorema afferma che se PER OGNI
funzione peso l'algoritmo da' l'ottimo allora ho un matroide (e viceversa, cioe' se ho un sistema che non e' un
matroide, allora non per tutte le funzioni peso ho l'ottimo). Questo vuol dire che posso trovare anche 100000
funzioni peso rispetto alle quali lo schema greedy restituisce l'ottimo, ma questo non dimostra che per ogni
funzione peso cio' sia vero, chiaramente. Dunque l'aver trovato UNA funzione peso rispetto alla quale l'algoritmo trova l'ottimo non e' un problema, rispetto al teorema di Rado. Non e' infatti difficile trovare sia altre funzioni
peso rispetto alle quali l'algoritmo greedy continui a fornire l'ottimo, sia funzioni peso rispetto alle quali
l'algoritmo non da' l'ottimo (confermando il Teorema di Rado: se un sistema non e' un matroide, allora esiste una funzione peso rispetto alla quale l'algoritmo non da' l'ottimo).
 

D2.  Ho notato alcune incongruenze negli esercizi in web di programmazione dinamica
 (http://dna.bio.disco.unimib.it/~bonizzoni/modulo1.html). ESERCIZIO 10:
> Mi sembra che l'equazione di ricorrenza non consideri il caso in cui k,
> vertice intermedio, appartenga sia al cammino da i a j sia
> all'insieme E1.
> io aggiungerei  la seguente riga nell'equazione
> min { D(i,k,k-1,t1) + D(i,k,k-1,t2) } con t1+t2-1<=t , se k appartiene
> al cammino da i a j e se k appartiene ad E1
 

R2.  ma k e' un vertice (dunque non puo' appartenere a E_1 che e' un insieme di archi)!!!!
 

D2 (continua).  Inoltre mi sembra che nella base vi sia un errore.  Penso che dovrebbe essere
> Caso in cui k=0 e t=0:
>                               D(i,j,0,0) = infinito, se (i,j)  appartiene ad E1
>                               D(i,j,0,0) = w(i,j), altrimenti
> Nel testo è scritto esattamente il contrario!

R2 (continua).  su questo ha ragione, bravo! La versione corrente della dispensa e' stata corretta.
 

D3. Sempre sugli esercizi della dispensa. ESERCIZIO 12:
> Quando considera il caso in cui l, vertice intermedio,  è rosso dice che
> il minimo va fatto su k1 + k2 = k-1 .
> Anche guardandolo più volte penso che dovrebbe essere k1 + k2 -1 = k.
>

R3.  ha ragione anche qui!!! La versione corrente e' stata aggiornata pure in questo punto.
 

D3 (continua). Sempre sugli esercizi della dispensa. ESERCIZIO 9:
> La seconda ricorrenza, per M((u,v),0) non dovrebbe avere come sottoproblemi
M((v, left(v)),0)+ M((v, right(v)),1) ?

R3(continua). Uffa, avete trovato anche questo!
E' vero, ma qui era proprio un errore di stampa!!! Da correggere!
 

D4. Ancora sugli esercizi della dispensa. ESERCIZIO 2: perche' non si puo' adattare semplicemente
l'equazione della LCS?

R4. E come si fa? Il problema e' che occorre SAPERE qual e' effettivamente l'ultimo elemento preso,
altrimenti non si puo' cotrollare la crescenza dell'output!!! In altre parole, l'equazione della ricorrenza
usa degli indici che NON rappresentano via via quelli che effettivamente sono presi nella LCS. Se per la LCS
questo non e' un problema, per la LCS crescente lo e'. Non c'e' altro modo per controllare la crescenza
dei valori in comune a due sequenze se non quello di "controllarlo" (!) considerandolo  un parametro da cui
dipende la ricorrenza: dunque occorre specificare che l'indice della ricorrenza corrisponde effettivamente
ad un simbolo che e' stato inserito, cioe' che e' comune.

D5. Ma allora e' lo stesso che per la sequenza ZIG-ZAG (esercizio 3)?
R5. Esatto!

D6. Nell'esercizio 7 (cammini alternati) perche' devo minimizzare su j e non considerare
solo j-1?

R6. Attenzione!!! E dove sta scritto che il precedente del vertice j e' il vertice j-1???????????
Bisogna capire che quello che di solito si fa (perche' usato in Floyd-Warshall) e' di numerare i vertici.
Ma questo non significa che il predecessore del vertice j e' il vertice j-1 e il successore e' j+1!
Difatti NON si sa quale e' il predecessore, anzi, quali sono i predecessori (analogamente per i successori).
Per questo se il cammino e' dal vertice 1 al vertice i, i vertici predecessori di i hanno indice da 1 a i-1.
Piu' semplicemente si puo' scrivere j appartenente all'insieme dei predecessori di i.
 

D7. Come avviene l'ordinamento con il counting sort? Come sono utilizzati
i vettori di appoggio?

R7. Supponiamo di avere in input l'array A[1 ... n] di interi, che sappiamo
essere nel range di possibili valori da 1 a k (minimo valore 1, massimo
valore k). Dobbiamo ordinarli. Usiamo due vettori: B[1 ... n]
conterra' alla fine dell'esecuzione l'array A ordinato, mentre
C[ 1 ..k] e' un array di supporto (ha k posizioni).

Procedo con un esempio di pari passo con l'algoritmo

(ATTENZIONE: se all'esame e' chiesto questo algoritmo, non spiegarlo attraverso
l'esempio, ma ragiona sull'algoritmo in generale. Io faccio cosi' perche'  mi e'
stato chiesto...)

Sia A[1...n]=3,2,3,5,4,3,2 (ossia 7 elementi tra 1 e 5, dunque k=5 e n=7).
Inizialmente C=0,0,0,0,0 (inizializzato).
Poi si scorre A (il ciclo e' fino ad n), e poi, per ogni posizione j di A,
si vede il valore A[j] e si conserva tale valore in C nella posizione A[j]
(che sappiamo essere al piu' k). Percio',
eseguendo l'algoritmo sull'esempio, si ha:

A[1]=3, allora  in C[3] metto 0+1=1. C diventa 0,0,1,0,0
A[2]=2, allora  in C[2] metto 0+1=1. C diventa 0,1,1,0,0
A[3]=3, allora  in C[3] metto 1+1=2. C diventa 0,1,2,0,0
A[4]=5, allora  in C[5] metto 0+1=1. C diventa 0,1,2,0,1
A[5]=4, allora  in C[4] metto 0+1=1. C diventa 0,1,2,1,1
A[6]=3, allora  in C[3] metto 2+1=3. C diventa 0,1,3,1,1
A[7]=2, allora  in C[2] metto 1+1=2. C diventa 0,2,3,1,1

Percio' alla fine di questa iterazione abbiamo che ogni posizione di C
contiene effettivamente il numero degli elementi che in A hanno valore
quella posizione ( 0 elementi di valore 1, 2 elementi di valore 2,
3 elementi di valore 3, 1 elemento di valore 4 e 1 di valore 5).

La seconda iterazione modifica C e fa in modo che ogni
posizione contenga il numero di elementi che in A hanno valore
minore o uguale a quella posizione (ossia 0 elementi minori
o uguali a 1, 2 elementi minori o uguali a 2, 5 elementi minori
o uguali a 3, 6 elementi minori o uguali a 4 e, infine, 7 elementi
minori o uguali a 5). Per far questo basta scorrere di nuovo C e sommare
al numero contenuto in una posizione di C quello contenuto nella
precedente.

Percio' alla fine del for si ha C=0,2,5,6,7.

Perche' si fa questo????? Si capisce dall'ultimo ciclo che
traduce in codice il seguente ragionamento (provo a scriverlo!).
Scandiamo A dalla fine (cosi' manteniamo la stabilita' dell'algoritmo).
Al passo generico, prendiamo l'elemento A[j].
Dove "mettiamo" questo elemento in B?
Cioe' in quale posizione di B?. Analizziamo C, nella posizione A[j]:
sappiamo che
C[A[j]] contiene il numero di elementi che in A hanno valore minore
o uguale ad A[j].....ma tali elementi che in A hanno valore minore
o uguale ad A[j] dove si troveranno in B? Cioe' in quali posizioni
staranno???? Ma staranno proprio prima di A[j]! Allora in B avro'
C[A[j]] -1 posizioni occupate e poi posso mettere A[j]!!!!!!
Percio' A[j] viene messo nella posizione B[C[A[j]]]. Poi
decremento C[A[j]] di 1, perche' ho inserito una occorrenza
di A[j] in B.

Continuiamo con l'esempio:
A[7]=2, C[2]=2 (ci sono 2 elementi minori o = a 2), metto 2=A[7] in B[2].
              Poi C[2]=1 (ossia C=0,1,5,6,7).
A[6]=3, C[3]=5 (ci sono 5 elementi minori o = a 3), metto 3=A[6] in B[5].
              Poi C[3]=4 (ossia C=0,1,4,6,7).
A[5]=4, C[4]=6  (ci sono 6 elementi minori o = a 4), metto 4=A[5] in B[6].
              Poi C[4]=5 (ossia C=0,1,4,5,7).
A[4]=5, C[5]=7  (ci sono 7 elementi minori o = a 5), metto 5=A[4] in B[7].
              Poi C[5]=6 (ossia C=0,1,4,5,6).
A[3]=3, C[3]=4  (ci sono 4 elementi minori o = a 3), metto 3=A[3] in B[4].
              Poi C[3]=3 (ossia C=0,1,3,5,6).
A[2]=2, C[2]=1  (c'e' 1 elemento minori o = a 2), metto 2=A[2] in B[1].
              Poi C[2]=0 (ossia C=0,0,3,5,6).
A[1]=3, C[3]=3  (ci sono 3 elementi minori o = a 3), metto 3=A[1] in B[3].
              Poi C[3]=2 (ossia C=0,0,2,5,6).

Consiglio: di riprovare con un altro esercizio, convincerti della stabilita'
(avevamo tre elementi uguali a 3, ma sono stati inseriti cosi' come comparivano nella sequenza di input...) e dell'uso dei vettori.
 

 D8 Puo' darci esempi di matroidi nei quali non e' verificata
la prima proprieta', ma la seconda si?

R8. Innanzitutto, una precisazione. Vi ricordo che,
dato un sistema I=(E,F) dove E e' un insieme finito non vuoto, F e' una famiglia di sottoinsiemi di E, si dice che I e' un matroide se valgono entrambe le
proprieta', ossia se non vale la prima (I non e' un sistema di indipendenza)
possiamo subito affermare che I non puo' essere un matroide. Cosa
vuol dire questo? Se all'esame vi viene dato un esercizio in cui si
chiede di dimostrare se un sistema I e' un matroide e dimostrate che la
prima proprieta' non e' verificata, potete fermarvi e concludere che I non e' un
matroide.

Questo significa che la vostra domanda non ha molto senso..... :-))))
Ripeto: se non vale la prima proprieta', il sistema non e' un  matroide.
Per essere un matroide, un sistema DEVE essere un sistema di indipendenza (prima
proprieta') e deve valere la seconda proprieta' (scambio).

Comunque, per soddisfare la vostra curiosita', potete controllare
un esempio che vi accenno nella soluzione e che vi scrivo qui sotto
(sistema che non verifica la prima proprieta', ma verifica la seconda...
ripeto...non è un matroide!)

Considerate E insieme degli interi positivi minori di 100 e
F la famiglia dei sottoinsiemi A di E tali che A contiene
ESATTAMENTE un elemento tra {1,2,3,4}.
La prima proprieta' non e' vera: consideriamo un insieme A in F
(dunque A contiene esattamente un elemento in {1,2,3,4} e poi altri
elementi di E che non sono in {1,2,3,4}). E' facile vedere che
esistono dei sottoinsiemi di A che non contengono  quell'elemento, ossia
che non hanno elementi in {1,2,3,4}, percio' esistono sottoinsiemi di A
che non stanno in F. La seconda proprieta' resta vera: difatti,
comunque prendo due insiemi A,B in F tali che |A|=|B|-1,
esiste sempre un elemento b in B (con b che non appartiene
ad A) che posso spostare: sara' proprio quello che sta in B e che
non appartiene a {1,2,3,4} (l'ipotesi
sulla cardinalita' di B garantisce l'esistenza di tale elemento).

Un'ultima cosa: vedete che ho scritto in maiuscolo ESATTAMENTE,
perche' "di solito" questo vincolo fa scattare la falsita' della
prima proprieta'....