Conversione di un problema con lo zaino limitato in un problema con lo zaino 0/1


12

Ho riscontrato un problema in cui l'obiettivo era utilizzare la programmazione dinamica (anziché altri approcci). C'è una distanza da percorrere e una serie di cavi di diverse lunghezze. Qual è il numero minimo di cavi necessari per coprire esattamente la distanza?

Per me questo sembrava un problema con lo zaino , ma poiché potevano esserci multipli di una lunghezza particolare, si trattava di un problema con lo zaino limitato, piuttosto che un problema con lo zaino 0/1. (Considera il valore di ogni articolo come il suo peso.) Prendendo l'approccio ingenuo (e non preoccupandomi dell'espansione dello spazio di ricerca), il metodo che ho usato per convertire il problema dello zaino limitato in un problema dello zaino 0/1, era semplicemente suddividere i multipli in singoli e applicare il noto algoritmo di programmazione dinamica. Sfortunatamente, questo porta a risultati non ottimali.

Ad esempio, dati forniti:
1 x 10 piedi,
1 x 7 piedi,
1 x 6 piedi,
5 x 3 piedi,
6 x 2 piedi,
7 x 1 piedi

Se l'intervallo di destinazione è 13 piedi, l'algoritmo DP seleziona 7 + 6 per coprire la distanza. Un algoritmo avido avrebbe scelto 10 + 3, ma è un pareggio per il numero minimo di cavi. Il problema sorge quando si tenta di estendere 15 piedi. L'algoritmo DP ha finito per raccogliere 6 + 3 + 3 + 3 per ottenere 4 cavi, mentre l'algoritmo greedy prende correttamente 10 + 3 + 2 per soli 3 cavi.

Ad ogni modo, facendo una leggera scansione della conversione limitata a 0/1, sembra il noto approccio per convertire più elementi in {p, 2p, 4p ...}. La mia domanda è: come funziona questa conversione se p + 2p + 4p non si somma al numero di più elementi. Ad esempio: ho 5 cavi da 3 piedi. Non riesco molto ad aggiungere {3, 2x3, 4x3} perché 3 + 2x3 + 4x3> 5x3. Devo aggiungere invece {3, 4x3}?

