- to practice thinking about recursion,
- practice using two-dimensional arrays, and
- more practice with C++.

The knapsack problem can be solved by recursion. Suppose that our knapsack
has capacity *K* and that there are *n* items numbered 1 through
*n*. The *i*th item has weight *w _{i}* and value

- Suppose we have a knapsack with capacity
*K-w*. What is the maximum value of the items that we can put into such a knapsack (without exceeding its capacity), if we could only choose from items numbered 1 through_{n}*n*- 1? - Suppose we have a knapsack with capacity
*K*. What is the maximum value of the items that we can put into such a knapsack (without exceeding its capacity), if we could only choose from items numbered 1 through*n*-1?

So, how do we solve questions 1 and 2? The answer is, of course, "recursively". Questions 1 and 2 are instances of the knapsack problem, so we can use the same process to solve these questions. There are two base cases for this recursion. First, we might have a knapsack with zero capacity. In that case, we know that we cannot put any items in the knapsack. Second, we might have a list of zero items to consider. In this base case, we also know that we cannot put any items in the knapsack.This recursive algorithm can be slow because each call to a recursive function implementing this algorithm could generate two additional recursive calls. However, using a memoization table does speed up the algorithm.

This recursive strategy for the knapsack problem described above is also known as "dynamic programming". This terminology comes from the field of Operations Research. The word "programming" in "dynamic programming" is not used in the computer science sense and has nothing to do with writing code.

Item 1: weight = 5 value = 1 Item 2: weight = 3 value = 3 Item 3: weight = 1 value = 2 Item 4: weight = 4 value = 1 Item 5: weight = 3 value = 5Suppose that we take item 5. Since item 5 has weight 3, we have a remaining capacity of 3 in the knapsack. So, what is the best way to pack items 1 - 4 in the remaining capacity? Well, we can't take item 1 or item 4, since they have weight greater than 3. So, the best we can do is to take item 2 which has weight 3 and value 3. Thus,

Conversely, suppose that we don't take item 5. Since we did not use
any space in the knapsack, it still has capacity 6. The best way to pack
items 1-4 in the knapsack is to take items 2 and 3 (try all
possibilities). This gives us a total value of 5, *if we do not take item
5*.

Comparing the two alternatives, we conclude that we should take item 5 since that gives us a more valuable knapsack.

How would a memoization table help? Consider an example where the capacity
of the knapsack is 27 and the last 3 items have weights 2, 3 and 1. In one
sequence of choices, we might decide to take the item with weight 3 and
leave the other two behind. In another sequence of choices we might decide
to take the items with weight 1 and 2. In either case, we would want to
know the best way to pack the remaining *n* - 3 items in a
knapsack with capacity 24. Using a memoization table guarantees that we
would solve this subproblem only once. The next time we could simply look
up the answer. Using a memoization table greatly reduces the running time
of our recursive function.

Use the UNIX time command to obtain the running time of your program with and without a memoization table. You should notice a big difference in the running time when the number of items is between 15 and 20.

Your program should take a command line argument *n* which specifies
the number of items in the knapsack problem. For testing purposes, you
should generate your test data randomly using the drand48() and srand48()
functions. You should set the random seed either using command line input
or using the system clock, so each run of your program would work with
different data. For this project, use a knapsack with capacity of 0.25
*n ^{2}*. Each item should have a random

- C/C++ indexes an array with
*n*elements from 0 to*n*-1. If this is too confusing for you, just declare an array with*n*+1 elements and don't use the element with index 0.

- The recursive function that solves the knapsack problem should
return an answer that tells us how to pack the knapsack --- i.e., which
items to bring and which items to leave behind. The easiest way to do this
is to return an integer array with
*n*elements. The*i*th element of the array is 1 if we should take item*i*and 0 otherwise. For the recursive calls that only consider items 1 through some index*m*, it is simpler to have these functions also return an array with*n*elements. You can find suggested class declaration for this project in the file:/afs/umbc.edu/users/c/h/chang/pub/cs202/project3/knapsack.h You may change these declarations if you wish.

- When you initialize the memoization table, you need to use a loop and allocate memory for each row of the two-dimensional array.

For 10 extra points, implement this greedy strategy and convince yourself that it does not provide the optimum solution to the knapsack problem. Your submission should include several sample runs comparing the greedy algorithm and the dynamic programming algorithm.

As usual, extra credit is "all or nothing" --- you either get all 10 points or none of it.

Last Modified: 19 Oct 1998 11:10:18 EDT by Richard Chang

Back up to Fall 1998 CMSC 202 Section Homepage