From 05d784d844c98677d8418130d33209b319c0d52d Mon Sep 17 00:00:00 2001 From: Naman Jain <103953760+namanmodi65@users.noreply.github.com> Date: Fri, 18 Oct 2024 21:59:49 +0530 Subject: [PATCH 1/7] Longest Increasing subsequence using binary search most optimal approach (Modified) (#2776) * Longest Increasing subsequence using binary search most optimal approach for this problem * Longest Increasing subsequence using binary search most optimal approach for this problem * Longest Increasing subsequence using binary search most optimal approach for this problem (Modified) * Longest Increasing subsequence using binary search most optimal approach for this problem * Longest Increasing subsequence using binary search most optimal approach for this problem(done) * Longest Increasing subsequence using binary search most optimal approach for this problem * Floyd warshall * Longest Increasing subsequence using binary search most optimal approach for this problem * Longest Increasing subsequence using binary search most optimal approach for this problem * Longest Increasing subsequence using binary search most optimal approach for this problem --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- ...easing_Subsequence_using_binary_search.cpp | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 search/Longest_Increasing_Subsequence_using_binary_search.cpp diff --git a/search/Longest_Increasing_Subsequence_using_binary_search.cpp b/search/Longest_Increasing_Subsequence_using_binary_search.cpp new file mode 100644 index 000000000..2f83de72e --- /dev/null +++ b/search/Longest_Increasing_Subsequence_using_binary_search.cpp @@ -0,0 +1,117 @@ +/** + * @file + * @brief find the length of the Longest Increasing Subsequence (LIS) + * using [Binary Search](https://en.wikipedia.org/wiki/Longest_increasing_subsequence) + * @details + * Given an integer array nums, return the length of the longest strictly + * increasing subsequence. + * The longest increasing subsequence is described as a subsequence of an array + * where: All elements of the subsequence are in increasing order. This subsequence + * itself is of the longest length possible. + + * For solving this problem we have Three Approaches :- + + * Approach 1 :- Using Brute Force + * The first approach that came to your mind is the Brute Force approach where we + * generate all subsequences and then manually filter the subsequences whose + * elements come in increasing order and then return the longest such subsequence. + * Time Complexity :- O(2^n) + * It's time complexity is exponential. Therefore we will try some other + * approaches. + + * Approach 2 :- Using Dynamic Programming + * To generate all subsequences we will use recursion and in the recursive logic we + * will figure out a way to solve this problem. Recursive Logic to solve this + * problem:- + * 1. We only consider the element in the subsequence if the element is grater then + * the last element present in the subsequence + * 2. When we consider the element we will increase the length of subsequence by 1 + * Time Complexity: O(N*N) + * Space Complexity: O(N*N) + O(N) + + * This approach is better then the previous Brute Force approach so, we can + * consider this approach. + + * But when the Constraints for the problem is very larger then this approach fails + + * Approach 3 :- Using Binary Search + * Other approaches use additional space to create a new subsequence Array. + * Instead, this solution uses the existing nums Array to build the subsequence + * array. We can do this because the length of the subsequence array will never be + * longer than the current index. + + * Time complexity: O(n∗log(n)) + * Space complexity: O(1) + + * This approach consider Most optimal Approach for solving this problem + + * @author [Naman Jain](https://github.com/namanmodi65) + */ + +#include /// for std::assert +#include /// for IO operations +#include /// for std::vector +#include /// for std::lower_bound +#include /// for std::uint32_t + +/** + * @brief Function to find the length of the Longest Increasing Subsequence (LIS) + * using Binary Search + * @tparam T The type of the elements in the input vector + * @param nums The input vector of elements of type T + * @return The length of the longest increasing subsequence + */ +template +std::uint32_t longest_increasing_subsequence_using_binary_search(std::vector& nums) { + if (nums.empty()) return 0; + + std::vector ans; + ans.push_back(nums[0]); + for (std::size_t i = 1; i < nums.size(); i++) { + if (nums[i] > ans.back()) { + ans.push_back(nums[i]); + } else { + auto idx = std::lower_bound(ans.begin(), ans.end(), nums[i]) - ans.begin(); + ans[idx] = nums[i]; + } + } + return static_cast(ans.size()); +} + +/** + * @brief Test cases for Longest Increasing Subsequence function + * @returns void + */ +static void tests() { + std::vector arr = {10, 9, 2, 5, 3, 7, 101, 18}; + assert(longest_increasing_subsequence_using_binary_search(arr) == 4); + + std::vector arr2 = {0, 1, 0, 3, 2, 3}; + assert(longest_increasing_subsequence_using_binary_search(arr2) == 4); + + std::vector arr3 = {7, 7, 7, 7, 7, 7, 7}; + assert(longest_increasing_subsequence_using_binary_search(arr3) == 1); + + std::vector arr4 = {-10, -1, -5, 0, 5, 1, 2}; + assert(longest_increasing_subsequence_using_binary_search(arr4) == 5); + + std::vector arr5 = {3.5, 1.2, 2.8, 3.1, 4.0}; + assert(longest_increasing_subsequence_using_binary_search(arr5) == 4); + + std::vector arr6 = {'a', 'b', 'c', 'a', 'd'}; + assert(longest_increasing_subsequence_using_binary_search(arr6) == 4); + + std::vector arr7 = {}; + assert(longest_increasing_subsequence_using_binary_search(arr7) == 0); + + std::cout << "All tests have successfully passed!\n"; +} + +/** + * @brief Main function to run tests + * @returns 0 on exit + */ +int main() { + tests(); // run self test implementation + return 0; +} From d438f0fc7fb3593fff2a076071714fe6aeafe954 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 15:22:15 +0530 Subject: [PATCH 2/7] docs: update DIRECTORY.md (#2857) Co-authored-by: github-actions[bot] --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 3e8c64d95..4ba06fa0b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -340,6 +340,7 @@ * [Sparse Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/range_queries/sparse_table.cpp) ## Search + * [Longest Increasing Subsequence Using Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/Longest_Increasing_Subsequence_using_binary_search.cpp) * [Binary Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/binary_search.cpp) * [Exponential Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/exponential_search.cpp) * [Fibonacci Search](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/search/fibonacci_search.cpp) From 37a98113724205f66d2f92adb784a34c44553c2b Mon Sep 17 00:00:00 2001 From: Nguyen Phuc Chuong <72879387+hollowcrust@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:21:23 +0800 Subject: [PATCH 3/7] docs, test: fit modular inverse fermat little theorem to contributing guidelines (#2779) * Update modular_inverse_fermat_little_theorem.cpp * Update modular_inverse_fermat_little_theorem.cpp * Update modular_inverse_fermat_little_theorem.cpp * Update modular_inverse_fermat_little_theorem.cpp * Update math/modular_inverse_fermat_little_theorem.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update math/modular_inverse_fermat_little_theorem.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update modular_inverse_fermat_little_theorem.cpp Add time complexity in comment * Update modular_inverse_fermat_little_theorem.cpp * Update modular_inverse_fermat_little_theorem.cpp --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- .../modular_inverse_fermat_little_theorem.cpp | 128 ++++++++++++------ 1 file changed, 85 insertions(+), 43 deletions(-) diff --git a/math/modular_inverse_fermat_little_theorem.cpp b/math/modular_inverse_fermat_little_theorem.cpp index 7550e14bf..d870c4da9 100644 --- a/math/modular_inverse_fermat_little_theorem.cpp +++ b/math/modular_inverse_fermat_little_theorem.cpp @@ -30,8 +30,8 @@ * a^{m-2} &≡& a^{-1} \;\text{mod}\; m * \f} * - * We will find the exponent using binary exponentiation. Such that the - * algorithm works in \f$O(\log m)\f$ time. + * We will find the exponent using binary exponentiation such that the + * algorithm works in \f$O(\log n)\f$ time. * * Examples: - * * a = 3 and m = 7 @@ -43,56 +43,98 @@ * (as \f$a\times a^{-1} = 1\f$) */ -#include -#include +#include /// for assert +#include /// for std::int64_t +#include /// for IO implementations -/** Recursive function to calculate exponent in \f$O(\log n)\f$ using binary - * exponent. +/** + * @namespace math + * @brief Maths algorithms. */ -int64_t binExpo(int64_t a, int64_t b, int64_t m) { - a %= m; - int64_t res = 1; - while (b > 0) { - if (b % 2) { - res = res * a % m; - } - a = a * a % m; - // Dividing b by 2 is similar to right shift. - b >>= 1; +namespace math { +/** + * @namespace modular_inverse_fermat + * @brief Calculate modular inverse using Fermat's Little Theorem. + */ +namespace modular_inverse_fermat { +/** + * @brief Calculate exponent with modulo using binary exponentiation in \f$O(\log b)\f$ time. + * @param a The base + * @param b The exponent + * @param m The modulo + * @return The result of \f$a^{b} % m\f$ + */ +std::int64_t binExpo(std::int64_t a, std::int64_t b, std::int64_t m) { + a %= m; + std::int64_t res = 1; + while (b > 0) { + if (b % 2 != 0) { + res = res * a % m; } - return res; + a = a * a % m; + // Dividing b by 2 is similar to right shift by 1 bit + b >>= 1; + } + return res; } - -/** Prime check in \f$O(\sqrt{m})\f$ time. +/** + * @brief Check if an integer is a prime number in \f$O(\sqrt{m})\f$ time. + * @param m An intger to check for primality + * @return true if the number is prime + * @return false if the number is not prime */ -bool isPrime(int64_t m) { - if (m <= 1) { - return false; - } else { - for (int64_t i = 2; i * i <= m; i++) { - if (m % i == 0) { - return false; - } - } +bool isPrime(std::int64_t m) { + if (m <= 1) { + return false; + } + for (std::int64_t i = 2; i * i <= m; i++) { + if (m % i == 0) { + return false; } - return true; + } + return true; +} +/** + * @brief calculates the modular inverse. + * @param a Integer value for the base + * @param m Integer value for modulo + * @return The result that is the modular inverse of a modulo m + */ +std::int64_t modular_inverse(std::int64_t a, std::int64_t m) { + while (a < 0) { + a += m; + } + + // Check for invalid cases + if (!isPrime(m) || a == 0) { + return -1; // Invalid input + } + + return binExpo(a, m - 2, m); // Fermat's Little Theorem +} +} // namespace modular_inverse_fermat +} // namespace math + +/** + * @brief Self-test implementation + * @return void + */ +static void test() { + assert(math::modular_inverse_fermat::modular_inverse(0, 97) == -1); + assert(math::modular_inverse_fermat::modular_inverse(15, -2) == -1); + assert(math::modular_inverse_fermat::modular_inverse(3, 10) == -1); + assert(math::modular_inverse_fermat::modular_inverse(3, 7) == 5); + assert(math::modular_inverse_fermat::modular_inverse(1, 101) == 1); + assert(math::modular_inverse_fermat::modular_inverse(-1337, 285179) == 165519); + assert(math::modular_inverse_fermat::modular_inverse(123456789, 998244353) == 25170271); + assert(math::modular_inverse_fermat::modular_inverse(-9876543210, 1000000007) == 784794281); } /** - * Main function + * @brief Main function + * @return 0 on exit */ int main() { - int64_t a, m; - // Take input of a and m. - std::cout << "Computing ((a^(-1))%(m)) using Fermat's Little Theorem"; - std::cout << std::endl << std::endl; - std::cout << "Give input 'a' and 'm' space separated : "; - std::cin >> a >> m; - if (isPrime(m)) { - std::cout << "The modular inverse of a with mod m is (a^(m-2)) : "; - std::cout << binExpo(a, m - 2, m) << std::endl; - } else { - std::cout << "m must be a prime number."; - std::cout << std::endl; - } + test(); // run self-test implementation + return 0; } From ffef54470dededc7cc82cffb930432c110f7c46a Mon Sep 17 00:00:00 2001 From: deDSeC00720 <62394683+deDSeC00720@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:34:52 +0530 Subject: [PATCH 4/7] feat: LRU (Least recently used) cache - different implementation (#2783) * feat: add lru cache * test: add test to cover exception * test: add assert for exception message * review: change int to uint32_t * review: add header for std::uint32_t --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- others/lru_cache2.cpp | 277 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 others/lru_cache2.cpp diff --git a/others/lru_cache2.cpp b/others/lru_cache2.cpp new file mode 100644 index 000000000..aaa6c943f --- /dev/null +++ b/others/lru_cache2.cpp @@ -0,0 +1,277 @@ +/** + * @file + * @brief Implementation for [LRU Cache] + * (https://en.wikipedia.org/wiki/Cache_replacement_policies#:~:text=Least%20Recently%20Used%20(LRU)) + * + * @details + * LRU discards the least recently used value. + * Data structures used - doubly linked list and unordered_map + * + * unordered_map maps the key to the address of the node of the linked list. + * If the element is accessed, the element is moved to the beginning of the + * linked list. + * + * When the cache is full, the last element in the linked list is popped. + * + * @author [Karan Sharma](https://github.com/deDSeC00720) + */ + +#include // for assert +#include // for std::uint32_t +#include // for std::cout +#include // for std::unordered_map + +/** + * @namespace + * @brief Other algorithms + */ +namespace others { + +/** + * @namespace + * @brief Cache algorithm + */ +namespace Cache { + +/** + * @class + * @brief Node for a doubly linked list with data, prev and next pointers + * @tparam T type of the data of the node + */ +template +class D_Node { + public: + T data; ///< data of the node + D_Node *prev; ///< previous node in the doubly linked list + D_Node *next; ///< next node in the doubly linked list + + explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {} +}; + +template +using CacheNode = D_Node>; + +/** + * @class + * @brief LRUCache + * @tparam K type of key in the LRU + * @tparam V type of value in the LRU + */ +template +class LRUCache { + CacheNode *head; ///< head of the doubly linked list + CacheNode *tail; ///< tail of the doubly linked list + std::uint32_t _capacity; ///< maximum capacity of the cache + + std::unordered_map *> + node_map; ///< maps the key to the node address + + public: + /** + * @brief Constructor, Initialize the head and tail pointers to nullptr and + * initialize the _capacity of the cache + * @param _capacity Total capacity of the cache + */ + explicit LRUCache(int _capacity) + : head(nullptr), tail(nullptr), _capacity(_capacity) {} + + private: + /** + * @brief push the node to the front of the linked list. + * @param node_ptr the node to be pushed + */ + void push_front(CacheNode *node_ptr) { + if (!head) { + head = node_ptr; + tail = node_ptr; + return; + } + + node_ptr->next = head; + head->prev = node_ptr; + head = node_ptr; + } + + /** + * @brief move the existing node in the list to the beginning of the list. + * @param node_ptr node to be moved to the beginning. + */ + void make_recent(CacheNode *node_ptr) { + if (head == node_ptr) { + return; + } + + CacheNode *prev = node_ptr->prev; + CacheNode *next = node_ptr->next; + + prev->next = next; + if (next) { + next->prev = prev; + } else { + tail = prev; + } + + node_ptr->prev = nullptr; + node_ptr->next = nullptr; + push_front(node_ptr); + } + + /** + * @brief pop the last node in the linked list. + */ + void pop_back() { + if (!head) { + return; + } + if (head == tail) { + delete head; + head = nullptr; + tail = nullptr; + return; + } + + CacheNode *temp = tail; + tail = tail->prev; + tail->next = nullptr; + delete temp; + } + + public: + /** + * @brief upsert a key-value pair + * @param key key of the key-value pair + * @param value value of the key-value pair + */ + void put(K key, V value) { + // update the value if key already exists + if (node_map.count(key)) { + node_map[key]->data.second = value; + make_recent(node_map[key]); + return; + } + + // if the cache is full + // remove the least recently used item + if (node_map.size() == _capacity) { + node_map.erase(tail->data.first); + pop_back(); + } + + CacheNode *newNode = new CacheNode({key, value}); + + node_map[key] = newNode; + push_front(newNode); + } + + /** + * @brief get the value of the key-value pair if exists + * @param key key of the key-value pair + * @return the value mapped to the given key + * @exception exception is thrown if the key is not present in the cache + */ + V get(K key) { + if (!node_map.count(key)) { + throw std::runtime_error("key is not present in the cache"); + } + + // move node to the beginning of the list + V value = node_map[key]->data.second; + make_recent(node_map[key]); + return value; + } + + /** + * @brief Returns the number of items present in the cache. + * @return number of items in the cache + */ + int size() const { return node_map.size(); } + + /** + * @brief Returns the total capacity of the cache + * @return Total capacity of the cache + */ + int capacity() const { return _capacity; } + + /** + * @brief returns whether the cache is empty or not + * @return true if the cache is empty, false otherwise. + */ + bool empty() const { return node_map.empty(); } + + /** + * @brief destructs the cache, iterates on the map and deletes every node + * present in the cache. + */ + ~LRUCache() { + auto it = node_map.begin(); + while (it != node_map.end()) { + delete it->second; + ++it; + } + } +}; +} // namespace Cache +} // namespace others + +/** + * @brief self test implementations + * @return void + */ +static void test() { + others::Cache::LRUCache cache(5); + + // test the initial state of the cache + assert(cache.size() == 0); + assert(cache.capacity() == 5); + assert(cache.empty()); + + // test insertion in the cache + cache.put(1, 10); + cache.put(-2, 20); + + // test the state of cache after inserting some items + assert(cache.size() == 2); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // test getting items from the cache + assert(cache.get(1) == 10); + assert(cache.get(-2) == 20); + + cache.put(-3, -30); + cache.put(4, 40); + cache.put(5, -50); + cache.put(6, 60); + + // test the state after inserting more items than the capacity + assert(cache.size() == 5); + assert(cache.capacity() == 5); + assert(!cache.empty()); + + // fetching 1 throws runtime_error + // as 1 was evicted being the least recently used + // when 6 was added + try { + cache.get(1); + } catch (const std::runtime_error &e) { + assert(std::string(e.what()) == "key is not present in the cache"); + } + + // test retrieval of all items in the cache + assert(cache.get(-2) == 20); + assert(cache.get(-3) == -30); + assert(cache.get(4) == 40); + assert(cache.get(5) == -50); + assert(cache.get(6) == 60); + + std::cout << "test - passed\n"; +} + +/** + * @brief main function + * @return 0 on exit + */ +int main() { + test(); // run the self test implementation + return 0; +} From ecb8a3388d2343dea268ed972577fca646654fa0 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid Khalid Date: Thu, 24 Oct 2024 21:24:38 +0500 Subject: [PATCH 5/7] feat: Added Binary Addition Algorithm (#2802) * Digit Separation Algorithm added * feat: Added namespace greedy_algoriithms * "Updated digit separation code: added comments and docstrings, changed data types to std::int64_t, and reformatted code." * feat: Made test function and removed extra whitespaces * removed some more whitespaces * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * fix: terminate multiline comment * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Corrected test function * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update greedy_algorithms/digit_separation.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * remove redundant declaration Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Corrected tests Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * file clang linted * "Moved method implementations from outside to inside class definition" * fowardOrder to forwardOrder on line 122 Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Removed Class scope resolution form digitSeparationReverseOrder function Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Removed Class scope resolution form digitSeparationForwardOrderfunction Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * docs: documentation updated * initial commit of Binary Addition * "Formatting changes and whitespace adjustments in binary_addition.cpp" * Documentation added * fix: removed unnecessary whitespaces * fix: Fixed the case of non binary strings * clang linted * docs: Documentation updated * Comments updated * Removed link Removed @see link in header documentation Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * clang-format and clang-tidy fixes for cd8a5953 * made `tests()` static Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * added @returns in documentation of tests function Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * added @return in documentation of main function Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> Co-authored-by: github-actions[bot] --- greedy_algorithms/binary_addition.cpp | 119 ++++++++++++++++++ ...asing_subsequence_using_binary_search.cpp} | 0 2 files changed, 119 insertions(+) create mode 100644 greedy_algorithms/binary_addition.cpp rename search/{Longest_Increasing_Subsequence_using_binary_search.cpp => longest_increasing_subsequence_using_binary_search.cpp} (100%) diff --git a/greedy_algorithms/binary_addition.cpp b/greedy_algorithms/binary_addition.cpp new file mode 100644 index 000000000..4a3653238 --- /dev/null +++ b/greedy_algorithms/binary_addition.cpp @@ -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 /// for reverse function +#include /// for tests +#include /// for input and outputs +#include /// 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; +} diff --git a/search/Longest_Increasing_Subsequence_using_binary_search.cpp b/search/longest_increasing_subsequence_using_binary_search.cpp similarity index 100% rename from search/Longest_Increasing_Subsequence_using_binary_search.cpp rename to search/longest_increasing_subsequence_using_binary_search.cpp From db3f9d34060ca40f7c29f3b61b40c8e80ba28148 Mon Sep 17 00:00:00 2001 From: Sanskruti Pravin Yeole <129084275+yeolesanskruti@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:21:55 +0530 Subject: [PATCH 6/7] Create Unbounded_knapsack.cpp (#2786) * Create Unbounded_knapsack.cpp * Update Unbounded_knapsack.cpp Documentation done. * Update dynamic_programming/Unbounded_knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update dynamic_programming/Unbounded_knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update dynamic_programming/Unbounded_knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update dynamic_programming/Unbounded_knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Delete dynamic_programming/Unbounded_knapsack.cpp * Create Unbounded_0_1_Knapsack.cpp * Update Unbounded_0_1_Knapsack.cpp * Update Unbounded_0_1_Knapsack.cpp * docs: add docs for main * Update dynamic_programming/Unbounded_0_1_Knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update dynamic_programming/Unbounded_0_1_Knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update Unbounded_0_1_Knapsack.cpp * Update Unbounded_0_1_Knapsack.cpp * Update Unbounded_0_1_Knapsack.cpp * Update dynamic_programming/Unbounded_0_1_Knapsack.cpp Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> * Update Unbounded_0_1_Knapsack.cpp * Update Unbounded_0_1_Knapsack.cpp --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- .../Unbounded_0_1_Knapsack.cpp | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 dynamic_programming/Unbounded_0_1_Knapsack.cpp diff --git a/dynamic_programming/Unbounded_0_1_Knapsack.cpp b/dynamic_programming/Unbounded_0_1_Knapsack.cpp new file mode 100644 index 000000000..96588fe39 --- /dev/null +++ b/dynamic_programming/Unbounded_0_1_Knapsack.cpp @@ -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 // Standard input-output stream +#include // Standard library for using dynamic arrays (vectors) +#include // For using assert function to validate test cases +#include // 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& val, + const std::vector& wt, + std::vector>& 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& val, + const std::vector& wt) { + if(N==0)return 0; // Expect 0 since no items + std::vector> dp(N, std::vector(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 wt1 = {1, 3, 4, 5}; // Weights of the items + std::vector 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 wt2 = {10, 20, 30}; // Weights of the items + std::vector 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 wt3 = {2, 4, 6}; // Weights of the items + std::vector 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 wt4 = {}; // Weights of the items + std::vector 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; +} + From 649a145234b2ce7842760bc18832b897f3ae22d3 Mon Sep 17 00:00:00 2001 From: Ritobroto Kalita Date: Tue, 29 Oct 2024 22:38:49 +0530 Subject: [PATCH 7/7] docs, test: Fit Sieve of Eratosthenes to contributing guidelines (#2803) * Update sieve_of_eratosthenes.cpp * Update doc --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com> --- math/sieve_of_eratosthenes.cpp | 94 ++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/math/sieve_of_eratosthenes.cpp b/math/sieve_of_eratosthenes.cpp index e011b6c00..e003706d1 100644 --- a/math/sieve_of_eratosthenes.cpp +++ b/math/sieve_of_eratosthenes.cpp @@ -1,6 +1,7 @@ /** * @file - * @brief Get list of prime numbers using Sieve of Eratosthenes + * @brief Prime Numbers using [Sieve of + * Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) * @details * Sieve of Eratosthenes is an algorithm that finds all the primes * between 2 and N. @@ -11,21 +12,39 @@ * @see primes_up_to_billion.cpp prime_numbers.cpp */ -#include -#include -#include +#include /// for assert +#include /// for IO operations +#include /// for std::vector /** - * This is the function that finds the primes and eliminates the multiples. + * @namespace math + * @brief Mathematical algorithms + */ +namespace math { +/** + * @namespace sieve_of_eratosthenes + * @brief Functions for finding Prime Numbers using Sieve of Eratosthenes + */ +namespace sieve_of_eratosthenes { +/** + * @brief Function to sieve out the primes + * @details + * This function finds all the primes between 2 and N using the Sieve of + * Eratosthenes algorithm. It starts by assuming all numbers (except zero and + * one) are prime and then iteratively marks the multiples of each prime as + * non-prime. + * * Contains a common optimization to start eliminating multiples of * a prime p starting from p * p since all of the lower multiples * have been already eliminated. - * @param N number of primes to check - * @return is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not + * @param N number till which primes are to be found + * @return is_prime a vector of `N + 1` booleans identifying if `i`^th number is + * a prime or not */ std::vector sieve(uint32_t N) { - std::vector is_prime(N + 1, true); - is_prime[0] = is_prime[1] = false; + std::vector is_prime(N + 1, true); // Initialize all as prime numbers + is_prime[0] = is_prime[1] = false; // 0 and 1 are not prime numbers + for (uint32_t i = 2; i * i <= N; i++) { if (is_prime[i]) { for (uint32_t j = i * i; j <= N; j += i) { @@ -37,9 +56,10 @@ std::vector sieve(uint32_t N) { } /** - * This function prints out the primes to STDOUT - * @param N number of primes to check - * @param is_prime a vector of `N + 1` booleans identifying if `i`^th number is a prime or not + * @brief Function to print the prime numbers + * @param N number till which primes are to be found + * @param is_prime a vector of `N + 1` booleans identifying if `i`^th number is + * a prime or not */ void print(uint32_t N, const std::vector &is_prime) { for (uint32_t i = 2; i <= N; i++) { @@ -50,23 +70,53 @@ void print(uint32_t N, const std::vector &is_prime) { std::cout << std::endl; } +} // namespace sieve_of_eratosthenes +} // namespace math + /** - * Test implementations + * @brief Self-test implementations + * @return void */ -void tests() { - // 0 1 2 3 4 5 6 7 8 9 10 - std::vector ans{false, false, true, true, false, true, false, true, false, false, false}; - assert(sieve(10) == ans); +static void tests() { + std::vector is_prime_1 = + math::sieve_of_eratosthenes::sieve(static_cast(10)); + std::vector is_prime_2 = + math::sieve_of_eratosthenes::sieve(static_cast(20)); + std::vector is_prime_3 = + math::sieve_of_eratosthenes::sieve(static_cast(100)); + + std::vector expected_1{false, false, true, true, false, true, + false, true, false, false, false}; + assert(is_prime_1 == expected_1); + + std::vector expected_2{false, false, true, true, false, true, + false, true, false, false, false, true, + false, true, false, false, false, true, + false, true, false}; + assert(is_prime_2 == expected_2); + + std::vector expected_3{ + false, false, true, true, false, true, false, true, false, false, + false, true, false, true, false, false, false, true, false, true, + false, false, false, true, false, false, false, false, false, true, + false, true, false, false, false, false, false, true, false, false, + false, true, false, true, false, false, false, true, false, false, + false, false, false, true, false, false, false, false, false, true, + false, true, false, false, false, false, false, true, false, false, + false, true, false, true, false, false, false, false, false, true, + false, false, false, true, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, false, false, + false}; + assert(is_prime_3 == expected_3); + + std::cout << "All tests have passed successfully!\n"; } /** - * Main function + * @brief Main function + * @returns 0 on exit */ int main() { tests(); - - uint32_t N = 100; - std::vector is_prime = sieve(N); - print(N, is_prime); return 0; }