Skip to content

Commit cfd7841

Browse files
o000SAI000oalxkm
andauthored
Feat(Improved): Add 0/1 Knapsack Problem: Recursive and Tabulation (Bottom-Up DP) Implementations in Java along with their corresponding Tests (#6425)
* feat: Add 0/1 Knapsack and its tabulation implementation with their corresponding tests * feat: Add 0/1 Knapsack and its tabulation implementation with their corresponding tests * feat: Add 0/1 Knapsack and its tabulation implementation with their corresponding tests * Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests * Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests * Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests --------- Co-authored-by: Oleksandr Klymenko <alexanderklmn@gmail.com>
1 parent bbbc1dd commit cfd7841

File tree

4 files changed

+268
-0
lines changed

4 files changed

+268
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* The {@code KnapsackZeroOne} provides Recursive solution for the 0/1 Knapsack
5+
* problem. Solves by exploring all combinations of items using recursion. No
6+
* memoization or dynamic programming optimizations are applied.
7+
*
8+
* Time Complexity: O(2^n) — explores all subsets.
9+
* Space Complexity: O(n) — due to recursive call stack.
10+
*
11+
* Problem Reference: https://en.wikipedia.org/wiki/Knapsack_problem
12+
*/
13+
public final class KnapsackZeroOne {
14+
15+
private KnapsackZeroOne() {
16+
// Prevent instantiation
17+
}
18+
19+
/**
20+
* Solves the 0/1 Knapsack problem using recursion.
21+
*
22+
* @param values the array containing values of the items
23+
* @param weights the array containing weights of the items
24+
* @param capacity the total capacity of the knapsack
25+
* @param n the number of items
26+
* @return the maximum total value achievable within the given weight limit
27+
* @throws IllegalArgumentException if input arrays are null, empty, or
28+
* lengths mismatch
29+
*/
30+
public static int compute(final int[] values, final int[] weights, final int capacity, final int n) {
31+
if (values == null || weights == null) {
32+
throw new IllegalArgumentException("Input arrays cannot be null.");
33+
}
34+
if (values.length != weights.length) {
35+
throw new IllegalArgumentException("Value and weight arrays must be of the same length.");
36+
}
37+
if (capacity < 0 || n < 0) {
38+
throw new IllegalArgumentException("Invalid input: arrays must be non-empty and capacity/n "
39+
+ "non-negative.");
40+
}
41+
if (n == 0 || capacity == 0 || values.length == 0) {
42+
return 0;
43+
}
44+
45+
if (weights[n - 1] <= capacity) {
46+
final int include = values[n - 1] + compute(values, weights, capacity - weights[n - 1], n - 1);
47+
final int exclude = compute(values, weights, capacity, n - 1);
48+
return Math.max(include, exclude);
49+
} else {
50+
return compute(values, weights, capacity, n - 1);
51+
}
52+
}
53+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
/**
4+
* Tabulation (Bottom-Up) Solution for 0-1 Knapsack Problem.
5+
* This method uses dynamic programming to build up a solution iteratively,
6+
* filling a 2-D array where each entry dp[i][w] represents the maximum value
7+
* achievable with the first i items and a knapsack capacity of w.
8+
*
9+
* The tabulation approach is efficient because it avoids redundant calculations
10+
* by solving all subproblems in advance and storing their results, ensuring
11+
* each subproblem is solved only once. This is a key technique in dynamic programming,
12+
* making it possible to solve problems that would otherwise be infeasible due to
13+
* exponential time complexity in naive recursive solutions.
14+
*
15+
* Time Complexity: O(n * W), where n is the number of items and W is the knapsack capacity.
16+
* Space Complexity: O(n * W) for the DP table.
17+
*
18+
* For more information, see:
19+
* https://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming
20+
*/
21+
public final class KnapsackZeroOneTabulation {
22+
23+
private KnapsackZeroOneTabulation() {
24+
// Prevent instantiation
25+
}
26+
27+
/**
28+
* Solves the 0-1 Knapsack problem using the bottom-up tabulation technique.
29+
* @param values the values of the items
30+
* @param weights the weights of the items
31+
* @param capacity the total capacity of the knapsack
32+
* @param itemCount the number of items
33+
* @return the maximum value that can be put in the knapsack
34+
* @throws IllegalArgumentException if input arrays are null, of different lengths,or if capacity or itemCount is invalid
35+
*/
36+
public static int compute(final int[] values, final int[] weights, final int capacity, final int itemCount) {
37+
if (values == null || weights == null) {
38+
throw new IllegalArgumentException("Values and weights arrays must not be null.");
39+
}
40+
if (values.length != weights.length) {
41+
throw new IllegalArgumentException("Values and weights arrays must be non-null and of same length.");
42+
}
43+
if (capacity < 0) {
44+
throw new IllegalArgumentException("Capacity must not be negative.");
45+
}
46+
if (itemCount < 0 || itemCount > values.length) {
47+
throw new IllegalArgumentException("Item count must be between 0 and the length of the values array.");
48+
}
49+
50+
final int[][] dp = new int[itemCount + 1][capacity + 1];
51+
52+
for (int i = 1; i <= itemCount; i++) {
53+
final int currentValue = values[i - 1];
54+
final int currentWeight = weights[i - 1];
55+
56+
for (int w = 1; w <= capacity; w++) {
57+
if (currentWeight <= w) {
58+
final int includeItem = currentValue + dp[i - 1][w - currentWeight];
59+
final int excludeItem = dp[i - 1][w];
60+
dp[i][w] = Math.max(includeItem, excludeItem);
61+
} else {
62+
dp[i][w] = dp[i - 1][w];
63+
}
64+
}
65+
}
66+
67+
return dp[itemCount][capacity];
68+
}
69+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
public class KnapsackZeroOneTabulationTest {
9+
10+
@Test
11+
public void basicCheck() {
12+
int[] values = {60, 100, 120};
13+
int[] weights = {10, 20, 30};
14+
int capacity = 50;
15+
int itemCount = values.length;
16+
17+
int expected = 220; // Best choice: item 1 (100) and item 2 (120)
18+
int result = KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount);
19+
assertEquals(expected, result);
20+
}
21+
22+
@Test
23+
public void emptyKnapsack() {
24+
int[] values = {};
25+
int[] weights = {};
26+
int capacity = 50;
27+
int itemCount = 0;
28+
29+
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount));
30+
}
31+
32+
@Test
33+
public void zeroCapacity() {
34+
int[] values = {60, 100, 120};
35+
int[] weights = {10, 20, 30};
36+
int capacity = 0;
37+
int itemCount = values.length;
38+
39+
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount));
40+
}
41+
42+
@Test
43+
public void negativeCapacity() {
44+
int[] values = {10, 20, 30};
45+
int[] weights = {1, 1, 1};
46+
int capacity = -10;
47+
int itemCount = values.length;
48+
49+
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount));
50+
assertEquals("Capacity must not be negative.", exception.getMessage());
51+
}
52+
53+
@Test
54+
public void mismatchedLengths() {
55+
int[] values = {60, 100}; // Only 2 values
56+
int[] weights = {10, 20, 30}; // 3 weights
57+
int capacity = 50;
58+
int itemCount = 2; // Matches `values.length`
59+
60+
// You could either expect 0 or throw an IllegalArgumentException in your compute function
61+
assertThrows(IllegalArgumentException.class, () -> { KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount); });
62+
}
63+
64+
@Test
65+
public void nullInputs() {
66+
int[] weights = {1, 2, 3};
67+
int capacity = 10;
68+
int itemCount = 3;
69+
70+
IllegalArgumentException exception1 = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(null, weights, capacity, itemCount));
71+
assertEquals("Values and weights arrays must not be null.", exception1.getMessage());
72+
73+
int[] values = {1, 2, 3};
74+
75+
IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(values, null, capacity, itemCount));
76+
assertEquals("Values and weights arrays must not be null.", exception2.getMessage());
77+
}
78+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.thealgorithms.dynamicprogramming;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
class KnapsackZeroOneTest {
9+
10+
@Test
11+
void basicCheck() {
12+
int[] values = {60, 100, 120};
13+
int[] weights = {10, 20, 30};
14+
int capacity = 50;
15+
int expected = 220;
16+
17+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
18+
assertEquals(expected, result);
19+
}
20+
21+
@Test
22+
void zeroCapacity() {
23+
int[] values = {10, 20, 30};
24+
int[] weights = {1, 1, 1};
25+
int capacity = 0;
26+
27+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
28+
assertEquals(0, result);
29+
}
30+
31+
@Test
32+
void zeroItems() {
33+
int[] values = {};
34+
int[] weights = {};
35+
int capacity = 10;
36+
37+
int result = KnapsackZeroOne.compute(values, weights, capacity, 0);
38+
assertEquals(0, result);
39+
}
40+
41+
@Test
42+
void weightsExceedingCapacity() {
43+
int[] values = {10, 20};
44+
int[] weights = {100, 200};
45+
int capacity = 50;
46+
47+
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length);
48+
assertEquals(0, result);
49+
}
50+
51+
@Test
52+
void throwsOnNullArrays() {
53+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(null, new int[] {1}, 10, 1));
54+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {1}, null, 10, 1));
55+
}
56+
57+
@Test
58+
void throwsOnMismatchedArrayLengths() {
59+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10, 20}, new int[] {5}, 15, 2));
60+
}
61+
62+
@Test
63+
void throwsOnNegativeInputs() {
64+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, -1, 1));
65+
66+
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, 5, -1));
67+
}
68+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy