Scrivere un algoritmo non deterministico di complessita` polinomiale che risolva il problema del commesso viaggiatore. Vengono proposte due soluzioni, entrambe riconducibili allo schema generale. Nella soluzione I l’insieme delle scelte varia, nel senso che ad ogni città visitata dal commesso viaggiatore tale città viene eliminata dall’insieme delle scelte che potranno essere fatte successivamente. Nella soluzione II, invece, l’insieme delle scelte resta inalterato, ma ad ogni scelta si dovrà verificare che la città non sia già stata visitata. Le città siano numerate da 1 a n, e 1 sia la città di partenza. Chiamiamo D la tabella della distanze (cioè il grafo memorizzato come matrice di adiacenza), k il costo da non superare, entrambi forniti in input, e C il vettore che memorizza il percorso, se esiste; la variabile costo e` usata per ricordare il costo del cammino.
ND1_commesso_viaggiatore (D,C, k) C[1] 1 costo 0 I_S {2, …, n} for i 2 to n do C[i] choice (I_S) I_S I_S - {C[i]} costo costo + D C[i-1],C[i] if costo > k then failure costo costo + D C[n],C[1] if costo > k then failure else success I soluzione NON_deterministica (…) … for i 1 to n do I ins. delle scelte per A[i] if I = then failure A[i] choice (I) If A[1],…,A[i] “non puo` diventare una soluzione” then failure else if “e` una soluzione” then success failure (success)
ND2_commesso_viaggiatore (D,C, k) C[1] 1 costo 0 for i 2 to n do C[i] choice ({2, …, n}) for j 2 to (i - 1) do if C[i] = C[j] then failure costo costo + D C[i-1],C[i] if costo > k then failure costo costo + D C[n],C[1] if costo > k then failure else success II soluzione NON_deterministica … for i 1 to n do I ins. delle scelte per A[i] if I = then failure A[i] choice (I) If A[1],…,A[i] “non puo` diventare una soluzione” then failure else if “e` una soluzione” then success failure (success)
Equivalente algoritmo deterministico per enumerazione I soluzione comm_viaggiatore1 (D, C, k) C[1] 1 costo 0 I_S if En_C_V (2, I_S, costo) then costo costo + D C[n],C[1] if costo k then return true return false
En1_C_V (i, I_S, costo) if i n then for j 1 to |I_S| do C[i] I_S[j] costo costo + D C[i-1],C[i] I_S I_S - {C[i]} if costo k then if En_C_V (i+1, I_S, costo) then return true return false else return true Enum (i,...) if i n then I ins. scelte per A[i] if I = then return false for j 1 to |I| do A[i] I[j] if A[1],…,A[i] “puo` diventare una soluzione” then if “e` una soluzione” then return true if Enum (i+1,...) then return true return false else return false (true)
Attenzione! Ci sono due errori dovuti a due caratteristiche che distinguono questo problema da quelli visti a lezione (Domino_limitato e Cricca) I side-effect negativi ai quali si deve rimediare quando si effettua back-tracking in Domino e Cricca erano rimediati dalle assegnazioni successive costo costo - D C[i-1],C[i] I_S I_S {C[i]} In questo caso invece, se il for deve essere eseguito per un altro valore di j, il costo e l’insieme delle scelte devono essere ripristinati al valore che avevano prima dell’ultima “strada” tentata, cioè:
if i n then for j 1 to |I_S| do C[i] I_S[j] costo costo + D C[i-1],C[i] I_S I_S - {C[i]} if costo k then if En1_C_V (i+1, I_S, costo) then return true Otteniamo così la seguente versione costo costo - D C[i-1],C[i] I_S I_S {C[i]} return false else return true En1_C_V (i, I_S, costo)
Per Domino e Cricca una volta trovata una soluzione è inutile continuare a tentare strade alternative sull’albero delle scelte. Per il Commesso Viaggiatore, al contrario, quando aggiungendo il costo dell’ultimo tratto di chiusura del percorso si scopre che il costo complessivo supera k, non si ha la sicurezza che la soluzione non esista. Bisogna esplorare le scelte alternative, se ne restano. Come rimediare? Basta spostare il controllo relativo all’ultimo tratto di percorso 1 - nei punti in cui si rientra con successo, oppure 2 - sulle foglie dell’albero delle scelte, cioè all’ultimo richiamo ricorsivo, quando i > n.
if i n then for j 1 to |I_S| do C[i] I_S[j] costo costo + D C[i-1],C[i] I_S I_S - {C[i]} if costo k then if En_C_V (i+1, I_S, costo) and costo + D C[i-1],C[i] k then return true costo costo - D C[i-1],C[i] I_S I_S {C[i]} return false else return true En11_C_V (i, I_S, costo) 1 - nei punti in cui si rientra con successo
if i n then for j 1 to |I_S| do C[i] I_S[j] costo costo + D C[i-1],C[i] I_S I_S - {C[i]} if costo k then if En_C_V (i+1, I_S, costo) then return true costo costo - D C[i-1],C[i] I_S I_S {C[i]} return false else if costo + D C[n],C[1] k then return true else return false En12_C_V (i, I_S, costo) 2 - all’ultimo richiamo ricorsivo
if i n then for j 1 to |I_S| do C[i] I_S[j] costo costo + D C[i-1],C[i] I_S I_S - {C[i]} if costo k then if En11_C_V (i+1, I_S, costo) then return true costo costo - D C[i-1],C[i] I_S I_S {C[i]} return false else if costo + D C[n],C[1] k then return true else return false En12_C_V (i, I_S, costo) In definitiva, poichè il controllo nel programma principale è diventato inutile, si ottiene: comm_viaggiatore1 (D, C, k) C[1] 1 costo 0 I_S if En_C_V (2, I_S, costo) then return true return false
comm_viaggiatore2 (D,C, k) C[1] 1 costo 0 if En2_C_V (2, costo) then return true return false II soluzione
En2_C_V (i, costo) if i n then for j 2 to n do C[i] j t 2 while t i-1 and C[i] C[t] do t t + 1 if t = i then costo costo + D C[i-1],C[i] if costo k then if En2_C_V (i+1, costo) then return true costo costo - D C[i-1],C[i] Enum (i,...) if i n then I ins. scelte per A[i] if I = then return false for j 1 to |I| do A[i] I[j] if A[1],…,A[i] “ puo` diventare una soluzione” then if “e` una soluzione” then return true else if Enum (i+1,...) then return true return false else return false (true) return false else if costo + D C[n],C[1] k then return true else return false
En2_C_V (i, costo) if i n then for j 2 to n do C[i] j t 2 while t i-1 and C[i] C[t] do t t + 1 if t = i then costo costo + D C[i-1],C[i] if costo k then if En2_C_V (i+1, costo) then return true costo costo - D C[i-1],C[i] return false else if costo + D C[n],C[1] k then return true else return false comm_viaggiatore2 (D,C, k) C[1] 1 costo 0 if En2_C_V (2, costo) then return true return false