lambda
- это просто синтаксис , что позволяет вам создать функциональный объект в выражении (def functionname(..): ...
- это оператор , и вы не можете использовать операторы внутри выражения).Так что лямбда просто создает функциональный объект, и ничего особенного.Вы не можете использовать функции для изменения локального пространства имен другой функции во время выполнения, нет.
Из комментариев выясняется, что вы спрашиваете, как можно использовать обратный вызов , чтобы, возможно, решить вашу проблему.проблема, но я не думаю, что вы еще полностью понимаете свое проблемное пространство и то, как работают такие вещи, как list.sort(key=...)
.Реализация сортировки в Python явно вызывает обратный вызов key
, по выбору вызываемая функция передает информацию, а функция сортировки изменяет поведение в зависимости от того, что возвращается;функция ключа не может выбрать, что произойдет с возвращаемым значением.
По моему мнению, здесь вы задаете не тот вопрос.
Проблема, которую вы пытаетесь решить, подмножество Проблема с ранцем ;у вас есть неограниченный вариант, так как Я могу объединить столько элементов списка «input_list», сколько я хочу (повторы разрешены) .
Попытка использовать itertools
решить это неправильный подход;itertools
функции будут генерировать много неправильных комбинаций.По сути, вы генерируете все комбинации с повторениями ( мультимножеств * ) для выходных размеров от 1 до целевого размера, поэтому вы можете рассчитать количество таких мультимножеств для каждого заданного размера и суммировать их:
def multiset_size(n, k):
return int(factorial(k + n - 1) / (factorial(k) * factorial(n - 1)))
generated = sum(multiset_size(len(input_list), i + 1) for i in range(target_number_of_packages))
Для вашего игрушечного примера, с 6 входами и целевым размером 4, вы генерируете 209 различных комбинаций, но есть только 5 жизнеспособных комбинаций.Это колоссальные 40,8 комбинаций, потраченных впустую за жизнеспособный выход!При больших входных размерах это соотношение будет только (намного) ухудшаться.
Проблема полного ранца обычно решается с использованием динамического программирования подход .Программирование chrestomathy site Rossettacode.org имеет отличный пример на Python о том, как использовать DP для решения неограниченного ранца .По сути, вы сохраняете таблицу «мешков» для каждого уровня емкости (от нуля до максимума) и обновляете эту таблицу, как видите, если добавление текущего элемента в мешки, в которых есть место для элемента, приводит к получению лучшего (более ценного) мешкапока лучшая комбинация для этого мешка.
Но при создании всех возможных комбинаций вам лучше использовать свой собственный итеративный или рекурсивный подход.Здесь нет готовой функции itertools
, которую вы можете использовать, и использование функции обратного вызова не облегчит эту задачу.
Если взять лист из решения DP, итеративное решение будет использовать серию складывает мешков для каждой возможной общей емкости и копирует их в следующую кучу, когда вы добавляете больше таких предметов в каждый мешок, в котором есть место для них:
from itertools import repeat
def unbounded_knapsack_combos(input_list, target):
# collect piles of filled sacks, here each pile contains
# all sacks of the same capacity.
# A sacks consist of counts for each item in input_list, so
# sack[0] counts how many input_list[0] items were used.
# piles start empty, except for pile 0 (the empty sack pile)
piles = [[] for _ in range(0, target)]
piles[0] = [[0] * len(input_list)]
# process from smallest to biggest so we can avoid some work, like
# adding an item of size target - 1 on a pile that will never combine
# with larger items
size_idx = [(s, i) for i, (_, s) in enumerate(input_list) if s <= target]
for size, i in sorted(size_idx):
for s in range(size, target + 1 - size):
for sack in piles[s - size]:
new_sack = sack[:]
new_sack[i] += 1
piles[s].append(new_sack)
# Yield knapsacks that can be completed
for sack in piles[target - size]:
new_sack = sack[:]
new_sack[i] += 1
# turn the item counts in each sack in the target pile back into
# items from the input list
yield [item for i, c in enumerate(new_sack) if c
for item in repeat(input_list[i], c)]
# remove piles that we no longer need; any pile that won't
# fit larger items (multiple items can have the same size)
del piles[target - size + 1:]
Это решение используетitertools.repeat()
, но только потому, что удобно производить окончательный вывод из счетчиков в мешках.
Для вашего игрушечного примера, используя тот же формат (value, size)
, типичный для проблемы с рюкзаком, это дает:
>>> input_list = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6)]
>>> target_size = 4
>>> solution = unbounded_knapsack_combos(input_list, target_size)
>>> [[s for _, s in sack] for sack in solution]
[[1, 1, 1, 1], [1, 1, 2], [2, 2], [1, 3], [4]]
Это только когда-либо генерирует фактических жизнеспособных комбинаций и ничего более.
Функция отличается от полного решения тем, что здесь all сохраняются возможные комбинации, а не только одна комбинация с наилучшим значением для комбинированных предметов.И поскольку мы генерируем все комбинации, нет необходимости сортировать элементы по их соотношению значений, как это делает подход связанного DP (сортировка помогает избежать копирования слишком большого количества менее оптимальных решений в цикле).
Рекурсивная версия произведенная schwobaseggl по сути делает то же самое;создайте мешки для данной емкости, а рекурсивные вызовы добавляют больше элементов для следующей большей емкости.Мой просто оказывается итеративным, так что вы не столкнетесь с рекурсивными пределами Python, и он выдает результаты так, как находит их (как itertools
).Рекурсивная версия также вынуждена многократно объединять списки (операция O (N ^ 2) для N == глубины рекурсии), поэтому итеративный подход намного быстрее.