-
Notifications
You must be signed in to change notification settings - Fork 20.1k
Feat(Improved): Add 0/1 Knapsack Problem: Recursive and Tabulation (Bottom-Up DP) Implementations in Java along with their corresponding Tests #6425
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3b8c5df
feat: Add 0/1 Knapsack and its tabulation implementation with their c…
o000SAI000o 8a8d1e2
feat: Add 0/1 Knapsack and its tabulation implementation with their c…
o000SAI000o 36fd4b2
feat: Add 0/1 Knapsack and its tabulation implementation with their c…
o000SAI000o 40b8bc5
Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests
o000SAI000o 10087e0
Merge branch 'master' into SAI
o000SAI000o 229d28b
Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests
o000SAI000o 51fe605
Merge branch 'SAI' of https://github.com/o000SAI000o/Java into SAI
o000SAI000o 72ce8a2
Feat:add 0/1knapsack and 0/1knapsacktabulation along with their tests
o000SAI000o 222663c
Merge branch 'master' into SAI
alxkm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
53 changes: 53 additions & 0 deletions
53
src/main/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOne.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
/** | ||
* The {@code KnapsackZeroOne} provides Recursive solution for the 0/1 Knapsack | ||
* problem. Solves by exploring all combinations of items using recursion. No | ||
* memoization or dynamic programming optimizations are applied. | ||
* | ||
* Time Complexity: O(2^n) — explores all subsets. | ||
* Space Complexity: O(n) — due to recursive call stack. | ||
* | ||
* Problem Reference: https://en.wikipedia.org/wiki/Knapsack_problem | ||
*/ | ||
public final class KnapsackZeroOne { | ||
|
||
private KnapsackZeroOne() { | ||
// Prevent instantiation | ||
} | ||
|
||
/** | ||
* 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 | ||
* @throws IllegalArgumentException if input arrays are null, empty, or | ||
* lengths mismatch | ||
*/ | ||
public static int compute(final int[] values, final int[] weights, final int capacity, final int n) { | ||
if (values == null || weights == null) { | ||
throw new IllegalArgumentException("Input arrays cannot be null."); | ||
} | ||
if (values.length != weights.length) { | ||
throw new IllegalArgumentException("Value and weight arrays must be of the same length."); | ||
} | ||
if (capacity < 0 || n < 0) { | ||
throw new IllegalArgumentException("Invalid input: arrays must be non-empty and capacity/n " | ||
+ "non-negative."); | ||
} | ||
if (n == 0 || capacity == 0 || values.length == 0) { | ||
return 0; | ||
} | ||
|
||
if (weights[n - 1] <= capacity) { | ||
final int include = values[n - 1] + compute(values, weights, capacity - weights[n - 1], n - 1); | ||
final int exclude = compute(values, weights, capacity, n - 1); | ||
return Math.max(include, exclude); | ||
} else { | ||
return compute(values, weights, capacity, n - 1); | ||
} | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
src/main/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTabulation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
/** | ||
* Tabulation (Bottom-Up) Solution for 0-1 Knapsack Problem. | ||
* This method uses dynamic programming to build up a solution iteratively, | ||
* filling a 2-D array where each entry dp[i][w] represents the maximum value | ||
* achievable with the first i items and a knapsack capacity of w. | ||
* | ||
* The tabulation approach is efficient because it avoids redundant calculations | ||
* by solving all subproblems in advance and storing their results, ensuring | ||
* each subproblem is solved only once. This is a key technique in dynamic programming, | ||
* making it possible to solve problems that would otherwise be infeasible due to | ||
* exponential time complexity in naive recursive solutions. | ||
* | ||
* Time Complexity: O(n * W), where n is the number of items and W is the knapsack capacity. | ||
* Space Complexity: O(n * W) for the DP table. | ||
* | ||
* For more information, see: | ||
* https://en.wikipedia.org/wiki/Knapsack_problem#Dynamic_programming | ||
*/ | ||
public final class KnapsackZeroOneTabulation { | ||
|
||
private KnapsackZeroOneTabulation() { | ||
// Prevent instantiation | ||
} | ||
|
||
/** | ||
* Solves the 0-1 Knapsack problem using the bottom-up tabulation technique. | ||
* @param values the values of the items | ||
* @param weights the weights of the items | ||
* @param capacity the total capacity of the knapsack | ||
* @param itemCount the number of items | ||
* @return the maximum value that can be put in the knapsack | ||
* @throws IllegalArgumentException if input arrays are null, of different lengths,or if capacity or itemCount is invalid | ||
*/ | ||
public static int compute(final int[] values, final int[] weights, final int capacity, final int itemCount) { | ||
if (values == null || weights == null) { | ||
throw new IllegalArgumentException("Values and weights arrays must not be null."); | ||
} | ||
if (values.length != weights.length) { | ||
throw new IllegalArgumentException("Values and weights arrays must be non-null and of same length."); | ||
} | ||
if (capacity < 0) { | ||
throw new IllegalArgumentException("Capacity must not be negative."); | ||
} | ||
if (itemCount < 0 || itemCount > values.length) { | ||
throw new IllegalArgumentException("Item count must be between 0 and the length of the values array."); | ||
} | ||
|
||
final int[][] dp = new int[itemCount + 1][capacity + 1]; | ||
|
||
for (int i = 1; i <= itemCount; i++) { | ||
final int currentValue = values[i - 1]; | ||
final int currentWeight = weights[i - 1]; | ||
|
||
for (int w = 1; w <= capacity; w++) { | ||
if (currentWeight <= w) { | ||
final int includeItem = currentValue + dp[i - 1][w - currentWeight]; | ||
final int excludeItem = dp[i - 1][w]; | ||
dp[i][w] = Math.max(includeItem, excludeItem); | ||
} else { | ||
dp[i][w] = dp[i - 1][w]; | ||
} | ||
} | ||
} | ||
|
||
return dp[itemCount][capacity]; | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/test/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTabulationTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
public class KnapsackZeroOneTabulationTest { | ||
|
||
@Test | ||
public void basicCheck() { | ||
int[] values = {60, 100, 120}; | ||
int[] weights = {10, 20, 30}; | ||
int capacity = 50; | ||
int itemCount = values.length; | ||
|
||
int expected = 220; // Best choice: item 1 (100) and item 2 (120) | ||
int result = KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount); | ||
assertEquals(expected, result); | ||
} | ||
|
||
@Test | ||
public void emptyKnapsack() { | ||
int[] values = {}; | ||
int[] weights = {}; | ||
int capacity = 50; | ||
int itemCount = 0; | ||
|
||
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount)); | ||
} | ||
|
||
@Test | ||
public void zeroCapacity() { | ||
int[] values = {60, 100, 120}; | ||
int[] weights = {10, 20, 30}; | ||
int capacity = 0; | ||
int itemCount = values.length; | ||
|
||
assertEquals(0, KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount)); | ||
} | ||
|
||
@Test | ||
public void negativeCapacity() { | ||
int[] values = {10, 20, 30}; | ||
int[] weights = {1, 1, 1}; | ||
int capacity = -10; | ||
int itemCount = values.length; | ||
|
||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount)); | ||
assertEquals("Capacity must not be negative.", exception.getMessage()); | ||
} | ||
|
||
@Test | ||
public void mismatchedLengths() { | ||
int[] values = {60, 100}; // Only 2 values | ||
int[] weights = {10, 20, 30}; // 3 weights | ||
int capacity = 50; | ||
int itemCount = 2; // Matches `values.length` | ||
|
||
// You could either expect 0 or throw an IllegalArgumentException in your compute function | ||
assertThrows(IllegalArgumentException.class, () -> { KnapsackZeroOneTabulation.compute(values, weights, capacity, itemCount); }); | ||
} | ||
|
||
@Test | ||
public void nullInputs() { | ||
int[] weights = {1, 2, 3}; | ||
int capacity = 10; | ||
int itemCount = 3; | ||
|
||
IllegalArgumentException exception1 = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(null, weights, capacity, itemCount)); | ||
assertEquals("Values and weights arrays must not be null.", exception1.getMessage()); | ||
|
||
int[] values = {1, 2, 3}; | ||
|
||
IllegalArgumentException exception2 = assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOneTabulation.compute(values, null, capacity, itemCount)); | ||
assertEquals("Values and weights arrays must not be null.", exception2.getMessage()); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/test/java/com/thealgorithms/dynamicprogramming/KnapsackZeroOneTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.thealgorithms.dynamicprogramming; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
class KnapsackZeroOneTest { | ||
|
||
@Test | ||
void basicCheck() { | ||
int[] values = {60, 100, 120}; | ||
int[] weights = {10, 20, 30}; | ||
int capacity = 50; | ||
int expected = 220; | ||
|
||
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length); | ||
assertEquals(expected, result); | ||
} | ||
|
||
@Test | ||
void zeroCapacity() { | ||
int[] values = {10, 20, 30}; | ||
int[] weights = {1, 1, 1}; | ||
int capacity = 0; | ||
|
||
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length); | ||
assertEquals(0, result); | ||
} | ||
|
||
@Test | ||
void zeroItems() { | ||
int[] values = {}; | ||
int[] weights = {}; | ||
int capacity = 10; | ||
|
||
int result = KnapsackZeroOne.compute(values, weights, capacity, 0); | ||
assertEquals(0, result); | ||
} | ||
|
||
@Test | ||
void weightsExceedingCapacity() { | ||
int[] values = {10, 20}; | ||
int[] weights = {100, 200}; | ||
int capacity = 50; | ||
|
||
int result = KnapsackZeroOne.compute(values, weights, capacity, values.length); | ||
assertEquals(0, result); | ||
} | ||
|
||
@Test | ||
void throwsOnNullArrays() { | ||
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(null, new int[] {1}, 10, 1)); | ||
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {1}, null, 10, 1)); | ||
} | ||
|
||
@Test | ||
void throwsOnMismatchedArrayLengths() { | ||
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10, 20}, new int[] {5}, 15, 2)); | ||
} | ||
|
||
@Test | ||
void throwsOnNegativeInputs() { | ||
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, -1, 1)); | ||
|
||
assertThrows(IllegalArgumentException.class, () -> KnapsackZeroOne.compute(new int[] {10}, new int[] {5}, 5, -1)); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.