#python #algorithm #recursion #dynamic-programming
#python #алгоритм #рекурсия #динамическое программирование
Вопрос:
В книге «Введение в алгоритмы» я пытаюсь реализовать задачу динамического программирования, которая известна как «программа для вырезания стержней». Здесь у меня есть массив, определяющий цену стержней переменной длины. Итак, array {1, 4} определяет, что цена стержня длиной 1 дюйм составляет 1 доллар, а цена стержня длиной 4 также равна 4 долларам. Мне дают входные данные, которые представляют собой длину заданного стержня. Моя цель — разрезать стержень на несколько частей, чтобы длина каждой части оставалась целой, а общая стоимость частей была максимальной.
Вот моя программа
print("Input Length Prices: ")
p = [int(x) for x in input().split()]
def cut_rod(n):
if n == 0:
return 0
q = -1
for i in range(1, n 1):
q = max(q, p[i-1] cut_rod(n-1))
return q
print("Input Length to Cut: ")
print(cut_rod(int(input())))
Вот мои входные и выходные данные.
Ввод
1 4
2
Вывод
5 (This is the sum of 1 and 4)
Ожидаемый результат
4
Таким образом, вместо максимальной общей цены она дает сумму всех цен длин. Я также попробовал несколько других входных данных. Во всех случаях это дает сумму. Это очень странно.
Редактировать: стержень тоже можно сохранить неразрезанным.
Комментарии:
1. Похоже, ожидаемый результат должен быть 2, поскольку длина стержня составляет всего 2.
2. @user3386109 Нет
3. Потому что цена стержня длиной 2 составляет 4 доллара. так что 4 — это правильный результат. Стержень может оставаться неразрезанным. @user3386109
Ответ №1:
В вашей программе есть две проблемы:
# 1) Вам нужно cut_rod(n-i)
, а не cut_rod(n-1)
в вашем рекурсивном вызове. Как только вы удалите часть длины i
, у вас p-i
останется.
# 2) Вы неоднократно вызываете cut_rod
рекурсивно, и для больших значений n
вы выполняете O(n*n)
рекурсивные вызовы. Суть динамического программирования в том, что вы вычисляете значение для меньших результатов, а затем кэшируете их, а не пересчитываете каждый раз, когда они вам нужны.
К счастью, Python упрощает это. Просто аннотируйте свой код с помощью @functools.lru_cache(None)
=== Исправление ===
Я писал выше, что этот код без кэширования был O(n*n)
. На самом деле это экспоненциально или хуже. Наивная рекурсивная реализация чисел Фибоначчи экспоненциальна, и это делает только два рекурсивных вызова для каждого аргумента n
больше 2; эта программа делает n - 1
.
Комментарии:
1. Не уверен, что декоратор functools лучше всего использовать с педагогической точки зрения. Имхо, было бы лучше использовать либо восходящий подход, либо явную обработку запоминания.
2. Извините, я набрал n-i в самой программе. Здесь ошибка ввода, потому что я писал на мобильном устройстве. И я попробовал короткий ввод только для тестирования с наивным подходом $ O (n ^ 2) $. Я еще не применял к нему DP. Редактировать: я каким-то образом меняю значение на n-1 и в реальном коде. Спасибо
3. Спасибо. Ваш ответ был полезен, и спасибо за информирование декоратора. Я не знал об этом раньше и использовал словари для запоминания.
4. @Tassle Я думаю, мы можем использовать его в конкурсах по программированию, пока у нас мало времени. И когда функция содержит больше параметра, lru_cache будет очень экономить время
5. @Md.AshrafulIslam Конечно, я просто говорил, что было бы неплохо увидеть, как это делается «вручную», чтобы узнать, как это делается. Конечно, вы можете использовать любой инструмент, который захотите 🙂