...

/

0/1 Knapsack (medium)

0/1 Knapsack (medium)

Dry-run templates

This is the implementation of the solution provided for the problem posed in the 0/1 Knapsack (medium) lesson using Top-down Dynamic Programming with Memoization appraoch:

Press + to interact
Python 3.5
def solve_knapsack(profits, weights, capacity):
# create a two dimensional array for Memoization, each element is initialized to '-1'
dp = [[-1 for x in range(capacity+1)] for y in range(len(profits))]
return knapsack_recursive(dp, profits, weights, capacity, 0)
def knapsack_recursive(dp, profits, weights, capacity, currentIndex):
# base checks
if capacity <= 0 or currentIndex >= len(profits):
return 0
# if we have already solved a similar problem, return the result from memory
if dp[currentIndex][capacity] != -1:
return dp[currentIndex][capacity]
# recursive call after choosing the element at the currentIndex
# if the weight of the element at currentIndex exceeds the capacity, we
# shouldn't process this
profit1 = 0
if weights[currentIndex] <= capacity:
profit1 = profits[currentIndex] + knapsack_recursive(
dp, profits, weights, capacity - weights[currentIndex], currentIndex + 1)
# recursive call after excluding the element at the currentIndex
profit2 = knapsack_recursive(
dp, profits, weights, capacity, currentIndex + 1)
dp[currentIndex][capacity] = max(profit1, profit2)
return dp[currentIndex][capacity]
def main():
print(solve_knapsack([1, 6, 10, 16], [1, 2, 3, 5], 7))
print(solve_knapsack([1, 6, 10, 16], [1, 2, 3, 5], 6))
main()

Students may be encouraged to run through the provided solution with the following sample inputs:

Sample input #1

profits:  [1, 2, 5, 7]  weights:  [1, 2, 3, 4]  capacity:  5

Recursive Call

Line No.

currentIndex

capacity

profit1

profit2


4

0

5



knapsack_recursive

--------------------------

Computing profit1 at index 1

22

1

4

-

-

knapsack_recursive

--------------------------

Computing profit1 at index 2

22

2

2



Got profit 1 at index 2

22

2

2

0

-

knapsack_recursive

--------------------------

Computing profit1 at index 3

22

3

2



Got profit 1 at index 3

22

3

2

0

-

knapsack_recursive

--------------------------

Computing profit2 at index 4

26

4

2



knapsack_recursive

--------------------------

Computing profit2 at index 3


26

3

2

0

0

knapsack_recursive

--------------------------

Computing profit2 at index 2

26

2

2

0

0

Got profit1 at index 1


22

1

4

2

-

knapsack_recursive

--------------------------

Computing profit2 at index 2


26

2

4



knapsack_recursive

--------------------------

Computing profit1 at index 3


22

3

1



Got profit1 at index 3


22

3

1

0


knapsack_recursive

--------------------------

Computing profit2 at index 4

26

4

1

0


Got profit2 at index 3

26

3

1

0

0

Got profit1 at index 2

22

2

4

5


knapsack_recursive

--------------------------

Computing profit1 at index 3


22

3

4



knapsack_recursive

--------------------------

Computing profit1 at index 4


22

4

0

0


Got profit1 at index 3

22

3

4

7


knapsack_recursive

--------------------------

Computing profit2 at index 4


26

4

4



Got profit2 at index 3

26

3

4

7

0

Got profit2 at index 2

26

2

4

5

7

Got profit2 at index 1

26

1

4

2

7

Got profit1 at index 0

22

0

5

8


knapsack_recursive

--------------------------

Computing profit2 at index 1


26

1

5



knapsack_recursive

--------------------------

Computing profit2 at index 2


26

2

3



knapsack_recursive

--------------------------

Computing profit2 at index 3

26

3

0



Got profit1 at index 2

22

2

3

5


knapsack_recursive

--------------------------

Computing profit2 at index 3

26

3

3



Got profit1 at index 3

26

3

3

0


knapsack_recursive

--------------------------

Computing profit2 at index 4

26

4

3



Got profit2 at index 3

26

3

3

0

0

Got profit2 at index 2

26

2

3

5

0

Got profit1 at index1

22

1

5

7


knapsack_recursive

--------------------------

Computing profit2 at index 1

26

1

5

7


knapsack_recursive

--------------------------

Computing profit2 at index 2

26

2

5



knapsack_recursive

--------------------------

Computing profit1 at index 2


22

2

2



Got profit1 at index 2

22

2

2

5


knapsack_recursive

--------------------------

Computing profit2 at index 3

26

3

5



knapsack_recursive

--------------------------

Computing profit2 at index 4

26

4

1



Got profit 1 at index 3

22

3

5

7


knapsack_recursive

--------------------------

Computing profit2 at index 4

26

4

5



Got profit2 at index 3

26

3

5

7

0

Got profit2 at index 2

26

2

2

5

7

Got profit2 at index 1

26

1

5

7

7

Got profit2 at index 2

26

0

5

8

7

Sample input #2

Students may be encouraged to complete the dry-run with this input:

profits:  [1, 2, 3]  weights:  [1, 2, 3]  capacity:  4

Recursive Call

Line No.

currentIndex

capacity

profit1

profit2

-

4

0

4

-

-

knapsack_recursive

--------------------------

Computing profit1 at index 0

22

1

3

-

-

knapsack_recursive

--------------------------

Computing profit1 at index 1

22

2

1

-

-

...

...

...

...

...

...

Step-by-step solution construction

We have three main parts of the solution:

  1. Writing the base conditions for the recursive code
  2. Recursively computing profit in two alternative ways:
  • While including the profit of the current item
  • While excluding the profit of the current item
  1. Utilizing the profits computed earlier for a given capacity and weight.

In the code below, on lines 40-42, we define the base conditions:

  • There is still capacity in the knapsack
  • The capacity will eventually reach a negative value as we will be subtracting weights[current_index] from capacity at each recursive call.
  • There are items still left to consider

We will be calling the function recursively by incrementing the current_index, so we will eventually reach the end of the profits array.

On lines 45-47, we implement the memoization check: if we have already computed the profit for the given current_index and capacity, we simply return the value stored in the matrix dp. This works because, at the end of each recursive call, we store the profit for the input current_index and capacity (see line 62).”

On lines 53-57, we start to implement the second part of the solution:

  • If the weight of the current item is less than the remaining capacity of the knapsack, we issue the recursive command to calculate the profit with the current item included (lines 56-57). Note that in making the call, we reduce the capacity of the knapsack by the weight of the current item (capacity - weights[current_index]) and move to the next item in the list (current_index + 1).

Note: The function print_matrix has been added to print the matrix dp that stores the profits already calculated. This function does not contribute to the solution itself.

Press + to interact
Python 3.5
def solve_knapsack(profits, weights, capacity):
# create a two dimensional array for Memoization, each element is initialized to '-1'
dp = [[-1 for x in range(capacity+1)] for y in range(len(profits))]
print("Initialized a two dimensional array dp: ")
print_matrix(dp,0)
result = knapsack_recursive(dp, profits, weights, capacity, 0)
return result
def print_matrix(dp,current_index):
print()
print("\t"*current_index," ",end=" ")
for k in range(len(dp[0])):
print(k,end=" ")
print()
print("\t"*current_index,"-"*40)
for i in range(len(dp)):
print("\t"*current_index,i,end= " | ")
for j in range(len(dp[i])):
if(j == len(dp[i])-1):
if(dp[i][j]<0 or dp[i][j]>9):
print(dp[i][j])
else:
print ("",dp[i][j])
else:
if(dp[i][j]<0 or dp[i][j]>9):
print(dp[i][j],end=" ")
else:
print ("",dp[i][j],end=" ")
# print("\n")
def knapsack_recursive(dp, profits, weights, capacity, current_index):
print("\t"*current_index, "Method knapsack_recursive called with parameters:",end=" ")
print("current_index: ",current_index,end=" ")
print(" ","capacity: ",capacity)
# base checks
if capacity <= 0 or current_index >= len(profits):
print("\t"*current_index, "failed base checks")
return 0
# if we have already solved a similar problem, return the result from memory
if dp[current_index][capacity] != -1:
print("\t"*current_index,"We already know the profit:", dp[current_index][capacity])
return dp[current_index][capacity]
# recursive call after choosing the element at the current_index
# if the weight of the element at current_index exceeds the capacity, we
# shouldn't process this
profit1 = 0
if weights[current_index] <= capacity:
print("\t"*current_index, " Computing profit while including item at index", current_index)
#print("\t"*current_index,"profits[current_index]: ",profits[current_index])
profit1 = profits[current_index] + knapsack_recursive(
dp, profits, weights, capacity - weights[current_index], current_index + 1)
print("\t"*current_index, " Profit when capacity is", capacity, "and item at index", current_index, "is included:", profit1)
else:
print("\t"*current_index, " Current item is too heavy (weight: ", weights[current_index], ") for the knapsack. Hence the profit is 0.", sep="")
dp[current_index][capacity] = profit1
print("\t"*current_index,"dp, after updating: ", end="")
print_matrix(dp, current_index)
return dp[current_index][capacity]
def main():
knapsacks = [[[1, 2, 5, 7], [1, 2, 3, 4], 5],[[1,2,3],[1,2,3],4], [[1, 6, 10, 16], [1, 2, 3, 5], 7]]
for knapsack in knapsacks:
print("Solving for the following parameters: \nProfits: ",knapsack[0]," Weights: ",knapsack[1]," Capacity: ",knapsack[2])
result = solve_knapsack(knapsack[0], knapsack[1], knapsack[2])
print("\nMaximum profit: ",result)
print("-"*100+"\n")
main()

