Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Coding · Dynamic programming

The unbounded knapsack

Lesson · 9 min read · Dynamic programming · Unbounded knapsack

Every item can be taken any number of times. One array, filled upwards, and the recurrence reads its own row.

Reuse changes one index

With unlimited copies, taking item i does not use it up: after taking it we may take it again. In the two-dimensional recurrence, "take" now stays in row i:

best(i, c) = max( best(i − 1, c), value[i] + best(i, c − weight[i]) )

Because the order of items no longer matters for an optimum, the item index can be dropped altogether: best(c) = max over items of value[i] + best(c − weight[i]), with best(0) = 0. Rod cutting (sell pieces of a rod at given prices) and fewest coins are the same recurrence with min instead of max.

Items 2 kg worth 3, 3 kg worth 5, 5 kg worth 9, any number of each, capacities 0 to 9.

  1. Step 1 / 10
    best
    00·1·2·3·4·5·6·7·8·9

    Base case: best[0] = 0.

  2. Step 2 / 10
    best
    0001·2·3·4·5·6·7·8·9

    Capacity 1: nothing fits; the best is 0.

  3. Step 3 / 10
    best
    000132·3·4·5·6·7·8·9

    Capacity 2: 3 + best[0]; the best is 3.

  4. Step 4 / 10
    best
    00013253·4·5·6·7·8·9

    Capacity 3: 3 + best[1], 5 + best[0]; the best is 5.

  5. Step 5 / 10
    best
    0001325364·5·6·7·8·9

    Capacity 4: 3 + best[2], 5 + best[1]; the best is 6.

  6. Step 6 / 10
    best
    000132536495·6·7·8·9

    Capacity 5: 3 + best[3], 5 + best[2], 9 + best[0]; the best is 9.

  7. Step 7 / 10
    best
    000132536495106·7·8·9

    Capacity 6: 3 + best[4], 5 + best[3], 9 + best[1]; the best is 10.

  8. Step 8 / 10
    best
    000132536495106127·8·9

    Capacity 7: 3 + best[5], 5 + best[4], 9 + best[2]; the best is 12.

  9. Step 9 / 10
    best
    000132536495106127148·9

    Capacity 8: 3 + best[6], 5 + best[5], 9 + best[3]; the best is 14.

  10. Step 10 / 10
    best
    000132536495106127148159

    Capacity 9: 3 + best[7], 5 + best[6], 9 + best[4]; the best is 15. best[9] = 15: for example 5 kg + 2 kg + 2 kg (9 + 3 + 3).

The loop direction is the whole difference

def unbounded_knapsack(weights, values, cap):
    best = [0] * (cap + 1)
    for w, v in zip(weights, values):
        for c in range(w, cap + 1):            # upwards: best[c - w] may already use this item
            best[c] = max(best[c], v + best[c - w])
    return best[cap]

Downwards: each item at most once (0/1). Upwards: each item any number of times (unbounded). Being able to say why is a common interview question.

Implementation

In six languages:

def knapsack_unbounded(weights, values, capacity):
    best = [0] * (capacity + 1)
    for w, v in zip(weights, values):
        for c in range(w, capacity + 1):       # upwards: the item may be reused
            best[c] = max(best[c], v + best[c - w])
    return best[capacity]

The same code is the reference solution to The unbounded knapsack, where it is compiled and run against the tests in all six languages.

TimeO(n · capacity)
SpaceO(capacity)