-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
619 additions
and
22 deletions.
There are no files selected for viewing
This file contains 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,151 @@ | ||
/** | ||
* @file | ||
* @brief Implementation of the Unbounded 0/1 Knapsack Problem | ||
* | ||
* @details | ||
* The Unbounded 0/1 Knapsack problem allows taking unlimited quantities of each item. | ||
* The goal is to maximize the total value without exceeding the given knapsack capacity. | ||
* Unlike the 0/1 knapsack, where each item can be taken only once, in this variation, | ||
* any item can be picked any number of times as long as the total weight stays within | ||
* the knapsack's capacity. | ||
* | ||
* Given a set of N items, each with a weight and a value, represented by the arrays | ||
* `wt` and `val` respectively, and a knapsack with a weight limit W, the task is to | ||
* fill the knapsack to maximize the total value. | ||
* | ||
* @note weight and value of items is greater than zero | ||
* | ||
* ### Algorithm | ||
* The approach uses dynamic programming to build a solution iteratively. | ||
* A 2D array is used for memoization to store intermediate results, allowing | ||
* the function to avoid redundant calculations. | ||
* | ||
* @author [Sanskruti Yeole](https://github.com/yeolesanskruti) | ||
* @see dynamic_programming/0_1_knapsack.cpp | ||
*/ | ||
|
||
#include <iostream> // Standard input-output stream | ||
#include <vector> // Standard library for using dynamic arrays (vectors) | ||
#include <cassert> // For using assert function to validate test cases | ||
#include <cstdint> // For fixed-width integer types like std::uint16_t | ||
|
||
/** | ||
* @namespace dynamic_programming | ||
* @brief Namespace for dynamic programming algorithms | ||
*/ | ||
namespace dynamic_programming { | ||
|
||
/** | ||
* @namespace Knapsack | ||
* @brief Implementation of unbounded 0-1 knapsack problem | ||
*/ | ||
namespace unbounded_knapsack { | ||
|
||
/** | ||
* @brief Recursive function to calculate the maximum value obtainable using | ||
* an unbounded knapsack approach. | ||
* | ||
* @param i Current index in the value and weight vectors. | ||
* @param W Remaining capacity of the knapsack. | ||
* @param val Vector of values corresponding to the items. | ||
* @note "val" data type can be changed according to the size of the input. | ||
* @param wt Vector of weights corresponding to the items. | ||
* @note "wt" data type can be changed according to the size of the input. | ||
* @param dp 2D vector for memoization to avoid redundant calculations. | ||
* @return The maximum value that can be obtained for the given index and capacity. | ||
*/ | ||
std::uint16_t KnapSackFilling(std::uint16_t i, std::uint16_t W, | ||
const std::vector<std::uint16_t>& val, | ||
const std::vector<std::uint16_t>& wt, | ||
std::vector<std::vector<int>>& dp) { | ||
if (i == 0) { | ||
if (wt[0] <= W) { | ||
return (W / wt[0]) * val[0]; // Take as many of the first item as possible | ||
} else { | ||
return 0; // Can't take the first item | ||
} | ||
} | ||
if (dp[i][W] != -1) return dp[i][W]; // Return result if available | ||
|
||
int nottake = KnapSackFilling(i - 1, W, val, wt, dp); // Value without taking item i | ||
int take = 0; | ||
if (W >= wt[i]) { | ||
take = val[i] + KnapSackFilling(i, W - wt[i], val, wt, dp); // Value taking item i | ||
} | ||
return dp[i][W] = std::max(take, nottake); // Store and return the maximum value | ||
} | ||
|
||
/** | ||
* @brief Wrapper function to initiate the unbounded knapsack calculation. | ||
* | ||
* @param N Number of items. | ||
* @param W Maximum weight capacity of the knapsack. | ||
* @param val Vector of values corresponding to the items. | ||
* @param wt Vector of weights corresponding to the items. | ||
* @return The maximum value that can be obtained for the given capacity. | ||
*/ | ||
std::uint16_t unboundedKnapsack(std::uint16_t N, std::uint16_t W, | ||
const std::vector<std::uint16_t>& val, | ||
const std::vector<std::uint16_t>& wt) { | ||
if(N==0)return 0; // Expect 0 since no items | ||
std::vector<std::vector<int>> dp(N, std::vector<int>(W + 1, -1)); // Initialize memoization table | ||
return KnapSackFilling(N - 1, W, val, wt, dp); // Start the calculation | ||
} | ||
|
||
} // unbounded_knapsack | ||
|
||
} // dynamic_programming | ||
|
||
/** | ||
* @brief self test implementation | ||
* @return void | ||
*/ | ||
static void tests() { | ||
// Test Case 1 | ||
std::uint16_t N1 = 4; // Number of items | ||
std::vector<std::uint16_t> wt1 = {1, 3, 4, 5}; // Weights of the items | ||
std::vector<std::uint16_t> val1 = {6, 1, 7, 7}; // Values of the items | ||
std::uint16_t W1 = 8; // Maximum capacity of the knapsack | ||
// Test the function and assert the expected output | ||
assert(unboundedKnapsack(N1, W1, val1, wt1) == 48); | ||
std::cout << "Maximum Knapsack value " << unboundedKnapsack(N1, W1, val1, wt1) << std::endl; | ||
|
||
// Test Case 2 | ||
std::uint16_t N2 = 3; // Number of items | ||
std::vector<std::uint16_t> wt2 = {10, 20, 30}; // Weights of the items | ||
std::vector<std::uint16_t> val2 = {60, 100, 120}; // Values of the items | ||
std::uint16_t W2 = 5; // Maximum capacity of the knapsack | ||
// Test the function and assert the expected output | ||
assert(unboundedKnapsack(N2, W2, val2, wt2) == 0); | ||
std::cout << "Maximum Knapsack value " << unboundedKnapsack(N2, W2, val2, wt2) << std::endl; | ||
|
||
// Test Case 3 | ||
std::uint16_t N3 = 3; // Number of items | ||
std::vector<std::uint16_t> wt3 = {2, 4, 6}; // Weights of the items | ||
std::vector<std::uint16_t> val3 = {5, 11, 13};// Values of the items | ||
std::uint16_t W3 = 27;// Maximum capacity of the knapsack | ||
// Test the function and assert the expected output | ||
assert(unboundedKnapsack(N3, W3, val3, wt3) == 27); | ||
std::cout << "Maximum Knapsack value " << unboundedKnapsack(N3, W3, val3, wt3) << std::endl; | ||
|
||
// Test Case 4 | ||
std::uint16_t N4 = 0; // Number of items | ||
std::vector<std::uint16_t> wt4 = {}; // Weights of the items | ||
std::vector<std::uint16_t> val4 = {}; // Values of the items | ||
std::uint16_t W4 = 10; // Maximum capacity of the knapsack | ||
assert(unboundedKnapsack(N4, W4, val4, wt4) == 0); | ||
std::cout << "Maximum Knapsack value for empty arrays: " << unboundedKnapsack(N4, W4, val4, wt4) << std::endl; | ||
|
||
std::cout << "All test cases passed!" << std::endl; | ||
|
||
} | ||
|
||
/** | ||
* @brief main function | ||
* @return 0 on successful exit | ||
*/ | ||
int main() { | ||
tests(); // Run self test implementation | ||
return 0; | ||
} | ||
|
This file contains 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,119 @@ | ||
/** | ||
* @file binary_addition.cpp | ||
* @brief Adds two binary numbers and outputs resulting string | ||
* | ||
* @details The algorithm for adding two binary strings works by processing them | ||
* from right to left, similar to manual addition. It starts by determining the | ||
* longer string's length to ensure both strings are fully traversed. For each | ||
* pair of corresponding bits and any carry from the previous addition, it | ||
* calculates the sum. If the sum exceeds 1, a carry is generated for the next | ||
* bit. The results for each bit are collected in a result string, which is | ||
* reversed at the end to present the final binary sum correctly. Additionally, | ||
* the function validates the input to ensure that only valid binary strings | ||
* (containing only '0' and '1') are processed. If invalid input is detected, | ||
* it returns an empty string. | ||
* @author [Muhammad Junaid Khalid](https://github.com/mjk22071998) | ||
*/ | ||
|
||
#include <algorithm> /// for reverse function | ||
#include <cassert> /// for tests | ||
#include <iostream> /// for input and outputs | ||
#include <string> /// for string class | ||
|
||
/** | ||
* @namespace | ||
* @brief Greedy Algorithms | ||
*/ | ||
namespace greedy_algorithms { | ||
/** | ||
* @brief A class to perform binary addition of two binary strings. | ||
*/ | ||
class BinaryAddition { | ||
public: | ||
/** | ||
* @brief Adds two binary strings and returns the result as a binary string. | ||
* @param a The first binary string. | ||
* @param b The second binary string. | ||
* @return The sum of the two binary strings as a binary string, or an empty | ||
* string if either input string contains non-binary characters. | ||
*/ | ||
std::string addBinary(const std::string& a, const std::string& b) { | ||
if (!isValidBinaryString(a) || !isValidBinaryString(b)) { | ||
return ""; // Return empty string if input contains non-binary | ||
// characters | ||
} | ||
|
||
std::string result; | ||
int carry = 0; | ||
int maxLength = std::max(a.size(), b.size()); | ||
|
||
// Traverse both strings from the end to the beginning | ||
for (int i = 0; i < maxLength; ++i) { | ||
// Get the current bits from both strings, if available | ||
int bitA = (i < a.size()) ? (a[a.size() - 1 - i] - '0') : 0; | ||
int bitB = (i < b.size()) ? (b[b.size() - 1 - i] - '0') : 0; | ||
|
||
// Calculate the sum of bits and carry | ||
int sum = bitA + bitB + carry; | ||
carry = sum / 2; // Determine the carry for the next bit | ||
result.push_back((sum % 2) + | ||
'0'); // Append the sum's current bit to result | ||
} | ||
if (carry) { | ||
result.push_back('1'); | ||
} | ||
std::reverse(result.begin(), result.end()); | ||
return result; | ||
} | ||
|
||
private: | ||
/** | ||
* @brief Validates whether a string contains only binary characters (0 or 1). | ||
* @param str The string to validate. | ||
* @return true if the string is binary, false otherwise. | ||
*/ | ||
bool isValidBinaryString(const std::string& str) const { | ||
return std::all_of(str.begin(), str.end(), | ||
[](char c) { return c == '0' || c == '1'; }); | ||
} | ||
}; | ||
} // namespace greedy_algorithms | ||
|
||
/** | ||
* @brief run self test implementation. | ||
* @returns void | ||
*/ | ||
static void tests() { | ||
greedy_algorithms::BinaryAddition binaryAddition; | ||
|
||
// Valid binary string tests | ||
assert(binaryAddition.addBinary("1010", "1101") == "10111"); | ||
assert(binaryAddition.addBinary("1111", "1111") == "11110"); | ||
assert(binaryAddition.addBinary("101", "11") == "1000"); | ||
assert(binaryAddition.addBinary("0", "0") == "0"); | ||
assert(binaryAddition.addBinary("1111", "1111") == "11110"); | ||
assert(binaryAddition.addBinary("0", "10101") == "10101"); | ||
assert(binaryAddition.addBinary("10101", "0") == "10101"); | ||
assert(binaryAddition.addBinary("101010101010101010101010101010", | ||
"110110110110110110110110110110") == | ||
"1100001100001100001100001100000"); | ||
assert(binaryAddition.addBinary("1", "11111111") == "100000000"); | ||
assert(binaryAddition.addBinary("10101010", "01010101") == "11111111"); | ||
|
||
// Invalid binary string tests (should return empty string) | ||
assert(binaryAddition.addBinary("10102", "1101") == ""); | ||
assert(binaryAddition.addBinary("ABC", "1101") == ""); | ||
assert(binaryAddition.addBinary("1010", "1102") == ""); | ||
assert(binaryAddition.addBinary("111", "1x1") == ""); | ||
assert(binaryAddition.addBinary("1x1", "111") == ""); | ||
assert(binaryAddition.addBinary("1234", "1101") == ""); | ||
} | ||
|
||
/** | ||
* @brief main function | ||
* @returns 0 on successful exit | ||
*/ | ||
int main() { | ||
tests(); /// To execute tests | ||
return 0; | ||
} |
Oops, something went wrong.