From 20f74d413866f05beb2786994fcf8962361a6f0d Mon Sep 17 00:00:00 2001 From: Swastika Gupta <64654203+Swastyy@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:30:42 +0530 Subject: [PATCH] feat: Karatsuba algorithm (#1550) * karatsuba algorithm * Update karatsuba_algorithm_for_fast_multiplication.cpp * Update karatsuba_algorithm_for_fast_multiplication.cpp * Update karatsuba_algorithm_for_fast_multiplication.cpp * Update karatsuba_algorithm_for_fast_multiplication.cpp * Update karatsuba_algorithm_for_fast_multiplication.cpp * Update karatsuba_algorithm_for_fast_multiplication.cpp * updating DIRECTORY.md * clang-format and clang-tidy fixes for 180f356d * Update divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp Co-authored-by: David Leal * Update divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp Co-authored-by: David Leal * Update divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp Co-authored-by: David Leal * clang-format and clang-tidy fixes for 2b8d67ad * Apply suggestions from code review Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal Co-authored-by: Ayaan Khan --- DIRECTORY.md | 3 + ...suba_algorithm_for_fast_multiplication.cpp | 167 ++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp diff --git a/DIRECTORY.md b/DIRECTORY.md index 4bd8db0e8..f2cb78cba 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -66,6 +66,9 @@ * [Trie Tree](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_tree.cpp) * [Trie Using Hashmap](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/data_structures/trie_using_hashmap.cpp) +## Divide And Conquer + * [Karatsuba Algorithm For Fast Multiplication](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp) + ## Dynamic Programming * [0 1 Knapsack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/0_1_knapsack.cpp) * [Abbreviation](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/dynamic_programming/abbreviation.cpp) diff --git a/divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp b/divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp new file mode 100644 index 000000000..1782d0a45 --- /dev/null +++ b/divide_and_conquer/karatsuba_algorithm_for_fast_multiplication.cpp @@ -0,0 +1,167 @@ +/** + * @file + * @brief Implementation of the [Karatsuba algorithm for fast + * multiplication](https://en.wikipedia.org/wiki/Karatsuba_algorithm) + * @details + * Given two strings in binary notation we want to multiply them and return the + * value Simple approach is to multiply bits one by one which will give the time + * complexity of around O(n^2). To make it more efficient we will be using + * Karatsuba' algorithm to find the product which will solve the problem + * O(nlogn) of time. + * @author [Swastika Gupta](https://github.com/Swastyy) + */ + +#include /// for assert +#include /// for string +#include /// for IO operations +#include /// for std::vector + +/** + * @namespace divide_and_conquer + * @brief Divide and Conquer algorithms + */ +namespace divide_and_conquer { +/** + * @namespace karatsuba_algorithm + * @brief Functions for the [Karatsuba algorithm for fast + * multiplication](https://en.wikipedia.org/wiki/Karatsuba_algorithm) + */ +namespace karatsuba_algorithm { +/** + * @brief Helper function for the main function, that implements Karatsuba's + * algorithm for fast multiplication + * @param first the input string 1 + * @param second the input string 2 + * @returns the concatenated string + */ +std::string addStrings(std::string first, std::string second) { + std::string result; // To store the resulting sum bits + + int64_t len1 = first.size(); + int64_t len2 = second.size(); + int64_t length = std::max(len1, len2); + std::string zero = "0"; + if (len1 < len2) // make the string lengths equal + { + for (int64_t i = 0; i < len2 - len1; i++) { + zero += first; + first = zero; + } + } else if (len1 > len2) { + zero = "0"; + for (int64_t i = 0; i < len1 - len2; i++) { + zero += second; + second = zero; + } + } + int64_t carry = 0; + for (int64_t i = length - 1; i >= 0; i--) { + int64_t firstBit = first.at(i) - '0'; + int64_t secondBit = second.at(i) - '0'; + + int64_t sum = (firstBit ^ secondBit ^ carry) + '0'; // sum of 3 bits + std::string temp; + temp = std::to_string(sum); + temp += result; + result = temp; + + carry = (firstBit & secondBit) | (secondBit & carry) | + (firstBit & carry); // sum of 3 bits + } + + if (carry) { + result = '1' + result; // adding 1 incase of overflow + } + return result; +} +/** + * @brief The main function implements Karatsuba's algorithm for fast + * multiplication + * @param str1 the input string 1 + * @param str2 the input string 2 + * @returns the multiplicative number value + */ +int64_t karatsuba_algorithm(std::string str1, std::string str2) { + int64_t len1 = str1.size(); + int64_t len2 = str2.size(); + int64_t n = std::max(len1, len2); + std::string zero = "0"; + if (len1 < len2) { + for (int64_t i = 0; i < len2 - len1; i++) { + zero += str1; + str1 = zero; + } + } else if (len1 > len2) { + zero = "0"; + for (int64_t i = 0; i < len1 - len2; i++) { + zero += str2; + str2 = zero; + } + } + if (n == 0) { + return 0; + } + if (n == 1) { + return (str1[0] - '0') * (str2[0] - '0'); + } + int64_t fh = n / 2; // first half of string + int64_t sh = (n - fh); // second half of string + + std::string Xl = str1.substr(0, fh); // first half of first string + std::string Xr = str1.substr(fh, sh); // second half of first string + + std::string Yl = str2.substr(0, fh); // first half of second string + std::string Yr = str2.substr(fh, sh); // second half of second string + + // Calculating the three products of inputs of size n/2 recursively + int64_t product1 = karatsuba_algorithm(Xl, Yl); + int64_t product2 = karatsuba_algorithm(Xr, Yr); + int64_t product3 = karatsuba_algorithm( + divide_and_conquer::karatsuba_algorithm::addStrings(Xl, Xr), + divide_and_conquer::karatsuba_algorithm::addStrings(Yl, Yr)); + + return product1 * (1 << (2 * sh)) + + (product3 - product1 - product2) * (1 << sh) + + product2; // combining the three products to get the final result. +} +} // namespace karatsuba_algorithm +} // namespace divide_and_conquer + +/** + * @brief Self-test implementations + * @returns void + */ +static void test() { + // 1st test + std::string s11 = "1"; + std::string s12 = "1010"; + std::cout << "1st test... "; + assert(divide_and_conquer::karatsuba_algorithm::karatsuba_algorithm( + s11, s12) == 10); // here the multiplication is 10 + std::cout << "passed" << std::endl; + + // 2nd test + std::string s21 = "11"; + std::string s22 = "1010"; + std::cout << "2nd test... "; + assert(divide_and_conquer::karatsuba_algorithm::karatsuba_algorithm( + s21, s22) == 30); // here the multiplication is 30 + std::cout << "passed" << std::endl; + + // 3rd test + std::string s31 = "110"; + std::string s32 = "1010"; + std::cout << "3rd test... "; + assert(divide_and_conquer::karatsuba_algorithm::karatsuba_algorithm( + s31, s32) == 60); // here the multiplication is 60 + std::cout << "passed" << std::endl; +} + +/** + * @brief Main function + * @returns 0 on exit + */ +int main() { + test(); // run self-test implementations + return 0; +}