Clearly, our solution is not complete and we are not finding the combinations of items that give us the most profit. So, let’s complete the second part of the solution:

  • Computing the profit while excluding the current item. This is done through a second recursive call (see lines 64–65) where the capacity of the knapsack remains unchanged, though we do still move to the next item.

Since we have two profits, profit1 and profit2, arising from two mutually exclusive possibilities, we will choose the greater of the two and update the dp matrix (line 73).

Press + to interact
Python 3.5
def solve_knapsack(profits, weights, capacity):
# create a two dimensional array for Memoization, each element is initialized to '-1'
dp = [[-1 for x in range(capacity+1)] for y in range(len(profits))]
print("Initialized a two dimensional array dp: ")
print_matrix(dp,0)
result = knapsack_recursive(dp, profits, weights, capacity, 0)
return result
def print_matrix(dp,current_index):
print()
print("\t"*current_index," ",end=" ")
for k in range(len(dp[0])):
print(k,end=" ")
print()
print("\t"*current_index,"-"*40)
for i in range(len(dp)):
print("\t"*current_index,i,end= " | ")
for j in range(len(dp[i])):
if(j == len(dp[i])-1):
if(dp[i][j]<0 or dp[i][j]>9):
print(dp[i][j])
else:
print ("",dp[i][j])
else:
if(dp[i][j]<0 or dp[i][j]>9):
print(dp[i][j],end=" ")
else:
print ("",dp[i][j],end=" ")
# print("\n")
def knapsack_recursive(dp, profits, weights, capacity, current_index):
print("\t"*current_index,"Method knapsack_recursive called with parameters:",end=" ")
print("current_index: ",current_index,end=" ")
print(" ","capacity: ",capacity)
# base checks
if capacity <= 0 or current_index >= len(profits):
print("\t"*current_index, "failed base checks")
return 0
# if we have already solved a similar problem, return the result from memory
if dp[current_index][capacity] != -1:
print("\t"*current_index,"We already know the profit:", dp[current_index][capacity])
return dp[current_index][capacity]
# recursive call after choosing the element at the current_index
# if the weight of the element at current_index exceeds the capacity, we
# shouldn't process this
profit1 = 0
if weights[current_index] <= capacity:
print("\t"*current_index, " Computing profit while including item at index", current_index)
#print("\t"*current_index,"profits[current_index]: ",profits[current_index])
profit1 = profits[current_index] + knapsack_recursive(
dp, profits, weights, capacity - weights[current_index], current_index + 1)
print("\t"*current_index, " Profit when capacity is", capacity, "and item at index", current_index, "is included:", profit1)
else:
print("\t"*current_index, " Current item is too heavy (weight: ", weights[current_index], ") for the knapsack. Hence the profit from it is 0.", sep="")
# recursive call after excluding the element at the current_index
print("\t"*current_index," Computing profit while excluding item at index", current_index)
profit2 = knapsack_recursive(
dp, profits, weights, capacity, current_index + 1)
print("\t"*current_index, " Profit when capacity is", capacity, "and item at index", current_index, "is excluded:", profit2)
in_ex = ""
if profit1 > profit2:
in_ex = "in"
else:
in_ex = "ex"
print("\t"*current_index, " With capacity at ", capacity, ", we make more profit by ", in_ex, "cluding the item at index ", current_index, sep="")
dp[current_index][capacity] = max(profit1, profit2)
print("\t"*current_index,"dp, after updating: ")
print_matrix(dp, current_index)
return dp[current_index][capacity]
def main():
knapsacks = [[[1, 2, 5, 7], [1, 2, 3, 4], 5],[[1,2,3],[1,2,3],4], [[1, 6, 10, 16], [1, 2, 3, 5], 7]]
for knapsack in knapsacks:
print("Solving for the following parameters: \nProfits: ",knapsack[0]," Weights: ",knapsack[1]," Capacity: ",knapsack[2])
result = solve_knapsack(knapsack[0], knapsack[1], knapsack[2])
print("\nMaximum profit: ",result)
print("-"*100+"\n")
main()