Skip to content

Instantly share code, notes, and snippets.

@boechat107
Last active March 17, 2022 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save boechat107/0937c28acf55ba1234a4c26488ec36a3 to your computer and use it in GitHub Desktop.
Save boechat107/0937c28acf55ba1234a4c26488ec36a3 to your computer and use it in GitHub Desktop.
Multiple knapsacks problem
"""
Knapsacks have weight capacity; items have weight and value.
We want to find the allocation of items which maximizes the total
carried value.
"""
from typing import List, Set, Tuple
def remove_subsets(combinations: List[Set[int]]) -> List[Set[int]]:
"""
Removes sets that are subset of any other set in the given list.
"""
scombs = sorted(combinations, key=len)
output = []
for i in range(len(scombs)):
comb = scombs[i]
if any(comb.issubset(scombs[j]) for j in range(i + 1, len(scombs))):
continue
else:
output.append(comb)
return output
def items_combinations(knapsack_capacity: int, items: Set[int],
items_weights: List[int]) -> List[Set[int]]:
"""
Given a knapsack and a set of items, returns all possible combinations of items allocation.
Combinations that don't use the maximum knapsack's capacity are ignored.
"""
def recur(knapsack: Set[int], remaining_items: Set[int]):
weight = sum(items_weights[i] for i in knapsack)
if len(remaining_items) == 0:
if weight <= knapsack_capacity and len(knapsack) > 0:
return [knapsack]
else:
return []
items = remaining_items.copy()
i = items.pop()
donttake = recur(knapsack, items)
if weight + items_weights[i] <= knapsack_capacity:
take = recur(knapsack.union([i]), items)
return donttake + take
else:
return donttake
return remove_subsets(recur(set(), items))
def main(knapsacks_capacities: List[int],
items_weights: List[int],
items_values: List[int]) -> Tuple[int, List[Set[int]]]:
"""
Returns the maximum value allocated in the list of knapsacks.
Knapsacks are represented as set of items.
* knapsacks_capacities: 1 x N
* items_weights: 1 x M
* items_values: 1 x M
* output: (total value, items in each knapsack)
"""
N = len(knapsacks_capacities)
M = len(items_values)
def total_value(knapsacks: List[Set[int]]) -> int:
return sum(sum(items_values[i] for i in ksack) for ksack in knapsacks)
def recur(remaining_items: Set[int], knapsacks: List[Set[int]],
knapsack_idx: int) -> Tuple[int, List[Set[int]]]:
"""
* output: the maximum value of the best possible allocation
"""
if knapsack_idx >= N:
value = total_value(knapsacks)
return value, knapsacks
combinations = items_combinations(knapsacks_capacities[knapsack_idx],
remaining_items, items_weights)
print("Combinations for sack {} and items {}: {}".format(
knapsack_idx, remaining_items, combinations))
if len(combinations) == 0:
return recur(remaining_items, knapsacks + [{}], knapsack_idx + 1)
max_val, best_knapsacks = max((recur(
remaining_items.difference(items), knapsacks + [items],
knapsack_idx + 1) for items in combinations),
key=lambda t: t[0])
print("Best combinations for knapsack {}: {}".format(
knapsack_idx, (max_val, best_knapsacks)))
return max_val, best_knapsacks
items = set(range(M))
return recur(items, [], 0)
#### TESTS ####
#print(items_combinations(10, set(range(4)), [3,6,1,2]))
# [{3}, {2, 3}, {1, 3}, {1, 2, 3}, {0, 3}, {0, 2, 3}, {0, 1}, {0, 1, 2}]
# [{1, 2, 3}, {0, 2, 3}, {0, 1, 2}] -> without subsets
print(main([10, 2], [3, 6, 1, 2], [5, 3, 2, 7]))
# (17, [{0, 1, 2}, {3}])
print(main([10, 5, 30, 15, 30], [13,8,12,10,13,6,11,9,12,7], [2,9,1,2,9,5,6,13,1,1]))
# (47, [{7}, {}, {0, 4}, {9, 5}, {1, 3, 6}])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment