-
Notifications
You must be signed in to change notification settings - Fork 20.1k
feat: Add 0/1 Knapsack Problem: Recursive and Tabulation (Bottom-Up DP) Implementations in Java along with their corresponding Tests #6421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3b8c5df
8a8d1e2
36fd4b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
/** | ||
* The {@code ZeroOneKnapsack} class provides a method to solve the classic 0/1 Knapsack problem. | ||
* It returns the maximum value that can be obtained by selecting items within the weight limit. | ||
* | ||
* Problem Description: Given weights and values of n items, put these items in a knapsack of capacity W | ||
* such that the total value is maximized. You cannot break an item, either pick the complete item or don't pick it. | ||
* | ||
* https://en.wikipedia.org/wiki/Knapsack_problem | ||
*/ | ||
public final class ZeroOneKnapsack { | ||
private ZeroOneKnapsack() { | ||
} | ||
|
||
/** | ||
* Solves the 0/1 Knapsack problem using recursion. | ||
* | ||
* @param values the array containing values of the items | ||
* @param weights the array containing weights of the items | ||
* @param capacity the total capacity of the knapsack | ||
* @param n the number of items | ||
* @return the maximum total value achievable within the given weight limit | ||
*/ | ||
public static int compute(int[] values, int[] weights, int capacity, int n) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making some parameters final is considered good practice. It makes sense to add input validation here also. |
||
if (n == 0 || capacity == 0) { | ||
return 0; | ||
} | ||
|
||
if (weights[n - 1] <= capacity) { | ||
int include = values[n - 1] + compute(values, weights, capacity - weights[n - 1], n - 1); | ||
int exclude = compute(values, weights, capacity, n - 1); | ||
return Math.max(include, exclude); | ||
} else { | ||
return compute(values, weights, capacity, n - 1); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
/** | ||
* The {@code ZeroOneKnapsackTab} class provides a method to solve the 0-1 Knapsack problem | ||
* using dynamic programming (tabulation approach). | ||
* | ||
* <p>0-1 Knapsack Problem - | ||
* Given weights and values of n items, and a maximum weight W, | ||
* determine the maximum total value of items that can be included in the knapsack | ||
* such that their total weight does not exceed W. Each item can be picked only once. | ||
* | ||
* Problem Link: https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/ | ||
*/ | ||
public final class ZeroOneKnapsackTab { | ||
|
||
private ZeroOneKnapsackTab() { | ||
// prevent instantiation | ||
} | ||
|
||
/** | ||
* Solves the 0-1 Knapsack problem using the bottom-up tabulation technique. | ||
* | ||
* @param val the values of the items | ||
* @param wt the weights of the items | ||
* @param W the total capacity of the knapsack | ||
* @param n the number of items | ||
* @return the maximum value that can be put in the knapsack | ||
*/ | ||
public static int compute(int[] val, int[] wt, int W, int n) { | ||
o000SAI000o marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int[][] dp = new int[n + 1][W + 1]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense to add input validation here, such as checks for null or empty arrays. This helps prevent runtime errors and makes the code more robust and reliable. And tests for that. |
||
|
||
for (int i = 1; i <= n; i++) { | ||
int value = val[i - 1]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using the |
||
int weight = wt[i - 1]; | ||
|
||
for (int w = 1; w <= W; w++) { | ||
if (weight <= w) { | ||
int include = value + dp[i - 1][w - weight]; | ||
int exclude = dp[i - 1][w]; | ||
dp[i][w] = Math.max(include, exclude); | ||
} else { | ||
dp[i][w] = dp[i - 1][w]; | ||
} | ||
} | ||
} | ||
|
||
return dp[n][W]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* Test class for {@code ZeroOneKnapsackTab}. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is redundant, as it merely restates what is already clear from the class structure. |
||
*/ | ||
public class ZeroOneknapsackTabTest { | ||
|
||
/** | ||
* Tests the 0-1 Knapsack tabulation approach with known values. | ||
*/ | ||
@Test | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider refactoring these test methods into a single parameterized test. Since all tests follow the same pattern calling There are already many examples of parameterized tests in the repository. |
||
public void testKnownValues() { | ||
int[] val = {60, 100, 120}; | ||
int[] wt = {10, 20, 30}; | ||
int W = 50; | ||
int n = val.length; | ||
|
||
// Expected result is 220 (items with weight 20 and 30) | ||
assertEquals(220, ZeroOneKnapsackTab.compute(val, wt, W, n), "Maximum value for capacity 50 should be 220."); | ||
} | ||
|
||
@Test | ||
public void testZeroCapacity() { | ||
int[] val = {10, 20, 30}; | ||
int[] wt = {1, 1, 1}; | ||
int W = 0; | ||
int n = val.length; | ||
|
||
// With zero capacity, the result should be 0 | ||
assertEquals(0, ZeroOneKnapsackTab.compute(val, wt, W, n), "Maximum value for capacity 0 should be 0."); | ||
} | ||
|
||
@Test | ||
public void testZeroItems() { | ||
int[] val = {}; | ||
int[] wt = {}; | ||
int W = 10; | ||
int n = val.length; | ||
|
||
// With no items, the result should be 0 | ||
assertEquals(0, ZeroOneKnapsackTab.compute(val, wt, W, n), "Maximum value with no items should be 0."); | ||
} | ||
|
||
@Test | ||
public void testExactFit() { | ||
int[] val = {5, 10, 15}; | ||
int[] wt = {1, 2, 3}; | ||
int W = 6; | ||
int n = val.length; | ||
|
||
// All items fit exactly into capacity 6 | ||
assertEquals(30, ZeroOneKnapsackTab.compute(val, wt, W, n), "Maximum value for exact fit should be 30."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same suggestions for improvement also apply to this class. |
||
* Test class for {@code ZeroOneKnapsack}. | ||
*/ | ||
public class ZeroOneKnapsackTest { | ||
|
||
/** | ||
* Tests the knapsack computation for a basic example. | ||
*/ | ||
@Test | ||
public void testKnapsackBasic() { | ||
int[] val = {15, 14, 10, 45, 30}; | ||
int[] wt = {2, 5, 1, 3, 4}; | ||
int W = 7; | ||
assertEquals(75, ZeroOneKnapsack.compute(val, wt, W, val.length), "Expected maximum value is 75."); | ||
} | ||
|
||
/** | ||
* Tests the knapsack computation when the knapsack capacity is zero. | ||
*/ | ||
@Test | ||
public void testZeroCapacity() { | ||
int[] val = {10, 20, 30}; | ||
int[] wt = {1, 1, 1}; | ||
int W = 0; | ||
assertEquals(0, ZeroOneKnapsack.compute(val, wt, W, val.length), "Expected maximum value is 0 for zero capacity."); | ||
} | ||
|
||
/** | ||
* Tests the knapsack computation when there are no items. | ||
*/ | ||
@Test | ||
public void testNoItems() { | ||
int[] val = {}; | ||
int[] wt = {}; | ||
int W = 10; | ||
assertEquals(0, ZeroOneKnapsack.compute(val, wt, W, 0), "Expected maximum value is 0 when no items are available."); | ||
} | ||
|
||
/** | ||
* Tests the knapsack computation when items exactly fit the capacity. | ||
*/ | ||
@Test | ||
public void testExactFit() { | ||
int[] val = {60, 100, 120}; | ||
int[] wt = {10, 20, 30}; | ||
int W = 50; | ||
assertEquals(220, ZeroOneKnapsack.compute(val, wt, W, val.length), "Expected maximum value is 220 for exact fit."); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about renaming all related classes to start with Knapsack?
This way, it will be easier to find and group all Knapsack implementations together in the project.