Преобразование ограниченной задачи о ранце в задачу 0/1.


12

Я столкнулся с проблемой, когда целью было использование динамического программирования (вместо других подходов). Существует расстояние, которое нужно охватить, и набор кабелей разной длины. Какое минимальное количество кабелей необходимо для точного определения расстояния?

Для меня это выглядело как проблема рюкзака , но, поскольку могли быть кратные определенной длины, это была проблема ограниченного ранца, а не проблема ранца 0/1. (Рассматривайте значение каждого элемента как его вес.) Используя наивный подход (не заботясь о расширении пространства поиска), метод, который я использовал для преобразования ограниченной задачи о ранце в задачу о ранце 0/1, был просто разбейте кратные числа на отдельные и примените известный алгоритм динамического программирования. К сожалению, это приводит к неоптимальным результатам.

Например, приведены кабели:
1 x 10 футов,
1 x 7 футов,
1 x 6 футов,
5 x 3 фута,
6 x 2
фута , 7 x 1 фут

Если целевой диапазон составляет 13 футов, алгоритм DP выбирает 7 + 6, чтобы охватить расстояние. Жадный алгоритм выбрал бы 10 + 3, но это ничья для минимального количества кабелей. Проблема возникает при попытке пролета 15 футов. Алгоритм DP в итоге выбрал 6 + 3 + 3 + 3, чтобы получить 4 кабеля, в то время как жадный алгоритм правильно выбирает 10 + 3 + 2 только для 3 кабелей.

В любом случае, делая небольшое сканирование с преобразованием в 0/1, кажется, что это хорошо известный подход для преобразования нескольких элементов в {p, 2p, 4p ...}. Мой вопрос заключается в том, как работает это преобразование, если p + 2p + 4p не складывается в число нескольких элементов. Например: у меня есть 5 3-футовых кабелей. Я не могу очень хорошо добавить {3, 2x3, 4x3}, потому что 3 + 2x3 + 4x3> 5x3. Должен ли я добавить {3, 4x3} вместо этого?

[В настоящее время я пытаюсь получить документ «Проблема рюкзака в Орегоне», но в настоящее время похоже, что используемый там подход - это не динамическое программирование.]


1
Я думаю, что это больше подходит для math.stackexchange.com или даже mathoverflow.net
Одед

3
Я разрывался между общим стековым потоком и здесь. Читая часто задаваемые вопросы на обоих сайтах, этот сайт сначала перечисляет структуры данных и алгоритмы. Читая часто задаваемые вопросы для математического сайта, казалось, что вместо этого предлагайте спросить на сайте cstheory.
Муравьи

Ответы:


1

Это может быть какая-то ошибка в вашем коде. Я написал программу DP, как упомянул Нарышкин. Для целевого промежутка 13 он сообщает 6 + 7, а для 15 - 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)

Если вы скорректируете порядок входных длин, это может дать другие оптимальные решения. Например, lens = 5*[3] + 6*[2] + 7*[1] + [10, 7, 6]даст 15 = 10 + 2 + 3.


Где вы взяли оператор if: 'if (w-weights [i] == 0 или t) и (K [i] [w] == 0 или t + значения [i] <K [i] [w] ): '? Если забыть сейчас об источнике моего алгоритма DP, но у меня не было нулевых проверок, нужно только проверять наличие '(t + value [i] <K [i] [w])'
Ants

1
Вы определяете точный общий вес, что означает, что всякий раз, когда предмет выбран, нам нужно убедиться, что точный вес (текущего шага) будет достигнут. Итак, когда мы решаем выбрать элемент, второе предложение «t ​​+ values ​​[i] <K [i] [w]» гарантирует, что мы получим меньшее общее значение; но до этого нам также необходимо заполнить требуемый вес, то есть первые элементы i-1 должны быть в состоянии заполнить вес (w-weights [i]), отсюда и первый пункт «if K [i-1] [w» -weights [i]] "(для этого я использую временную переменную t).
JSZ

Есть две дополнительные проверки "w == weights [i]" и "K [i] [w] == 0"; они необходимы и из-за того, как таблицы инициализируются; Я думаю, вы сможете разобраться, поэтому я не буду вдаваться в подробности. (Я изменил w-weights [i] == 0 на w == weights [i]; это должно быть более понятно).
JSZ

1

Способ, который я видел, использовал для преобразования ограниченной задачи о ранце в 0/1, это просто иметь несколько одинаковых предметов. Скажите, если у вас есть следующие предметы (указаны как вес, полезность):

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

Вы бы превратили его в задачу 0/1, используя элементы с

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

И используйте алгоритм 0/1 для его решения. Скорее всего, у вас будет несколько решений одинаковой правильности, поэтому вы выбираете произвольное.


Теперь о вашей проблеме с проводом: я бы хотел, чтобы длина кабеля была точной, а значение каждого кабеля было бы одинаковым (назовите его 1, хотя любое положительное значение будет работать). Теперь используйте ваш любимый алгоритм решения ранца, но там, где вы обычно выбираете (частичное) решение, которое максимизирует значение, выберите то, которое минимизирует его. Кроме того, не обращайте внимания на все решения, которые не имеют общий вес, равный емкости. Я, вероятно, могу (попытаться) написать более конкретный алгоритм с реальным кодом, если кто-то захочет.


Да, это именно то, что я делаю, чтобы заполнить вес и ценность. Я рассчитывал на максимальное значение, а не мин. Я только что изменил код для вычисления на min, как вы предложили, и инициализировал строку 0 таблицы DP как MAXINT. Тем не менее, тот же результат, решение динамического программирования для задач рюкзака все равно заканчивается выбором 6 + 3 + 3 + 3 вместо 10 + 3 + 2 или 7 + 6 + 2.
Муравьи
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.