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.

  • ๐ŸŽ’ Problema: Dati n elementi, ciascuno con peso W[i] e valore V[i], selezionare un sottoinsieme che si adatti alla capacitร  M e massimizzi il valore totale senza suddividere alcun elemento.
  • ๐Ÿงฎ Ricorrenza: B[i][j] = max(B[i-1][j], V[i] + B[i-1][j โ€“ W[i]]) cattura la scelta di prendere o saltare per ogni articolo e capacitร .
  • ๐Ÿงฑ Tabella dal basso verso l'alto: Una griglia (n+1) per (M+1) memorizza le risposte ai sottoproblemi in modo che nessun lavoro venga ripetuto nelle chiamate ricorsive.
  • ๐Ÿ” Trace-Back: Leggendo la tabella da B[n][M] fino alla riga 0 si recuperano esattamente i pacchetti utilizzati dalla soluzione ottimale.
  • ๏ธ Complessitร : Il tempo di esecuzione รจ O(nยทM) e lo spazio di archiviazione รจ O(nยทM), il che rende l'algoritmo pseudo-polinomiale e inadatto quando M รจ esponenziale.
  • ๐Ÿš€ Usi: Il carico delle merci, l'allocazione del budget, la crittografia, la pianificazione delle risorse e la selezione delle funzionalitร  basata sull'IA si affidano tutti a 0/1 Knapsack.

0/1 Problema dello zaino Programmazione dinamica

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

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:

  1. Quanti pacchetti sono ancora in fase di valutazione?
  2. 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.

Calcola la tabella delle opzioni

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

Funzione zainoDyProg() in Java

Spiegazione del codice:

  1. Assegna tabella B[][] e inizializzare ogni cella a 0.
  2. Riempi B[][] dal basso verso l'alto utilizzando la relazione di ricorrenza della sezione precedente.
  3. Inizia ogni cella con il valore โ€œskip package iโ€. B[i-1][j].
  4. Se la selezione del pacchetto i รจ fattibile e offre un valore nettamente migliore, sovrascrivi la cella.
  5. Trace riporta gli elementi selezionati dalla riga n alla riga 0.
  6. 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.

DOMANDE FREQUENTI

0/1 Lo zaino seleziona un sottoinsieme di oggetti ponderati e di valore in modo che il peso totale rimanga entro la capacitร  M, massimizzando al contempo il valore totale. Ogni oggetto viene preso intero o escluso.

Il problema presenta sovrapposizioniping sottoproblemi e sottostruttura ottimale. La programmazione dinamica memorizza la risposta a ciascun sottoproblema una sola volta, quindi la ricorsione passa da un tempo esponenziale a un tempo polinomiale O(n moltiplicato per M).

0/1 Il problema dello zaino richiede oggetti interi e si risolve tramite programmazione dinamica. Zaino frazionato Consente di sezionare gli elementi e viene risolto tramite un algoritmo greedy che seleziona prima il rapporto valore-peso piรน elevato.

Sรฌ. Il problema dello zaino 0/1 รจ NP-difficile. La programmazione dinamica ha una complessitร  temporale O(n moltiplicato per M), che รจ pseudo-polinomiale. Il tempo di esecuzione รจ polinomiale rispetto al valore di M, ma esponenziale rispetto al numero di bit utilizzati per codificare M.

Sรฌ. Quando รจ necessario solo il valore massimo e non i pacchetti selezionati, รจ sufficiente conservare la riga precedente della tabella. Questo riduce la memoria da O(n moltiplicato per M) a O(M) mentre il tempo di esecuzione rimane invariato.

Il carico delle merci, l'allocazione del budget, il taglio delle scorte, la crittografia, la pianificazione delle risorse cloud e la selezione delle caratteristiche di apprendimento automatico si riducono tutti al problema dello zaino 0/1. Qualsiasi problema di imballaggio con capacitร  fissa e articoli indivisibili รจ un candidato.

Le euristiche di apprendimento automatico e di apprendimento per rinforzo superano la programmazione dinamica esatta quando M รจ molto grande. Anche le reti di puntatori e le reti neurali a grafo prevedono la selezione degli articoli su istanze industriali di grandi dimensioni.

Sรฌ. GitHub Copilot genera la tabella DP, la ricorrenza e il tracritorno elettronico Java, Python, o C++e genera test unitari che verificano sia il valore massimo che i pacchetti selezionati.

Riassumi questo post con: