Skip to content

Commit b9ce42a

Browse files
authored
Add approaches for Knapsack (#2791)
* Add approaches for Knapsack * Fix formatting in introduction.md
1 parent ce49275 commit b9ce42a

File tree

6 files changed

+217
-0
lines changed

6 files changed

+217
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"introduction": {
3+
"authors": [
4+
"kahgoh"
5+
]
6+
},
7+
"approaches": [
8+
{
9+
"uuid": "bf439128-7610-4959-b9f6-bd033c979d8e",
10+
"slug": "recursive",
11+
"title": "Recursive",
12+
"blurb": "Use recursion to work out whether each item should be added.",
13+
"authors": [
14+
"kahgoh"
15+
]
16+
},
17+
{
18+
"uuid": "c51300cb-94f8-4a33-8f64-56deebaa1a22",
19+
"slug": "dynamic-programming",
20+
"title": "Dynamic Programming",
21+
"blurb": "Build up a table of maximum values.",
22+
"authors": [
23+
"kahgoh"
24+
]
25+
}
26+
]
27+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Dynamic Programming
2+
3+
```java
4+
import java.util.List;
5+
6+
class Knapsack {
7+
8+
int maximumValue(int maxWeight, List<Item> items) {
9+
int [][]maxValues = new int[maxWeight + 1][items.size() + 1];
10+
11+
for (int nItems = 0; nItems <= items.size(); nItems++) {
12+
maxValues[0][nItems] = 0;
13+
}
14+
15+
for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
16+
Item item = items.get(itemIndex);
17+
18+
for (int capacity = 0; capacity <= maxWeight; capacity++) {
19+
if (capacity < item.weight) {
20+
maxValues[capacity][itemIndex + 1] = maxValues[capacity][itemIndex];
21+
} else {
22+
int valueWith = maxValues[capacity - item.weight][itemIndex] + item.value;
23+
int valueWithout = maxValues[capacity][itemIndex];
24+
maxValues[capacity][itemIndex + 1] = Math.max(valueWith, valueWithout);
25+
}
26+
}
27+
}
28+
29+
return maxValues[maxWeight][items.size()];
30+
}
31+
}
32+
```
33+
34+
This approach works by building a table of maximum total values.
35+
The table is represented by the 2D [array][array] `maxValues`.
36+
The table's axes are the capacity (starting from 0 and going up to the `maxWeight`) and the number of items (starting from 0 and going up to length of the `items` list).
37+
The number of items always count from the first item.
38+
1 is added to the `maxWeight` and the number of items to allow space for 0 weight and 0 items.
39+
40+
The first [for loop][for-loop] fills table for when there are no items available.
41+
In this case, the maximum value must be 0 because there are no items.
42+
43+
The next for loops fills the rest of the table.
44+
The outer for loop iterates over each item.
45+
The inner loop fills calculates and stores the maximum value for the item and capacity.
46+
When storing, 1 is added to the `itemIndex` on the left hand side because the first item in the `maxValues` array represents no items.
47+
If the knapsack doesn't have enough capacity for the item, then maximum value is same as the maximum value _without_ the item.
48+
The maximum value without the item is obtained simply by looking up the value for the previous item and capacity in the table (i.e `maxValues[capacity][itemIndex]`).
49+
50+
Otherwise, the maximum value is the greater of the value with and without the item.
51+
The maximum value _with_ (`valueWith`) the item is obtained by, first, looking up the maximum value _without_ the item and enough capacity for the item (i.e `capacity - item.weight`).
52+
The item's value (`item.value`) is then added to get the maximum value for the get the maximum value _with_ the item.
53+
54+
After the table is completely filled, the maximum value is obtained from the table.
55+
56+
[array]: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
57+
[for-loop]: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
2+
for (int capacity = 0; capacity <= maxWeight; capacity++) {
3+
int valueWith = maxValues[capacity - item.weight][itemIndex] + item.value;
4+
int valueWithout = maxValues[capacity][itemIndex];
5+
maxValues[capacity][itemIndex + 1] = Math.max(valueWith, valueWithout);
6+
}
7+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Introduction
2+
3+
There are a couple of approaches to solve Knapsack.
4+
You can recursively determine whether each item should be added to the knapsack.
5+
Or, you can solve it iteratively using a dynamic programming approach.
6+
7+
## General guidance
8+
9+
The key to solving Knapsack is to determine whether each item should be added to the knapsack or not.
10+
An item should be added only if:
11+
12+
1. There is enough capacity to add the item and:
13+
2. It increases the total value.
14+
15+
## Approach: Recursive
16+
17+
```java
18+
import java.util.List;
19+
20+
class Knapsack {
21+
22+
int maximumValue(int maxWeight, List<Item> items) {
23+
if (items.isEmpty()) {
24+
return 0;
25+
}
26+
27+
List<Item> remainingItems = items.subList(1, items.size());
28+
int valueWithout = maximumValue(maxWeight, remainingItems);
29+
30+
Item item = items.get(0);
31+
if (item.weight > maxWeight) {
32+
return valueWithout;
33+
}
34+
35+
int valueWith = maximumValue(maxWeight - item.weight, remainingItems) + item.value;
36+
return Math.max(valueWithout, valueWith);
37+
}
38+
}
39+
```
40+
41+
For more information, check the [Recursive approach][approach-recursive].
42+
43+
## Approach: Dynamic programming
44+
45+
```java
46+
import java.util.List;
47+
48+
class Knapsack {
49+
50+
int maximumValue(int maxWeight, List<Item> items) {
51+
int [][]maxValues = new int[maxWeight + 1][items.size() + 1];
52+
53+
for (int nItems = 0; nItems <= items.size(); nItems++) {
54+
maxValues[0][nItems] = 0;
55+
}
56+
57+
for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
58+
Item item = items.get(itemIndex);
59+
60+
for (int capacity = 0; capacity <= maxWeight; capacity++) {
61+
if (capacity < item.weight) {
62+
maxValues[capacity][itemIndex + 1] = maxValues[capacity][itemIndex];
63+
} else {
64+
int valueWith = maxValues[capacity - item.weight][itemIndex] + item.value;
65+
int valueWithout = maxValues[capacity][itemIndex];
66+
maxValues[capacity][itemIndex + 1] = Math.max(valueWith, valueWithout);
67+
}
68+
}
69+
}
70+
71+
return maxValues[maxWeight][items.size()];
72+
}
73+
}
74+
```
75+
76+
For more information, check the [dynamic programming approach][approach-dynamic].
77+
78+
## Which approach to use?
79+
80+
The recursive approach is inefficient because it recalculates the maximum value for some item combinations a number of times.
81+
The dynamic programming approach avoids this by storing them in an [array][array].
82+
In addition, the dynamic programming approach is also an iterative approach and avoids overhead of making method calls.
83+
84+
[approach-recursive]: https://exercism.org/tracks/java/exercises/knapsack/approaches/recursive
85+
[approach-dynamic]: https://exercism.org/tracks/java/exercises/knapsack/approaches/dynamic-programming
86+
[array]: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Recursive
2+
3+
```java
4+
import java.util.List;
5+
6+
class Knapsack {
7+
8+
int maximumValue(int maxWeight, List<Item> items) {
9+
if (items.isEmpty()) {
10+
return 0;
11+
}
12+
13+
List<Item> remainingItems = items.subList(1, items.size());
14+
int valueWithout = maximumValue(maxWeight, remainingItems);
15+
16+
Item item = items.get(0);
17+
if (item.weight > maxWeight) {
18+
return valueWithout;
19+
}
20+
21+
int valueWith = maximumValue(maxWeight - item.weight, remainingItems) + item.value;
22+
return Math.max(valueWithout, valueWith);
23+
}
24+
}
25+
```
26+
27+
The approach uses recursion to find the maximum value with and without each item.
28+
Each iteration works on the first item in the list (`items.get(0)`).
29+
If the list is empty, the maximum value must be `0`.
30+
Otherwise, a recursive call is made to work out the maximum value _without_ the first item (`maximumValue(maxWeight, remainingItems)`).
31+
32+
If the there is not enough capacity to add the current item, the maximum value is the same as the maximum value without the first item.
33+
Otherwise, another recursive call is made to calculate the maximum value _with_ the first item.
34+
Since the item is included, the recursive call is done with the item's weight subtracted from the maximum weight (`maxWeight - item.weight`) and its value (`item.value`) is added.
35+
The maximum value is greater of these two values.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
int maximumValue(int maxWeight, List<Item> items) {
2+
int valueWithout = maximumValue(maxWeight, remainingItems);
3+
int valueWith = maximumValue(maxWeight - item.weight, remainingItems) + item.value;
4+
return Math.max(valueWithout, valueWith);
5+
}

0 commit comments

Comments
 (0)