Salta al contenuto
Note per Studenti Esercizio - (2,1)-boxing di oggetti, scelta greedy e ottimalità (esercitazione greedy, es. 10.3)

Esercizio - (2,1)-boxing di oggetti, scelta greedy e ottimalità (esercitazione greedy, es. 10.3)

In questa pagina 5

Testo (esercitazione sul paradigma greedy, dispense del corso di Algoritmi per l'Ingegneria, Ing. Informatica UniPD). Sia n>0n > 0 e sia O={o1,…,on}O = \{o_1, \dots, o_n\} un insieme di oggetti, ciascuno con un peso pip_i. Senza perdita di generalità i pesi sono normalizzati in (0,1](0, 1] e gli oggetti sono ordinati per peso non decrescente: 0<p1≤p2≤⋯≤pn.0 < p_1 \le p_2 \le \dots \le p_n . Un boxing di OO è una partizione di OO in m≤nm \le n sottoinsiemi non vuoti B={B1,…,Bm}B = \{B_1, \dots, B_m\} (le "scatole"): Bi∩Bj=∅B_i \cap B_j = \emptyset per i≠ji \ne j e ⋃iBi=O\bigcup_i B_i = O. Un boxing è un (2,1)-boxing se ∀i:∣Bi∣≤2∧∑oj∈Bipj≤1,\forall i : \quad |B_i| \le 2 \quad\wedge\quad \sum_{o_j \in B_i} p_j \le 1, cioè ogni scatola contiene al più due oggetti, di peso complessivo al più 11. Si vuole un (2,1)-boxing di cardinalità minima, con un approccio greedy: algoritmo, correttezza (scelta greedy e sottostruttura ottima), complessità.

Esempio. Con p1=0,2p_1 = 0{,}2, p2=0,6p_2 = 0{,}6, p3=0,7p_3 = 0{,}7 i (2,1)-boxing sono tre: {{0,2},{0,6},{0,7}}\{\{0{,}2\}, \{0{,}6\}, \{0{,}7\}\}, {{0,2,0,6},{0,7}}\{\{0{,}2, 0{,}6\}, \{0{,}7\}\} e {{0,2,0,7},{0,6}}\{\{0{,}2, 0{,}7\}, \{0{,}6\}\} (la coppia 0,6+0,70{,}6 + 0{,}7 pesa 1,3>11{,}3 > 1). Gli ultimi due sono ottimi, con 2 scatole.


Passo 1: la scelta greedy

Si guarda l'oggetto più pesante, ono_n, perché è quello più difficile da sistemare. Casi:

  1. n=1n = 1: ono_n è l'unico oggetto, scatola B1={on}B_1 = \{o_n\}.
  2. n>1n > 1 e p1+pn>1p_1 + p_n > 1: anche accoppiato con l'oggetto più leggero o1o_1 si supera 11, quindi con qualsiasi altro oggetto (di peso almeno p1p_1) si supererebbe: ono_n sta da solo, B1={on}B_1 = \{o_n\}.
  3. n>1n > 1 e p1+pn≤1p_1 + p_n \le 1: si fa la scatola B1={o1,on}B_1 = \{o_1, o_n\}, accoppiando il più pesante con il più leggero.

Nel caso 3 la scelta è "azzardata": si accontenta di accoppiare ono_n con o1o_1, anche se potrebbe esistere un oggetto più pesante di o1o_1 che ci sta ancora con ono_n, sprecando parte della portata. La proprietà di scelta greedy proverà che non si perde nulla.

Clean-up: nessuno. Dopo la scelta la sottoistanza residua ORO_R è formata dagli oggetti ancora da sistemare:

  • casi 1 e 2: OR={o1,…,on−1}O_R = \{o_1, \dots, o_{n-1}\} (nel caso 1 è vuoto);
  • caso 3: OR={o2,…,on−1}O_R = \{o_2, \dots, o_{n-1}\}.

La residua è ancora ordinata per peso e soddisfa le stesse ipotesi, e ha almeno un oggetto in meno (Paradigma greedy - scelta golosa, sottostruttura e selezione delle attivitàIl paradigma greedy compie subito una scelta localmente migliore (scelta greedy), pulisce l'istanza (clean-up) e risolve l'unica sottoistanza residua, con ricorsione di coda eliminabile; il codice è facile, la correttezza no e richiede due proprietà, la scelta greedy (esiste un ottimo che la contiene) e la sottostruttura ottima (cut-and-paste), da cui la correttezza segue per induzione. Caso di studio: la selezione delle attività, con l'algoritmo del primo che finisce, correttezza completa, complessità Theta(n) dopo l'ordinamento e controesempi per le scelte greedy sbagliate.Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività →).

Passo 2: l'algoritmo

Gli indici first e last delimitano la residua: {ofirst,…,olast}\{o_{first}, \dots, o_{last}\}.

BOXING_2_1(O, p)                         {p ordinato non decrescente}
    n <- |O|
    B <- {}
    first <- 1;  last <- n
    while first <= last do
        if first = last or p[first] + p[last] > 1 then      // casi 1 e 2
            B <- B U { {o_last} }
        else                                                  // caso 3
            B <- B U { {o_first, o_last} }
            first <- first + 1
        last <- last - 1
    return B

(last diminuisce sempre, first aumenta solo nel caso 3, quando ofirsto_{first} è stato inscatolato.)

Ritorna un (2,1)-boxing. Ogni iterazione inscatola olasto_{last} e, nel caso 3, anche ofirsto_{first}, poi li esclude dalla residua; il ciclo termina con first>lastfirst > last, cioè dopo aver inscatolato ogni oggetto in una e una sola scatola. Le scatole hanno uno o due oggetti e peso totale ≤1\le 1: nel caso 3 per la condizione dell'if, altrimenti c'è un solo oggetto con p≤1p \le 1.

Complessità. last decresce di 11 a ogni iterazione, quindi le iterazioni sono al più nn; sono almeno ⌈n/2⌉\lceil n/2 \rceil (ogni iterazione sistema al più due oggetti). Ogni iterazione fa una somma e un confronto: Θ(n)\Theta(n) confronti tra pesi (più O(nlog⁡n)O(n \log n) se i pesi non sono già ordinati).

Esempio eseguito a mano

Pesi (0,10; 0,20; 0,30; 0,45; 0,60; 0,70; 0,85; 0,95)(0{,}10;\ 0{,}20;\ 0{,}30;\ 0{,}45;\ 0{,}60;\ 0{,}70;\ 0{,}85;\ 0{,}95), n=8n = 8.

iter. first, last pfirst+plastp_{first} + p_{last} caso scatola dopo (first, last)
1 1, 8 0,10+0,95=1,05>10{,}10 + 0{,}95 = 1{,}05 > 1 2 {o8}\{o_8\} 1, 7
2 1, 7 0,10+0,85=0,95≤10{,}10 + 0{,}85 = 0{,}95 \le 1 3 {o1,o7}\{o_1, o_7\} 2, 6
3 2, 6 0,20+0,70=0,90≤10{,}20 + 0{,}70 = 0{,}90 \le 1 3 {o2,o6}\{o_2, o_6\} 3, 5
4 3, 5 0,30+0,60=0,90≤10{,}30 + 0{,}60 = 0{,}90 \le 1 3 {o3,o5}\{o_3, o_5\} 4, 4
5 4, 4 first=lastfirst = last 1 {o4}\{o_4\} 4, 3 (fine)

Risultato: 55 scatole, che è l'ottimo (verificato con una ricerca esaustiva). Con l'esempio del testo (0,2;0,6;0,70{,}2; 0{,}6; 0{,}7): n=3n = 3, p1+p3=0,9≤1p_1 + p_3 = 0{,}9 \le 1 dà {o1,o3}\{o_1, o_3\}; resta {o2}\{o_2\} con first=lastfirst = last: {o2}\{o_2\}. Due scatole.

Passo 3: proprietà di scelta greedy

Enunciato. Esiste un (2,1)-boxing ottimo che contiene la scelta greedy.

Dimostrazione. Casi 1 e 2. In ogni (2,1)-boxing ono_n sta da solo (non può stare con nessun altro oggetto), quindi ogni boxing, in particolare uno ottimo, contiene {on}\{o_n\}.

Caso 3 (scelta greedy {o1,on}\{o_1, o_n\} con p1+pn≤1p_1 + p_n \le 1). Sia B∗B^\ast un (2,1)-boxing ottimo.

  • Se o1o_1 e ono_n sono nella stessa scatola, questa è proprio {o1,on}\{o_1, o_n\} (al più due oggetti): B∗B^\ast contiene la scelta greedy.
  • Altrimenti sono in due scatole distinte B1∋o1B_1 \ni o_1 e B2∋onB_2 \ni o_n. Si costruisce B^=(B∗∖{B1,B2})∪{{o1,on}, (B1∪B2)∖{o1,on}},\hat B = \big(B^\ast \setminus \{B_1, B_2\}\big) \cup \big\{\{o_1, o_n\},\ (B_1 \cup B_2) \setminus \{o_1, o_n\}\big\}, sostituendo le due scatole con la scatola greedy e una scatola con gli oggetti rimasti.

Verifiche:

  1. Numero di scatole. ∣B^∣≤∣B∗∣|\hat B| \le |B^\ast| (la seconda nuova scatola, se fosse vuota, si toglie: ci sarebbe una scatola in meno, assurdo per l'ottimalità; quindi non è vuota e ∣B^∣=∣B∗∣|\hat B| = |B^\ast|).
  2. La scatola greedy {o1,on}\{o_1, o_n\} è ammissibile per ipotesi (p1+pn≤1p_1 + p_n \le 1).
  3. La seconda scatola contiene al più due oggetti, perché B1B_1 porta al più un oggetto oltre a o1o_1 e B2B_2 al più uno oltre a ono_n. Se ne ha uno solo, rispetta il vincolo di peso. Se ne ha due, siano os∈B1o_s \in B_1 e ot∈B2o_t \in B_2. Allora ps≤pnp_s \le p_n (ono_n è il più pesante) e pt+pn≤1p_t + p_n \le 1 (vincolo su B2B_2, che contiene oto_t e ono_n), cioè pt≤1−pnp_t \le 1 - p_n. Sommando, ps+pt≤pn+(1−pn)=1.p_s + p_t \le p_n + (1 - p_n) = 1 .
  4. Gli altri oggetti restano nelle scatole di prima.

Dunque B^\hat B è un (2,1)-boxing con la stessa cardinalità di B∗B^\ast, quindi ottimo, e contiene la scelta greedy. □\square

Passo 4: proprietà di sottostruttura ottima

Enunciato. Se B^\hat B è un (2,1)-boxing ottimo che contiene la scelta greedy, allora le altre scatole B^R\hat B_R formano un (2,1)-boxing ottimo della residua ORO_R.

Dimostrazione. Non c'è clean-up, quindi è semplice. Le scatole di B^\hat B diverse dalla scatola greedy contengono tutti e soli gli oggetti di ORO_R, nel rispetto dei vincoli: sono un (2,1)-boxing ammissibile B^R\hat B_R di ORO_R. Se non fosse ottimo, aggiungendo la scatola greedy a un (2,1)-boxing ottimo di ORO_R si otterrebbe un (2,1)-boxing di OO con meno scatole di B^\hat B (cut-and-paste), contro la sua ottimalità. □\square

In formula opt(O)=1+opt(OR)\mathrm{opt}(O) = 1 + \mathrm{opt}(O_R), con opt(∅)=0\mathrm{opt}(\emptyset) = 0.

Passo 5: correttezza dell'algoritmo

Per induzione sul numero di oggetti nn. Base: n=0n = 0, nessuna scatola. Passo: l'algoritmo compie la scelta greedy dei casi 1, 2 o 3 e prosegue, con le stesse istruzioni, sulla residua (la parte rimasta tra first e last), che per ipotesi induttiva è risolta in modo ottimo. Per la sottostruttura il numero di scatole è 1+opt(OR)=opt(O)1 + \mathrm{opt}(O_R) = \mathrm{opt}(O).

Verifica numerica: confronto con la ricerca esaustiva del minimo numero di scatole su 3000 istanze casuali (n≤10n \le 10, pesi interi in (0,100](0, 100] con limite 100100) e controllo che ogni uscita sia un (2,1)-boxing con ⌈n/2⌉≤\lceil n/2 \rceil \le iterazioni ≤n\le n.

Versione ripasso

  • Problema: pesi 0<p1≤⋯≤pn≤10 < p_1 \le \dots \le p_n \le 1; partizione in scatole con al più 22 oggetti e peso totale ≤1\le 1 (un (2,1)-boxing); si vuole il minimo numero di scatole. Esempio 0,2;0,6;0,70{,}2; 0{,}6; 0{,}7: i (2,1)-boxing sono 33, due ottimi con 22 scatole.
  • Scelta greedy (si guarda l'oggetto più pesante ono_n): caso 1 n=1n = 1 e caso 2 p1+pn>1p_1 + p_n > 1 danno {on}\{o_n\} (con nessun altro ci sta); caso 3 p1+pn≤1p_1 + p_n \le 1 dà {o1,on}\{o_1, o_n\} (accoppia con il più leggero, anche se ne esiste uno più pesante che ci starebbe). Clean-up vuoto; residua {o1..on−1}\{o_1..o_{n-1}\} (casi 1 e 2) o {o2..on−1}\{o_2..o_{n-1}\} (caso 3), ancora ordinata (Paradigma greedy - scelta golosa, sottostruttura e selezione delle attivitàIl paradigma greedy compie subito una scelta localmente migliore (scelta greedy), pulisce l'istanza (clean-up) e risolve l'unica sottoistanza residua, con ricorsione di coda eliminabile; il codice è facile, la correttezza no e richiede due proprietà, la scelta greedy (esiste un ottimo che la contiene) e la sottostruttura ottima (cut-and-paste), da cui la correttezza segue per induzione. Caso di studio: la selezione delle attività, con l'algoritmo del primo che finisce, correttezza completa, complessità Theta(n) dopo l'ordinamento e controesempi per le scelte greedy sbagliate.Paradigma greedy - scelta golosa, sottostruttura e selezione delle attività →).
  • Algoritmo: first <- 1; last <- n; finché first <= last: se first = last o p[first] + p[last] > 1 scatola {o_last}, altrimenti scatola {o_first, o_last} e first <- first + 1; sempre last <- last - 1. Ogni oggetto finisce in una sola scatola, il ciclo termina con first > last; iterazioni tra ⌈n/2⌉\lceil n/2 \rceil e nn, una somma e un confronto ciascuna: Θ(n)\Theta(n).
  • Esempio (0,10;0,20;0,30;0,45;0,60;0,70;0,85;0,95)(0{,}10; 0{,}20; 0{,}30; 0{,}45; 0{,}60; 0{,}70; 0{,}85; 0{,}95): 0,10+0,95>10{,}10 + 0{,}95 > 1 dà {o8}\{o_8\}; o1+o7=0,95o_1 + o_7 = 0{,}95 dà {o1,o7}\{o_1, o_7\}; o2+o6=0,90o_2 + o_6 = 0{,}90 dà {o2,o6}\{o_2, o_6\}; o3+o5=0,90o_3 + o_5 = 0{,}90 dà {o3,o5}\{o_3, o_5\}; first = last dà {o4}\{o_4\}. In tutto 55 scatole, ottimo.
  • Scelta greedy (prova): casi 1 e 2 banali (ono_n è sempre solo). Caso 3: B∗B^\ast ottimo; se o1o_1 e ono_n sono nella stessa scatola questa è {o1,on}\{o_1, o_n\}; altrimenti o1∈B1o_1 \in B_1, on∈B2o_n \in B_2 e si sostituisce con B^=B∗∖{B1,B2}∪{{o1,on},(B1∪B2)∖{o1,on}}\hat B = B^\ast \setminus \{B_1, B_2\} \cup \{\{o_1, o_n\}, (B_1 \cup B_2) \setminus \{o_1, o_n\}\}. La seconda scatola ha al più due oggetti os∈B1o_s \in B_1, ot∈B2o_t \in B_2 con ps≤pnp_s \le p_n e pt+pn≤1p_t + p_n \le 1 (vincolo di B2B_2): ps+pt≤pn+1−pn=1p_s + p_t \le p_n + 1 - p_n = 1. Stessa cardinalità, dunque B^\hat B è ottimo e contiene la scelta greedy.
  • Sottostruttura: le altre scatole di un ottimo con la scelta greedy sono un boxing ammissibile di ORO_R; se non fosse ottimo, con la scatola greedy si migliorerebbe l'ottimo (cut-and-paste). opt(O)=1+opt(OR)\mathrm{opt}(O) = 1 + \mathrm{opt}(O_R), opt(∅)=0\mathrm{opt}(\emptyset) = 0.
  • Correttezza: induzione su nn; base istanza vuota; il passo applica scelta greedy e sottostruttura. Verificato con la ricerca esaustiva su 3000 istanze casuali.
  • Errori tipici: incrementare first anche nei casi 1 e 2 (quando ofirsto_{first} non è stato inscatolato); non trattare first = last; dimenticare che se o1o_1 e ono_n sono già nella stessa scatola non c'è nulla da scambiare; usare pt≤1−pnp_t \le 1 - p_n senza citare il vincolo su B2B_2; dimenticare che la seconda scatola può avere un solo oggetto.

Teoria collegata