Risolto il problema dello zaino 0/1 utilizzando l'esempio di programmazione dinamica
โก Riepilogo intelligente
Il problema dello zaino 0/1 utilizza la programmazione dinamica per selezionare da un insieme di pacchi ponderati e di valore in modo che il peso totale rimanga entro una capacitร M mentre il valore totale raggiunge il massimo possibile.

Qual รจ il problema dello zaino?
Migliori Problema dello zaino รจ un classico problema di ottimizzazione combinatoria. Un supermercato immagazzina n pacchetti (n โค 100). Pacchetto i ha peso W[i] โค 100 e valore V[i] โค 100. Un ladro non puรฒ trasportare un peso superiore alla capacitร M (M โค 100). Quali pacchi dovrebbe prendere il ladro per massimizzare il valore totale?
Ingresso:
- Il peso massimo M ed il numero di colli n.
- Array di peso W[i] e valore corrispondente V[i].
Produzione:
- Valore totale massimo ottenibile entro i limiti della capacitร .
- L'insieme esatto dei pacchi che il ladro dovrebbe portare via.
L'algoritmo dello zaino si divide in due varianti ben note:
- 0/1 Problema con lo zaino Risolto tramite programmazione dinamica. Ogni pacchetto viene preso per intero o scartato, senza parti parziali nรฉ duplicati.
- Problema dello zaino frazionario Risolto con una strategia avida. Qui puoi prendere una frazione di qualsiasi pacchetto per riempire la capacitร rimanente.
Come risolvere il problema dello zaino utilizzando la programmazione dinamica con esempio
Il metodo "divide et impera" scompone un problema complesso in sottoproblemi, continuando a suddividere fino a quando ciascun sottoproblema non risulta semplice. La ricorsione pura, al contrario, spesso risolve lo stesso sottoproblema piรน volte, sprecando risorse.
L'idea centrale della programmazione dinamica dello zaino รจ quella di memorizzare ogni sottoproblema risolto in una tabella. Le chiamate ripetute leggono la risposta invece di ricalcolarla, trasformando una ricorsione esponenziale in codice a tempo polinomiale.
Risolvi il problema dello zaino utilizzando la programmazione dinamica
Per progettare una soluzione di programmazione dinamica, รจ necessario seguire quattro passaggi:
- Risolvi prima i sottoproblemi piรน piccoli.
- Ricavare una relazione di ricorrenza che permetta di costruire la soluzione di un sottoproblema a partire da problemi piรน piccoli.
- Memorizza le risposte ai sottoproblemi in una tabella calcolata dal basso verso l'alto utilizzando la relazione di ricorrenza.
- Ricava la risposta finale utilizzando i dati presenti nella tabella.
Analizza il problema dello zaino 0/1
Il valore ottimale dipende da due fattori indipendenti:
- Quanti pacchetti sono ancora in fase di valutazione?
- Il peso rimanente che lo zaino puรฒ ancora contenere.
Poichรฉ la funzione obiettivo dipende da due quantitร , la tabella delle opzioni deve essere bidimensionale. Sia B[i][j] indica il valore massimo quando si sceglie tra i pacchetti {1, โฆ, i} con limite di peso j.
- La risposta finale รจ
B[n][M], il miglior valore totale su tutti gli n pacchetti con capacitร M. - Il peso totale selezionato รจ sempre limitato dalla capacitร attuale:
B[i][j] โค j.
Esempio: se B[4][10] = 8, il peso totale migliore dei primi quattro pacchi con capacitร inferiore a 10 รจ 8. Alcuni di questi quattro pacchi possono essere saltati.
Formula per calcolare B[i][j]
W[i],V[i]sono il peso e il valore del pacchetto i, dove i รจ in {1, โฆ, n}.Mรจ il peso massimo che lo zaino puรฒ trasportare.
Caso base con un pacchetto: per ogni capacitร j โฅ W[1]:
B[1][j] = W[1]
Nel caso generale, decidere se includere il pacchetto i nella capacitร j:
- Se il pacchetto i รจ saltato, B[i][j] รจ uguale al miglior valore utilizzando i pacchetti {1, โฆ, i-1} con capacitร j:
B[i][j] = B[i - 1][j]
- Se il pacchetto i รจ preso (consentito solo quando W[i] โค j), B[i][j] รจ uguale a V[i] piรน il valore migliore tra i pacchetti {1, โฆ, i-1} con capacitร j โ W[i]:
B[i][j] = V[i] + B[i - 1][j - W[i]]
Scegli il candidato piรน grande tra i due.
Basi della Programmazione Dinamica
Combinando i due casi si ottiene la ricorrenza completa:
B[i][j] = max(B[i - 1][j], V[i] + B[i - 1][j - W[i]])
Il caso base รจ B[0][j] = 0 per ogni j, perchรฉ zero pacchetti danno zero valore indipendentemente dalla capacitร .
Calcola la tabella delle opzioni
Costruisci B usando la ricorrenza. Una volta riempito B, la stessa tabella guida il trace-back che ricostruisce i pacchetti scelti. La tabella B ha n + 1 righe e M + 1 colonne:
- La riga 0 rappresenta il caso base, riempita con zeri.
- Utilizzare la riga 0 per calcolare la riga 1, la riga 1 per calcolare la riga 2 e continuare fino al completamento della riga n.
Tabella delle opzioni
Trace
Una volta completato B, concentrati su B[n][M], il valore totale ottimale su tutti gli n pacchetti con capacitร M.
- If B[n][M] = B[n-1][M], il pacchetto n non รจ stato selezionato, quindi continua tracing da B[n-1][M].
- If B[n][M] โ B[n-1][M], il pacchetto n รจ stato selezionato, quindi continua tracing da B[n-1][M โ W[n]].
Ripeti l'operazione finchรฉ non raggiungi la riga 0 della tabella.
Algoritmo per cercare la tabella delle opzioni per trovare i pacchetti selezionati
Nota: ogni volta che B[i][j] = B[i-1][j], il pacchetto i non รจ selezionato. Il valore B[n][M] รจ il valore totale ottimale da riporre nello zaino.
Passi per tracselezionando i pacchetti scelti:
- Passo 1: Inizia da i = n, j = M.
- Passo 2: Scansiona la colonna j dal basso verso l'alto finchรฉ non trovi una riga i in cui B[i][j] > B[i-1][j]. Contrassegna il pacchetto i come selezionato:
Select[i] = true. - Passo 3: Aggiorna j = j โ W[i]. Se j > 0, torna al passaggio 2, altrimenti vai al passaggio 4.
- Passo 4: Stampare tutti i pacchetti contrassegnati come selezionati.
Java Code
Le seguenti Java il metodo riempie B[][] dal basso verso l'alto, stampa la tabella per l'ispezione e poi traces i pacchetti selezionati.
public void knapsackDyProg(int W[], int V[], int M, int n) { int B[][] = new int[n + 1][M + 1]; for (int i = 0; i <= n; i++) for (int j = 0; j <= M; j++) { B[i][j] = 0; } for (int i = 1; i <= n; i++) { for (int j = 0; j <= M; j++) { B[i][j] = B[i - 1][j]; if ((j >= W[i - 1]) && (B[i][j] < B[i - 1][j - W[i - 1]] + V[i - 1])) { B[i][j] = B[i - 1][j - W[i - 1]] + V[i - 1]; } System.out.print(B[i][j] + " "); } System.out.print("\n"); } System.out.println("Max Value:\t" + B[n][M]); System.out.println("Selected Packs: "); int j = M; while (n != 0) { if (B[n][j] != B[n - 1][j]) { System.out.println("\tPackage " + n + " with W = " + W[n - 1] + " and Value = " + V[n - 1]); j = j - W[n - 1]; } n--; } }
Funzione zainoDyProg() in Java
Spiegazione del codice:
- Assegna tabella
B[][]e inizializzare ogni cella a 0. - Riempi B[][] dal basso verso l'alto utilizzando la relazione di ricorrenza della sezione precedente.
- Inizia ogni cella con il valore โskip package iโ.
B[i-1][j]. - Se la selezione del pacchetto i รจ fattibile e offre un valore nettamente migliore, sovrascrivi la cella.
- Trace riporta gli elementi selezionati dalla riga n alla riga 0.
- Ogniqualvolta viene scelto il pacchetto n, decrementa la capacitร rimanente di
W[n-1].
Nota di correzione: il parametro mutato del frammento originale M mentre continuavo a leggere B[n][M]La versione piรน sicura qui sopra utilizza un cursore separato. j per l' trace.
Migliori Java Il driver esegue l'algoritmo su due esempi pratici:
public void run() { // First Example // int W[] = new int[]{3, 4, 5, 9, 4}; // int V[] = new int[]{3, 4, 4, 10, 4}; // int M = 11; // Second Example int W[] = new int[]{12, 2, 1, 1, 4}; int V[] = new int[]{4, 2, 1, 2, 10}; int M = 15; int n = V.length; knapsackDyProg(W, V, M, n); }
Output per il primo esempio:
0 0 0 3 3 3 3 3 3 3 3 3 0 0 0 3 4 4 4 7 7 7 7 7 0 0 0 3 4 4 4 7 7 8 8 8 0 0 0 3 4 4 4 7 7 10 10 10 0 0 0 3 4 4 4 7 8 10 10 11 Max Value: 11 Selected Packs: Package 5 with W = 4 and Value = 4 Package 2 with W = 4 and Value = 4 Package 1 with W = 3 and Value = 3
Output per il secondo esempio:
0 0 0 0 0 0 0 0 0 0 0 0 4 4 4 4 0 0 2 2 2 2 2 2 2 2 2 2 4 4 6 6 0 1 2 3 3 3 3 3 3 3 3 3 4 5 6 7 0 2 3 4 5 5 5 5 5 5 5 5 5 6 7 8 0 2 3 4 10 12 13 14 15 15 15 15 15 15 15 15 Max Value: 15 Selected Packs: Package 5 with W = 4 and Value = 10 Package 4 with W = 1 and Value = 2 Package 3 with W = 1 and Value = 1 Package 2 with W = 2 and Value = 2
Complessitร temporale e spaziale del problema dello zaino 0/1
- Complessitร temporale: O(n ยท M) โ i due cicli annidati fanno scorrere n elementi attraverso M+1 stati di capacitร .
- Complessitร spaziale: O(n ยท M) per la tabella completa, riducibile a O(M) tramite keeping solo la riga precedente quando tracNon รจ necessario il pagamento elettronico in contrassegno.
Il tempo di esecuzione รจ pseudo-polinomio: polinomiale nel valore di M ma esponenziale nei bit utilizzati per codificare M. Ecco perchรฉ il problema dello zaino 0/1 rimane NP-difficile anche se la programmazione dinamica รจ efficiente nella pratica.
Applicazioni del problema dello zaino 0/1
- Carico merci, imballaggio container e prelievo in magazzino nel rispetto dei limiti di peso.
- Allocazione del budget tra progetti di investimento con costi fissi e rendimento atteso.
- Problemi di taglio del materiale nella produzione che non consentono di separare i singoli pezzi.
- Schemi crittografici come Merkle-Hellman che si basano sulla difficoltร del problema dello zaino.
- Pianificazione con risorse limitate nel cloud computing e allocazione dei task della CPU.
- Selezione delle caratteristiche nell'apprendimento automatico con un budget di caratteristiche fisso.



