From c43b5b2ebf3b602a61494c7ffe82cea77ec09982 Mon Sep 17 00:00:00 2001 From: madhu90 Date: Thu, 20 Jun 2019 15:06:56 -0400 Subject: [PATCH 1/4] Implementation of Karatsuba multiplication --- karatsubaMultiplication.cpp | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 karatsubaMultiplication.cpp diff --git a/karatsubaMultiplication.cpp b/karatsubaMultiplication.cpp new file mode 100644 index 0000000..67fc489 --- /dev/null +++ b/karatsubaMultiplication.cpp @@ -0,0 +1,101 @@ +// Implementation of Karatsuba multiplication +#include +#include +#include +#include +#include + +// x = (10^n/2)*a + b +// y = (10^n/2)*c + d +// x*y = ((10^n/2)*a + b)*((10^n/2)*c + d) +// = (10^n)*ac + (10^n/2)*(ad + bc) + bd + +class Karatsuba +{ +private: + unsigned int f1; + unsigned int f2; + unsigned int numdigits; + +public: + Karatsuba(unsigned int val1, unsigned int val2) + : f1(val1), f2(val2) + { + numdigits = std::to_string(std::max(f1, f2)).length(); + } + + unsigned int computeProduct(void) + { + return compute(f1, f2, numdigits); + } + + void printLookupContents() + { + std::cout << "Lookup map contents" << std::endl; + for (const auto& iter : lookup) + { + std::cout << iter.first << " ---> " << iter.second << std::endl; + } + } + +private: + unsigned int compute(unsigned int val1, unsigned int val2, unsigned int numdigits) + { + if (numdigits == 1) + return val1 * val2; + + unsigned int newdigits = numdigits / 2; + unsigned int a = val1 / std::pow(10, newdigits); + unsigned int b = val1 % static_cast(std::pow(10, newdigits)); + unsigned int c = val2 / std::pow(10, newdigits); + unsigned int d = val2 % static_cast(std::pow(10, newdigits)); + + // Check map if entry exists + unsigned int ac = 0; + unsigned int bd = 0; + unsigned int ad_bc = 0; + + // ac + std::string key1 = std::to_string(a) + std::string("*") + std::to_string(c); + if (lookup.find(key1) == lookup.end()) { + lookup[key1] = compute(a, c, newdigits); + ac = lookup[key1]; + } else { + ac = lookup[key1]; + } + + // bd + std::string key2 = std::to_string(b) + std::string("*") + std::to_string(d); + if (lookup.find(key2) == lookup.end()) { + lookup[key2] = compute(b, d, newdigits); + bd = lookup[key2]; + } else { + bd = lookup[key2]; + } + + // ad + bc + std::string key3 = std::to_string(a) + std::to_string(d) + std::string("*") + std::to_string(b) + std::to_string(c); + if (lookup.find(key3) == lookup.end()) { + lookup[key3] = compute((a + b), (c + d), newdigits) - ac - bd; + ad_bc = lookup[key3]; + } else { + ad_bc = lookup[key3]; + } + + return std::pow(10, numdigits)*ac + std::pow(10, newdigits)*ad_bc + bd; + } + +private: + std::unordered_map lookup; +}; + +int main() +{ + unsigned int value1 = 1234; + unsigned int value2 = 5678; + Karatsuba calc(value1, value2); + unsigned int result = calc.computeProduct(); + std::cout << "Result of " << value1 << "*" << value2 << " " << result << std::endl; + calc.printLookupContents(); + return 0; +} From 62e7d85654dd820be34b86f00cc0a023270048e3 Mon Sep 17 00:00:00 2001 From: madhu90 Date: Fri, 21 Jun 2019 15:02:15 -0400 Subject: [PATCH 2/4] Accounting for different number of digits --- karatsubaMultiplication.cpp | 96 +++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/karatsubaMultiplication.cpp b/karatsubaMultiplication.cpp index 67fc489..25fa875 100644 --- a/karatsubaMultiplication.cpp +++ b/karatsubaMultiplication.cpp @@ -5,28 +5,37 @@ #include #include +// If x and y have same lengths // x = (10^n/2)*a + b // y = (10^n/2)*c + d // x*y = ((10^n/2)*a + b)*((10^n/2)*c + d) // = (10^n)*ac + (10^n/2)*(ad + bc) + bd +// If x and y have different lengths +// x = (10^n/2)*a + b +// y = (10^m/2)*c + d +// x*y = ((10^n/2)*a + b)*((10^m/2)*c + d) +// = (10^(n+m)/2)*ac + (10^n/2)*ad + (10^m/2)*bc + bd + class Karatsuba { private: unsigned int f1; unsigned int f2; - unsigned int numdigits; + unsigned int numdigits1; + unsigned int numdigits2; public: Karatsuba(unsigned int val1, unsigned int val2) : f1(val1), f2(val2) { - numdigits = std::to_string(std::max(f1, f2)).length(); + numdigits1 = std::to_string(f1).length(); + numdigits2 = std::to_string(f2).length(); } unsigned int computeProduct(void) { - return compute(f1, f2, numdigits); + return compute(f1, f2, numdigits1, numdigits2); } void printLookupContents() @@ -39,60 +48,65 @@ class Karatsuba } private: - unsigned int compute(unsigned int val1, unsigned int val2, unsigned int numdigits) - { - if (numdigits == 1) - return val1 * val2; - - unsigned int newdigits = numdigits / 2; - unsigned int a = val1 / std::pow(10, newdigits); - unsigned int b = val1 % static_cast(std::pow(10, newdigits)); - unsigned int c = val2 / std::pow(10, newdigits); - unsigned int d = val2 % static_cast(std::pow(10, newdigits)); - - // Check map if entry exists + unsigned int compute(unsigned int val1, + unsigned int val2, + unsigned int m, + unsigned int n) + { + unsigned int a = 0; + unsigned int b = 0; + unsigned int c = 0; + unsigned int d = 0; unsigned int ac = 0; unsigned int bd = 0; - unsigned int ad_bc = 0; + unsigned int ad = 0; + unsigned int bc = 0; + unsigned int new_m = m; + unsigned int new_n = n; - // ac - std::string key1 = std::to_string(a) + std::string("*") + std::to_string(c); - if (lookup.find(key1) == lookup.end()) { - lookup[key1] = compute(a, c, newdigits); - ac = lookup[key1]; + if (new_m == 1 && new_n == 1) { + return val1 * val2; + } else if (new_m == 1) { + // Split y + a = val1; + b = 1; + splitData(c, d, val2, new_n); + } else if (new_n == 1) { + // Split x + c = val2; + d = 1; + splitData(a, b, val1, new_m); } else { - ac = lookup[key1]; + // Split x and y + splitData(a, b, val1, new_m); + splitData(c, d, val2, new_n); } + + // ac + ac = compute(a, c, new_m, new_n); // bd - std::string key2 = std::to_string(b) + std::string("*") + std::to_string(d); - if (lookup.find(key2) == lookup.end()) { - lookup[key2] = compute(b, d, newdigits); - bd = lookup[key2]; - } else { - bd = lookup[key2]; - } + bd = compute(b, d, new_m, new_n); // ad + bc - std::string key3 = std::to_string(a) + std::to_string(d) + std::string("*") + std::to_string(b) + std::to_string(c); - if (lookup.find(key3) == lookup.end()) { - lookup[key3] = compute((a + b), (c + d), newdigits) - ac - bd; - ad_bc = lookup[key3]; - } else { - ad_bc = lookup[key3]; - } + ad = compute(a, d, new_m, new_n); + bc = compute(b, c, new_m, new_n); - return std::pow(10, numdigits)*ac + std::pow(10, newdigits)*ad_bc + bd; + return std::pow(10, (m + n)/2)*ac + std::pow(10, n/ 2)*ad + std::pow(10, m / 2)*bc + bd; } -private: - std::unordered_map lookup; + void splitData(unsigned int& c, unsigned int &d, unsigned int val, unsigned int& n) + { + n = n / 2; + c = val / std::pow(10, n); + d = val % static_cast(std::pow(10, n)); + } }; int main() { - unsigned int value1 = 1234; - unsigned int value2 = 5678; + unsigned int value1 = 12345; + unsigned int value2 = 56783; Karatsuba calc(value1, value2); unsigned int result = calc.computeProduct(); std::cout << "Result of " << value1 << "*" << value2 << " " << result << std::endl; From a6c9fbb4fb2820d9f094fad7097ff1b71521bfaa Mon Sep 17 00:00:00 2001 From: madhu90 Date: Sun, 23 Jun 2019 20:25:16 -0400 Subject: [PATCH 3/4] Accounting for unequal lengths and odd number of digits --- karatsubaMultiplication.cpp | 139 ++++++++++++++---------------------- 1 file changed, 55 insertions(+), 84 deletions(-) diff --git a/karatsubaMultiplication.cpp b/karatsubaMultiplication.cpp index 25fa875..b9ebe7d 100644 --- a/karatsubaMultiplication.cpp +++ b/karatsubaMultiplication.cpp @@ -11,105 +11,76 @@ // x*y = ((10^n/2)*a + b)*((10^n/2)*c + d) // = (10^n)*ac + (10^n/2)*(ad + bc) + bd -// If x and y have different lengths -// x = (10^n/2)*a + b -// y = (10^m/2)*c + d -// x*y = ((10^n/2)*a + b)*((10^m/2)*c + d) -// = (10^(n+m)/2)*ac + (10^n/2)*ad + (10^m/2)*bc + bd - class Karatsuba { private: - unsigned int f1; - unsigned int f2; - unsigned int numdigits1; - unsigned int numdigits2; + unsigned int f1; + unsigned int f2; + std::string f1String; + std::string f2String; public: - Karatsuba(unsigned int val1, unsigned int val2) - : f1(val1), f2(val2) - { - numdigits1 = std::to_string(f1).length(); - numdigits2 = std::to_string(f2).length(); - } + Karatsuba(unsigned int val1, unsigned int val2) + : f1(val1), f2(val2) + { + f1String = std::to_string(f1); + f2String = std::to_string(f2); + } - unsigned int computeProduct(void) - { - return compute(f1, f2, numdigits1, numdigits2); - } - - void printLookupContents() - { - std::cout << "Lookup map contents" << std::endl; - for (const auto& iter : lookup) - { - std::cout << iter.first << " ---> " << iter.second << std::endl; - } - } + unsigned int computeProduct(void) + { + return compute(f1String, f2String); + } private: - unsigned int compute(unsigned int val1, - unsigned int val2, - unsigned int m, - unsigned int n) - { - unsigned int a = 0; - unsigned int b = 0; - unsigned int c = 0; - unsigned int d = 0; - unsigned int ac = 0; - unsigned int bd = 0; - unsigned int ad = 0; - unsigned int bc = 0; - unsigned int new_m = m; - unsigned int new_n = n; - if (new_m == 1 && new_n == 1) { - return val1 * val2; - } else if (new_m == 1) { - // Split y - a = val1; - b = 1; - splitData(c, d, val2, new_n); - } else if (new_n == 1) { - // Split x - c = val2; - d = 1; - splitData(a, b, val1, new_m); - } else { - // Split x and y - splitData(a, b, val1, new_m); - splitData(c, d, val2, new_n); - } - - // ac - ac = compute(a, c, new_m, new_n); + unsigned int padZeros(std::string& str1, std::string& str2) + { + if (str1.length() > str2.length()) { + str2.insert(0, std::string(str1.length() - str2.length(), '0')); + } else if (str1.length() < str2.length()) { + str1.insert(0, std::string(str2.length() - str1.length(), '0')); + } + + return str1.length(); + } + + unsigned long long int compute( + std::string num1, + std::string num2) + { + unsigned int length = padZeros(num1, num2); + unsigned long long int val1 = static_cast(std::stoi(num1)); + unsigned long long int val2 = static_cast(std::stoi(num2)); - // bd - bd = compute(b, d, new_m, new_n); + if (length == 1) + return val1 * val2; - // ad + bc - ad = compute(a, d, new_m, new_n); - bc = compute(b, c, new_m, new_n); + unsigned int left_idx = length / 2; + unsigned int right_idx = length - left_idx; + + std::string a = num1.substr(0, left_idx); + std::string b = num1.substr(left_idx, right_idx); + std::string c = num2.substr(0, left_idx); + std::string d = num2.substr(left_idx, right_idx); - return std::pow(10, (m + n)/2)*ac + std::pow(10, n/ 2)*ad + std::pow(10, m / 2)*bc + bd; - } + unsigned long long int ac = compute(a, c); + unsigned long long int bd = compute(b, d); + unsigned long long int ad_bc = compute(std::to_string(std::stoi(a) + std::stoi(b)), std::to_string(std::stoi(c)+std::stoi(d))) - ac - bd; + + std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << std::endl; + std::cout << " ac = " << ac << ", bd = " << bd << ", ad + bc = " << ad_bc << std::endl; - void splitData(unsigned int& c, unsigned int &d, unsigned int val, unsigned int& n) - { - n = n / 2; - c = val / std::pow(10, n); - d = val % static_cast(std::pow(10, n)); - } + return std::pow(10, 2*right_idx)*ac + std::pow(10, right_idx)*ad_bc + bd; + } }; int main() { - unsigned int value1 = 12345; - unsigned int value2 = 56783; - Karatsuba calc(value1, value2); - unsigned int result = calc.computeProduct(); - std::cout << "Result of " << value1 << "*" << value2 << " " << result << std::endl; - calc.printLookupContents(); - return 0; + unsigned int value1 = 123; + unsigned int value2 = 5678; + Karatsuba calc(value1, value2); + unsigned int result = calc.computeProduct(); + std::cout << "Result of " << value1 << "*" << value2 << " " << result << std::endl; + return 0; } From 96d84c0ed7be2fa9ed9704d0fd5bd5d05fdb0117 Mon Sep 17 00:00:00 2001 From: madhu90 Date: Sun, 23 Jun 2019 20:38:12 -0400 Subject: [PATCH 4/4] Justification for using right_index as 10s exponent --- karatsubaMultiplication.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/karatsubaMultiplication.cpp b/karatsubaMultiplication.cpp index b9ebe7d..ce0ab83 100644 --- a/karatsubaMultiplication.cpp +++ b/karatsubaMultiplication.cpp @@ -70,7 +70,21 @@ class Karatsuba std::cout << "a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << std::endl; std::cout << " ac = " << ac << ", bd = " << bd << ", ad + bc = " << ad_bc << std::endl; - + + /* Why do we multiply by right_idx here and not length/2 ? + * Consider a situation where we have odd length numbers: + * x = 12345 ----> a = 12, b = 345 + * y = 05678 ----> c = 05, d = 678 + * Here, how are a,b and c,d combined to get x and y? + * x = 12*1000 + 345 + * y = 5*1000 + 345 + * The power of 10 is really the length of the right half + * because that is how much the left half must be shifted + * Also, in our split for odd length data, the left will always + * be shorter (floor(length/2)) and the right will be larger + * ceil(length/2). So, we always pick the length of the right + * half and use that as 10's exponent + */ return std::pow(10, 2*right_idx)*ac + std::pow(10, right_idx)*ad_bc + bd; } };