Given the weights and profits of ‘N’ items, put it in a knapsack of capacity ‘C’ such that we get the max profit.
Only difference between 0/1Knapsack and Unbounded knapsack is that here we are allowed to use an unlimited quantity of an item.
Example:
items: {Apple, Orange, Melon}
weights: {1, 2, 3}
profits: {15, 20, 50}
capacity: 5
Different Profit Combinations:
5 Apples (total weight 5) => 75 profit
1 Apple + 2 Oranges (total weight 5) => 55 profit
3 Apples + 1 Orange (total weight 5) => 65 profit
2 Apples + 1 Melon (total weight 5) => 80 profit
1 Orange + 1 Melon (total weight 5) => 70 profit
Best Profit Combination : 2 Apples + 1 Melon with 80 profit.
Given the weights w and profits p of ‘N’ items, need to find a subset of these items which will give max profit with contstraint that their cumulative sum should not be greater than total knapsack capacity C.
Each item can only be selected multiple times.
Code:
def unbounded_knapsack_recursion(weights, profits, capacity):
item_index = 0
return unbounded_knapsack_recursion_util(weights, profits, capacity, item_index)
def unbounded_knapsack_recursion_util(weights, profits, capacity, item_index):
if (capacity <= 0 or item_index >= len(profits)):
return 0
current_included_profit, current_excluded_profit = 0, 0
if(weights[item_index] <= capacity):
current_included_profit = profits[item_index] + \
unbounded_knapsack_recursion_util(weights, profits, capacity - weights[item_index], item_index)
current_excluded_profit = unbounded_knapsack_recursion_util(weights, profits, capacity, item_index + 1)
return max(current_included_profit, current_excluded_profit)
print("Recursive Method :")
print(unbounded_knapsack_recursion([1, 3, 4, 5], [15, 50, 60, 90], 8))
print(unbounded_knapsack_recursion([1, 3, 4, 5], [15, 50, 60, 90], 6))
Output:
140
105
Complexity:
Code:
def unbounded_knapsack_dp_memoization(weights, profits, capacity):
memory = [[None] * (capacity + 1) for i in range(len(weights))]
item_index = 0
return unbounded_knapsack_dp_memoization_util(weights, profits, capacity, item_index, memory)
def unbounded_knapsack_dp_memoization_util(weights, profits, capacity, item_index, memory):
if (capacity <= 0 or item_index >= len(profits)):
return 0
if (memory[item_index][capacity]):
return memory[item_index][capacity]
current_included_profit, current_excluded_profit = 0, 0
if(weights[item_index] <= capacity):
current_included_profit = profits[item_index] + \
unbounded_knapsack_dp_memoization_util(weights, profits, capacity - weights[item_index], item_index, memory)
current_excluded_profit = unbounded_knapsack_dp_memoization_util(weights, profits, capacity, item_index + 1, memory)
memory[item_index][capacity] = max(current_included_profit, current_excluded_profit)
return memory[item_index][capacity]
print("\nDP -> Recursion + Memoization Method :")
print(unbounded_knapsack_dp_memoization([1, 3, 4, 5], [15, 50, 60, 90], 8))
print(unbounded_knapsack_dp_memoization([1, 3, 4, 5], [15, 50, 60, 90], 6))
Output:
140
105
Complexity:
Table Filling Formula:
table[index][c] = max(table[index-1][c], profits[item_index] + table[index][c-weights[item_index]])
Tabulation:
Final Table:
Code:
def unbounded_knapsack_dp_tabulation(weights, profits, capacity):
n = len(profits)
table = [[0] * (capacity + 1) for i in range(n)]
# When capacity is 0, none of the item can be selected
for i in range(n):
table[i][0] = 0
# When only 1 item is considered, get the profits using that item for every capapcity 'c'
for c in range(capacity + 1):
table[0][c] = (c // weights[0]) * profits[0]
# For each item, check for every possible capacities
for i in range(1, n):
for c in range(1, capacity + 1):
if (weights[i] <= c):
table[i][c] = max(table[i - 1][c], profits[i] + table[i][c - weights[i]])
else:
table[i][c] = table[i - 1][c]
return table[i][c]
print("\nDP -> Iteration + Tabulation Method :")
print(unbounded_knapsack_dp_tabulation([1, 3, 4, 5], [15, 50, 60, 90], 8))
print(unbounded_knapsack_dp_tabulation([1, 3, 4, 5], [15, 50, 60, 90], 6))
Output:
DP -> Iteration + Tabulation Method :
140
105
Complexity:
Code:
def unbounded_knapsack_dp_tabulation(weights, profits, capacity):
n = len(profits)
table = [[0] * (capacity + 1) for i in range(n)]
# When capacity is 0, none of the item can be selected
for i in range(n):
table[i][0] = 0
# When only 1 item is considered, get the profits using that item for every capapcity 'c'
for c in range(capacity + 1):
table[0][c] = (c // weights[0]) * profits[0]
# For each item, check for every possible capacities
for i in range(1, n):
for c in range(1, capacity + 1):
if (weights[i] <= c):
table[i][c] = max(table[i - 1][c], profits[i] + table[i][c - weights[i]])
else:
table[i][c] = table[i - 1][c]
pick_selected_items(table, weights, profits)
return table[i][c]
def pick_selected_items(table, weights, profits):
i, c = len(table)-1, len(table[0])-1
total_profit = table[i][c]
print("Selected items are : ")
while (i-1 >= 0 and table[i][c] >= 0):
if (table[i][c] != table[i - 1][c]):
print(f"item - {i+1} with weight : {weights[i]} and profit : {profits[i]}")
c -= weights[i]
total_profit -= profits[i]
else:
i -= 1
while (total_profit > 0):
print(f"item - {1} with weight : {weights[0]} and profit : {profits[0]}")
total_profit -= profits[0]
print("\nDP -> Iteration + Tabulation Method :")
print(unbounded_knapsack_dp_tabulation([1, 3, 4, 5], [15, 50, 60, 90], 8))
print(unbounded_knapsack_dp_tabulation([1, 3, 4, 5], [15, 50, 60, 90], 6))
Output:
Selected items are :
item - 4 with weight : 5 and profit : 90
item - 2 with weight : 3 and profit : 50
140
Selected items are :
item - 4 with weight : 5 and profit : 90
item - 1 with weight : 1 and profit : 15
105
← Previous: 0/1 Knapsack Pattern
Next: Palindromic Subsequence Pattern →