Karatsuba algorithm for fast multiplication using Divide and Conquer algorithm - GeeksforGeeks

23 Feb, 2022
Given two binary strings that represent value of two integers, find the product of two strings. For example, if the first bit string is “1100” and second bit string is “1010”, output should be 120.
For simplicity, let the length of two strings be same and be n.
A Naive Approach is to follow the process we study in school. One by one take all bits of second number and multiply it with all bits of first number. Finally add all multiplications. This algorithm takes O(n^2) time.
Using Divide and Conquer, we can multiply two integers in less time complexity. We divide the given numbers in two halves. Let the given numbers be X and Y.
For simplicity let us assume that n is even 
X =  Xl*2n/2 + Xr    [Xl and Xr contain leftmost and rightmost n/2 bits of X]
Y =  Yl*2n/2 + Yr    [Yl and Yr contain leftmost and rightmost n/2 bits of Y]
The product XY can be written as following. 
XY = (Xl*2n/2 + Xr)(Yl*2n/2 + Yr)
   = 2n XlYl + 2n/2(XlYr + XrYl) + XrYr
If we take a look at the above formula, there are four multiplications of size n/2, so we basically divided the problem of size n into four sub-problems of size n/2. But that doesn’t help because solution of recurrence T(n) = 4T(n/2) + O(n) is O(n^2). The tricky part of this algorithm is to change the middle two terms to some other form so that only one extra multiplication would be sufficient. The following is tricky expression for middle two terms.  
XlYr + XrYl = (Xl + Xr)(Yl + Yr) - XlYl- XrYr
So the final value of XY becomes  
XY = 2n XlYl + 2n/2 * [(Xl + Xr)(Yl + Yr) - XlYl - XrYr] + XrYr
With above trick, the recurrence becomes T(n) = 3T(n/2) + O(n) and solution of this recurrence is O(n1.59).
What if the lengths of input strings are different and are not even? To handle the different length case, we append 0’s in the beginning. To handle odd length, we put floor(n/2) bits in left half and ceil(n/2) bits in right half. So the expression for XY changes to following.  
XY = 22ceil(n/2) XlYl + 2ceil(n/2) * [(Xl + Xr)(Yl + Yr) - XlYl - XrYr] + XrYr
The above algorithm is called Karatsuba algorithm and it can be used for any base. 
Following is C++ implementation of above algorithm.
C++
// C++ implementation of Karatsuba algorithm for bit string multiplication.#include<iostream>#include<stdio.h> using namespace std; // FOLLOWING TWO FUNCTIONS ARE COPIED FROM http://goo.gl/q0OhZ// Helper method: given two unequal sized bit strings, converts them to// same length by adding leading 0s in the smaller string. Returns the// the new lengthint makeEqualLength(string &str1, string &str2){    int len1 = str1.size();    int len2 = str2.size();    if (len1 < len2)    {        for (int i = 0 ; i < len2 - len1 ; i++)            str1 = '0' + str1;        return len2;    }    else if (len1 > len2)    {        for (int i = 0 ; i < len1 - len2 ; i++)            str2 = '0' + str2;    }    return len1; // If len1 >= len2} // The main function that adds two bit sequences and returns the additionstring addBitStrings( string first, string second ){    string result;  // To store the sum bits     // make the lengths same before adding    int length = makeEqualLength(first, second);    int carry = 0;  // Initialize carry     // Add all bits one by one    for (int i = length-1 ; i >= 0 ; i--)    {        int firstBit = first.at(i) - '0';        int secondBit = second.at(i) - '0';         // boolean expression for sum of 3 bits        int sum = (firstBit ^ secondBit ^ carry)+'0';         result = (char)sum + result;         // boolean expression for 3-bit addition        carry = (firstBit&secondBit) | (secondBit&carry) | (firstBit&carry);    }     // if overflow, then add a leading 1    if (carry)  result = '1' + result;     return result;} // A utility function to multiply single bits of strings a and bint multiplyiSingleBit(string a, string b){  return (a[0] - '0')*(b[0] - '0');  } // The main function that multiplies two bit strings X and Y and returns// result as long integerlong int multiply(string X, string Y){    // Find the maximum of lengths of x and Y and make length    // of smaller string same as that of larger string    int n = makeEqualLength(X, Y);     // Base cases    if (n == 0) return 0;    if (n == 1) return multiplyiSingleBit(X, Y);     int fh = n/2;   // First half of string, floor(n/2)    int sh = (n-fh); // Second half of string, ceil(n/2)     // Find the first half and second half of first string.    // Refer http://goo.gl/lLmgn for substr method    string Xl = X.substr(0, fh);    string Xr = X.substr(fh, sh);     // Find the first half and second half of second string    string Yl = Y.substr(0, fh);    string Yr = Y.substr(fh, sh);     // Recursively calculate the three products of inputs of size n/2    long int P1 = multiply(Xl, Yl);    long int P2 = multiply(Xr, Yr);    long int P3 = multiply(addBitStrings(Xl, Xr), addBitStrings(Yl, Yr));     // Combine the three products to get the final result.    return P1*(1<<(2*sh)) + (P3 - P1 - P2)*(1<<sh) + P2;} // Driver program to test above functionsint main(){    printf ("%ld\n", multiply("1100", "1010"));    printf ("%ld\n", multiply("110", "1010"));    printf ("%ld\n", multiply("11", "1010"));    printf ("%ld\n", multiply("1", "1010"));    printf ("%ld\n", multiply("0", "1010"));    printf ("%ld\n", multiply("111", "111"));    printf ("%ld\n", multiply("11", "11"));}
Output: 
120
60
30
10
0
49
9
Time Complexity: Time complexity of the above solution is O(nlog23) = O(n1.59).Time complexity of multiplication can be further improved using another Divide and Conquer algorithm, fast Fourier transform. We will soon be discussing fast Fourier transform as a separate post.
Exercise The above program returns a long int value and will not work for big strings. Extend the above program to return a string instead of a long int value.
SolutionMultiplication process for large numbers is an important problem in Computer Science. Given approach uses Divide and Conquer methodology. Run the code to see the time complexity comparison for normal Binary Multiplication and Karatsuba Algorithm. You can see the full code in this repository
Examples: 
First Binary Input : 101001010101010010101001010100101010010101010010101 
Second Binary Input : 101001010101010010101001010100101010010101010010101
Decimal Output : Not Representable 
Output : 2.1148846e+30
First Binary Input : 1011 
Second Binary Input : 1000
Decimal Output : 88
Output : 5e-05
C++
#include <iostream>#include <ctime>#include <fstream>#include <string.h>#include <cmath>#include <sstream> using namespace std; // classical method classclass BinaryMultiplier{public:    string MakeMultiplication(string,string);         string MakeShifting(string,int);                  string addBinary(string,string);    void BinaryStringToDecimal(string);}; // karatsuba method classclass Karatsuba{public:    int lengthController(string &,string &);    string addStrings(string,string);    string multiply(string,string);    string DecimalToBinary(long long int);    string Subtraction(string,string);    string MakeShifting(string,int);}; // this function get strings and go over str2 bit// if it sees 1  it calculates the shifted version according to position bit// Makes add operation for binary strings// returns result stringstring BinaryMultiplier::MakeMultiplication(string str1, string str2){    string allSum = "";    for (int j = 0 ; j<str2.length(); j++)    {        int secondDigit = str2[j] - '0';        if (secondDigit == 1)        {            string shifted = MakeShifting(str1,str2.size()-(j+1));            allSum = addBinary(shifted, allSum);        }        else        {            continue;        }             }    return allSum;}  // this function adds binary strings with carrystring BinaryMultiplier::addBinary(string a, string b){    string result = "";    int s = 0;         int i = a.size() - 1;    int j = b.size() - 1;    while (i >= 0 || j >= 0 || s == 1)    {        s += ((i >= 0)? a[i] - '0': 0);        s += ((j >= 0)? b[j] - '0': 0);                 result = char(s % 2 + '0') + result;                 s /= 2;             i--;        j--;    }    return result;} // this function shifts the given string according to given number// returns shifted versionstring BinaryMultiplier::MakeShifting(string str, int stepnum){    string shifted = str;    for (int i = 0 ; i < stepnum ; i++)        shifted = shifted + '0';    return shifted;} // this function converts Binary String Number to Decimal Number// After 32 bits it gives 0 because it overflows the size of intvoid BinaryMultiplier::BinaryStringToDecimal(string result){    cout<<"Binary Result : "<<result<<endl;    unsigned long long int val = 0;    for (int i = result.length()-1; i >= 0; i--)    {        if (result[i] == '1')        {            val += pow(2,(result.length()-1)-i);        }    }    cout<<"Decimal Result (Not proper for Large Binary Numbers):" <<val<<endl;} // this function controls lengths of strings and make their lengths equal// returns the maximum lengthint Karatsuba::lengthController(string &str1, string &str2){    int len1 = str1.size();    int len2 = str2.size();    if (len1 < len2)    {        for (int i = 0 ; i < len2 - len1 ; i++)            str1 = '0' + str1;        return len2;    }    else if (len1 > len2)    {        for (int i = 0 ; i < len1 - len2 ; i++)            str2 = '0' + str2;    }    return len1;} // this function add strings with carry// uses one by one bit addition methodology// returns result stringstring Karatsuba::addStrings(string first, string second){    string result;  // To store the sum bits         // make the lengths same before adding    int length = lengthController(first, second);    int carry = 0;  // Initialize carry         // Add all bits one by one    for (int i = length-1 ; i >= 0 ; i--)    {        int firstBit = first.at(i) - '0';        int secondBit = second.at(i) - '0';                 // boolean expression for sum of 3 bits        int sum = (firstBit ^ secondBit ^ carry)+'0';                 result = (char)sum + result;                 // Boolean expression for 3-bit addition        carry = (firstBit&secondBit) | (secondBit&carry) | (firstBit&carry);    }         // if overflow, then add a leading 1    if (carry)    {        result = '1' + result;    }         return result;} // this function converts decimal number to binary stringstring Karatsuba::DecimalToBinary(long long int number){    string result = "";    if (number <= 0)    {        return "0";    }    else    {        int i = 0;        while (number > 0)        {                         long long int num= number % 2;            stringstream ss;            ss<<num;            result = ss.str() + result;            number = number / 2;            i++;        }        return result;             }} // this function makes binary string subtraction with overflowstring Karatsuba::Subtraction(string lhs, string rhs){         int length = lengthController(lhs, rhs);    int diff;    string result;         for (int i = length-1; i >= 0; i--)    {        diff = (lhs[i]-'0') - (rhs[i]-'0');        if (diff >= 0)        {            result = DecimalToBinary(diff) + result;        }        else        {            for (int j = i-1; j>=0; j--)            {                lhs[j] = ((lhs[j]-'0') - 1) % 10 + '0';                if (lhs[j] != '1')                {                    break;                }            }            result = DecimalToBinary(diff+2) + result;        }    }    return result;} // this function makes shiftingstring Karatsuba::MakeShifting(string str, int stepnum){    string shifted = str;    for (int i = 0 ; i < stepnum ; i++)        shifted = shifted + '0';    return shifted;} // this function is the core of the Karatsuba// divides problem into 4 subproblems// recursively multiplies them// returns the result stringstring Karatsuba::multiply(string X,  string Y){    int n = lengthController(X, Y);         if (n == 1) return ((Y[0]-'0' == 1) && (X[0]-'0' == 1)) ? "1" : "0";         int fh = n/2;   // First half of string, floor(n/2)    int sh = (n-fh); // Second half of string, ceil(n/2)         // Find the first half and second half of first string.    string Xl = X.substr(0, fh);    string Xr = X.substr(fh, sh);         // Find the first half and second half of second string    string Yl = Y.substr(0, fh);    string Yr = Y.substr(fh, sh);         // Recursively calculate the three products of inputs of size n/2    string P1 = multiply(Xl, Yl);    string P2 = multiply(Xr, Yr);    string P3 = multiply(addStrings(Xl, Xr), addStrings(Yl, Yr));         // return added string version    return addStrings(addStrings(MakeShifting(P1, 2*(n-n/2)),P2),MakeShifting(Subtraction(P3,addStrings(P1,P2)), n-(n/2)));}  int main(int argc, const char * argv[]){    // get the binary numbers as strings    string firstNumber,secondNumber;       cout<<"Please give the First Binary number : ";    cin>>firstNumber;    cout<<endl<<"Please give the Second Binary number : ";    cin>>secondNumber;    cout << endl;          // make the initial lengths equal by adding zeros    int len1 = firstNumber.size();    int len2 = secondNumber.size();    int general_len = firstNumber.size();         if (len1 < len2)    {        for (int i = 0 ; i < len2 - len1 ; i++)            firstNumber = '0' + firstNumber;        general_len = firstNumber.size();    }    else if (len1 > len2)    {        for (int i = 0 ; i < len1 - len2 ; i++)            secondNumber = '0' + secondNumber;        general_len = secondNumber.size();    }         // In classical methodology Binary String Multiplication    cout<<"Classical Algorithm : "<<endl;    BinaryMultiplier newobj;    const clock_t classical_time = clock();    string classic = newobj.MakeMultiplication(firstNumber, secondNumber);    cout << float( clock () - classical_time ) /  CLOCKS_PER_SEC<<endl<<endl;    float c_time = float( clock () - classical_time ) /  CLOCKS_PER_SEC;    newobj.BinaryStringToDecimal(classic);         // Using Karatsuba Multiplication Algorithm Binary String Multiplication    cout<<endl<<"Karatsuba Algorithm : "<<endl;    Karatsuba obj;    const clock_t karatsuba_time = clock();    string karatsuba = obj.multiply(firstNumber, secondNumber);    cout << float( clock () - karatsuba_time ) /  CLOCKS_PER_SEC<<endl<<endl;    float k_time = float( clock () - classical_time ) /  CLOCKS_PER_SEC;    newobj.BinaryStringToDecimal(karatsuba);         return 0;}
Related Article : Multiply Large Numbers Represented as Strings
References: Wikipedia page for Karatsuba algorithm Algorithms 1st Edition by Sanjoy Dasgupta, Christos Papadimitriou and Umesh Vazirani http://courses.csail.mit.edu/6.006/spring11/exams/notes3-karatsuba http://www.cc.gatech.edu/~ninamf/Algos11/lectures/lect0131.pdfPlease write comments if you find anything incorrect, or you want to share more information about the topic discussed above. 
harshitSingh_11
emreuysal
ShayekhBinIslam
arorakashish0911
simmytarika5
Divide and Conquer
Strings
Strings
Divide and Conquer
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Program for Tower of Hanoi
Divide and Conquer Algorithm | Introduction
Write a program to calculate pow(x,n)
Count number of occurrences (or frequency) in a sorted array
Quick Sort vs Merge Sort
Reverse a string in Java
Write a program to reverse an array or string
Longest Common Subsequence | DP-4
Write a program to print all permutations of a given string
C++ Data Types