Conversion d'un problème de sac à dos borné en problème de sac à dos 0/1


12

J'ai rencontré un problème où l'objectif était d'utiliser une programmation dynamique (au lieu d'autres approches). Il y a une distance à parcourir et un jeu de câbles de différentes longueurs. Quel est le nombre minimum de câbles nécessaires pour couvrir exactement la distance?

Pour moi, cela ressemblait à un problème de sac à dos , mais comme il pouvait y avoir des multiples d'une longueur particulière, c'était un problème de sac à dos borné, plutôt qu'un problème de sac à dos 0/1. (Considérez la valeur de chaque élément comme son poids.) En adoptant l'approche naïve (et sans se soucier de l'expansion de l'espace de recherche), la méthode que j'ai utilisée pour convertir le problème du sac à dos borné en un problème de sac à dos 0/1, était simplement décomposer les multiples en simples et appliquer l'algorithme de programmation dynamique bien connu. Malheureusement, cela conduit à des résultats sous-optimaux.

Par exemple, pour des câbles donnés:
1 x 10 pi,
1 x 7 pi,
1 x 6 pi ,
5 x 3 pi,
6 x 2 pi,
7 x 1 pi

Si la portée cible est de 13 pieds, l'algorithme DP sélectionne 7 + 6 pour couvrir la distance. Un algorithme gourmand aurait choisi 10 + 3, mais c'est une égalité pour le nombre minimum de câbles. Le problème se pose lorsque vous essayez de franchir 15 pieds. L'algorithme DP a fini par choisir 6 + 3 + 3 + 3 pour obtenir 4 câbles, tandis que l'algorithme gourmand choisit correctement 10 + 3 + 2 pour seulement 3 câbles.

Quoi qu'il en soit, en effectuant un léger balayage de la conversion limitée à 0/1, cela semble être l'approche bien connue pour convertir plusieurs éléments en {p, 2p, 4p ...}. Ma question est de savoir comment fonctionne cette conversion si p + 2p + 4p ne correspond pas au nombre de plusieurs éléments. Par exemple: j'ai 5 câbles de 3 pieds. Je ne peux pas très bien ajouter {3, 2x3, 4x3} car 3 + 2x3 + 4x3> 5x3. Dois-je ajouter {3, 4x3} à la place?

[J'essaie actuellement de chercher le document "Oregon Trail Knapsack Problem", mais il semble actuellement que l'approche utilisée n'est pas une programmation dynamique.]


1
Je pense que cela est plus appropriée math.stackexchange.com ou même mathoverflow.net
Oded

3
J'étais déchiré entre le stackoverflow générique et ici. En lisant la FAQ sur les deux sites, ce site répertorie d'abord les structures de données et les algorithmes. En lisant la FAQ pour le site de mathématiques, il a semblé être suggéré de demander à la place sur le site cstheory.
Fourmis

Réponses:


1

Cela pourrait être une erreur dans votre code. J'ai écrit le programme DP comme mentionné par Naryshkin. Pour la plage cible 13, il rapporte 6 + 7, et pour 15, il rapporte 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)

Si vous ajustez l'ordre des longueurs d'entrée, cela peut donner d'autres solutions optimales. Par exemple, lens = 5*[3] + 6*[2] + 7*[1] + [10, 7, 6]donnera 15 = 10 + 2 + 3.


Où avez-vous obtenu l'instruction if: 'if (w-weights [i] == 0 or t) and (K [i] [w] == 0 or t + values ​​[i] <K [i] [w] ): '? Si j'oublie maintenant la source de mon algorithme DP, mais je n'ai eu aucun contrôle zéro, vérifiez seulement '(t + value [i] <K [i] [w])'
Ants

1
Vous résolvez pour le poids total exact , ce qui signifie que chaque fois qu'un article est sélectionné, nous devons nous assurer que le poids exact (de l'étape en cours) est respecté. Ainsi, lorsque nous décidons de choisir un élément, la deuxième clause "valeurs t + [i] <K [i] [w]" garantit que nous aurons une valeur totale plus petite; mais avant cela, nous devons également remplir le poids requis, c'est-à-dire que les premiers i-1 éléments doivent être capables de remplir le poids (w-poids [i]), d'où la première clause "si K [i-1] [w -weights [i]] "(j'utilise une variable temporaire t pour cela).
jsz

Il existe deux vérifications supplémentaires "w == poids [i]" et "K [i] [w] == 0"; ils sont nécessaires et dus à la façon dont les tableaux sont initialisés; Je pense que vous pourrez vous en rendre compte, donc je n'entrerai pas dans les détails. (J'ai changé les poids w [i] == 0 en poids w == [i]; cela devrait être plus clair).
jsz

1

La façon dont j'ai vu utilisé pour transformer un problème de sac à dos borné en un problème de 0/1 est d'avoir simplement plusieurs éléments identiques. Dites si vous avez les éléments suivants (donnés comme poids, utilité):

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

Vous le transformeriez en un problème 0/1 en utilisant des éléments avec

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

Et utilisez un algorithme 0/1 pour le résoudre. Vous aurez probablement plusieurs solutions de même exactitude, vous en choisissez donc une arbitraire.


Maintenant, à propos de votre problème de fil: je voudrais que la longueur du câble soit le poids et que la valeur de chaque câble soit exactement la même (appelez-la 1, bien que toute valeur positive fonctionne). Maintenant, utilisez votre algorithme de résolution de sac à dos préféré mais où vous devriez normalement sélectionner une solution (partielle) qui maximise la valeur, sélectionnez-en une qui la minimise. En outre, ignorez toutes les solutions qui n'ont pas un poids total égal à la capacité. Je peux probablement (essayer) d'écrire un algorithme plus concret avec du code réel si quelqu'un le veut.


Oui, c'est exactement ce que je fais pour remplir le poids et la valeur. Je calculais pour la valeur max, plutôt que min. Je viens de changer le code pour calculer pour min comme vous l'avez suggéré, et j'ai initialisé la ligne 0 de la table DP pour être le MAXINT. Toujours le même résultat, la solution de programmation dynamique pour les problèmes de sac à dos finit toujours par choisir 6 + 3 + 3 + 3 au lieu de 10 + 3 + 2 ou 7 + 6 + 2.
Fourmis
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.