[Attualmente sto cercando di scrivere il documento "Oregon Trail Knapsack Problem", ma al momento sembra che l'approccio usato non sia la programmazione dinamica.]


1
Penso che questo sia più adatto a math.stackexchange.com o anche a mathoverflow.net
Oded,

3
Sono stato diviso tra lo stackoverflow generico e qui. Leggendo le FAQ su entrambi i siti, questo sito elenca prima le strutture dati e gli algoritmi. Leggendo le FAQ per il sito di matematica, sembrava suggerire invece di chiedere al sito di cstheory.
Formiche

Risposte:


1

Potrebbe essere un errore nel tuo codice. Ho scritto il programma DP come menzionato da Naryshkin. Per l'intervallo target 13, segnala 6 + 7 e per 15, segnala 2 + 6 + 7.

# weight: cable length
# total weight: target span
# value: 1 for each cable
# want minimum number of cables, i.e. minimum total value

def knapsack_01_exact_min(weights, values, W):
    # 0-1 knapsack, exact total weight W, minimizing total value
    n = len(weights)
    values = [0] + values
    weights = [0] + weights
    K = [[0 for i in range(W+1)] for j in range(n+1)]
    choice = [[0 for i in range(W+1)] for j in range(n+1)]
    for i in range(1, n+1):
        for w in range(1, W+1):
            K[i][w] = K[i-1][w]
            choice[i][w] = '|'
            if w >= weights[i]:
                t = K[i-1][w-weights[i]]
                if (w==weights[i] or t) and (K[i][w]==0 or t+values[i] < K[i][w]):
                    choice[i][w] = '\\'
                    K[i][w] = t+values[i]
    return K[n][W], choice

def print_choice(choice, weights):
    i = len(choice)-1
    j = len(choice[0])-1
    weights = [0] + weights
    while i > 0 and j > 0:
        if choice[i][j]=='\\':
            print weights[i],
            j -= weights[i]
        i -= 1
    print

lens = [10, 7, 6] + 5*[3] + 6*[2] + 7*[1]
values = (3+5+6+7)*[1]
span = 13
v, choice = knapsack_01_exact_min(lens, values, span)
print "need %d cables to span %d:" % (v,span),
print_choice(choice, lens)

span = 15
v, choice = knapsack_01_exact_min(lens, values, span)
print "need %d cables to span %d:" % (v,span),
print_choice(choice, lens)

Se si regola l'ordine delle lunghezze di input, è possibile fornire altre soluzioni ottimali. Ad esempio, lens = 5*[3] + 6*[2] + 7*[1] + [10, 7, 6]darà 15 = 10 + 2 + 3.


Dove hai ottenuto l'istruzione if: 'if (w-weights [i] == 0 o t) e (K [i] [w] == 0 o t + valori [i] <K [i] [w] ): '? Se dimentica ora la fonte del mio algoritmo DP, ma non avevo controlli zero, controlla solo "(t + valore [i] <K [i] [w])"
Ants

1
Stai risolvendo il peso totale esatto , il che significa che ogni volta che un articolo viene prelevato, dobbiamo assicurarci che sia rispettato il peso esatto (del passaggio corrente). Quindi, quando decidiamo di scegliere un oggetto, la seconda clausola "t + valori [i] <K [i] [w]" assicura che avremo un valore totale più piccolo; ma prima di ciò, dobbiamo anche riempire il peso richiesto, ovvero i primi articoli i-1 devono essere in grado di riempire il peso (w-weights [i]), quindi la prima clausola "if K [i-1] [w -weights [i]] "(sto usando una variabile temporanea t per quello).
jsz,

Esistono due controlli aggiuntivi "w == pesi [i]" e "K [i] [w] == 0"; sono necessari e dovuti alla modalità di inizializzazione delle tabelle; Penso che sarai in grado di capirlo, quindi non entrerò nei dettagli. (Ho cambiato w-weights [i] == 0 in w == weights [i]; dovrebbe essere più chiaro).
jsz,

1

Il modo che ho visto usato per trasformare un problema a zaino limitato in uno 0/1 è quello di avere più elementi identici. Dire se si dispone dei seguenti elementi (dati come peso, utilità):

  • 2 x 1, 2
  • 3 x 2, 3

Lo trasformeresti in un problema 0/1 usando gli oggetti con

  • 1, 2
  • 1, 2
  • 2, 3
  • 2, 3
  • 2, 3

E usa un algoritmo 0/1 per risolverlo. Probabilmente avrai più soluzioni di uguale correttezza, quindi ne sceglierai una arbitraria.


Ora riguardo al tuo problema con il filo: vorrei che la lunghezza del cavo fosse pari e il valore di ciascun cavo fosse esattamente lo stesso (chiamalo 1, anche se qualsiasi valore positivo funzionerà). Ora usa il tuo algoritmo di risoluzione degli zaini preferito, ma dove normalmente selezioneresti una soluzione (parziale) che massimizzi il valore, selezionane uno che lo minimizzi. Inoltre, ignorare tutte le soluzioni che non hanno un peso totale uguale alla capacità. Posso probabilmente (provare a) scrivere un algoritmo più concreto con il codice reale se qualcuno lo desidera.


Sì, è esattamente quello che sto facendo per riempire peso e valore. Stavo calcolando per valore massimo, piuttosto che min. Ho appena cambiato il codice per calcolare per min come hai suggerito, e ho inizializzato la riga 0 della tabella DP in modo che diventasse il MAXINT. Sempre lo stesso risultato, la soluzione di programmazione dinamica per i problemi con lo zaino finisce ancora per raccogliere 6 + 3 + 3 + 3 invece di 10 + 3 + 2 o 7 + 6 + 2.
Formiche
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.