# Karatsuba algorithm for fast multiplication using Divide and Conquer algorithm

Last Updated : 06 Feb, 2024

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 follows.Â

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 the 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.Â

Recommended Practice

Following is C++ implementation of above algorithm.

## Java

 // Java implementation of Karatsuba algorithm for bit string multiplication.   public class GFG {       // Driver Code     public static void main(String[] args)     {         System.out.println(multiply("1100", "1010"));         System.out.println(multiply("110", "1010"));         System.out.println(multiply("11", "1010"));         System.out.println(multiply("1", "1010"));         System.out.println(multiply("0", "1010"));         System.out.println(multiply("111", "111"));         System.out.println(multiply("11", "11"));     }         // Helper method: given two unequal sized bit strings,     // converts them to same length by adding leading 0s in     // the smaller string. Returns the new length     private static int makeEqualLength(StringBuilder str1,                                        StringBuilder str2)     {         int len1 = str1.length();         int len2 = str2.length();         if (len1 < len2) {             for (int i = 0; i < len2 - len1; i++) {                 str1.insert(0, '0');             }             return len2;         }         else if (len1 > len2) {             for (int i = 0; i < len1 - len2; i++) {                 str2.insert(0, '0');             }         }         return len1; // If len1 >= len2     }       // The main function that adds two bit sequences and     // returns the addition     private static StringBuilder     addBitStrings(StringBuilder first, StringBuilder second)     {         StringBuilder result = new StringBuilder();         int length = makeEqualLength(first, second);         int carry = 0;           // Add all bits one by one         for (int i = length - 1; i >= 0; i--) {             int firstBit = first.charAt(i) - '0';             int secondBit = second.charAt(i) - '0';               // boolean expression for sum of 3 bits             int sum = (firstBit ^ secondBit ^ carry) + '0';             result.insert(0, (char)sum);               // boolean expression for 3-bit addition             carry = (firstBit & secondBit)                     | (secondBit & carry)                     | (firstBit & carry);         }           // if overflow, then add a leading 1         if (carry == 1) {             result.insert(0, '1');         }           return result;     }       // A utility function to multiply single bits of strings     // a and b     private static int multiplySingleBit(int a, int b)     {         return a * b;     }       // The main function that multiplies two bit strings X     // and Y and returns result as long integer     public static long 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 = Math.max(X.length(), Y.length());         X = String.format("%" + n + "s", X)                 .replace(' ', '0');         Y = String.format("%" + n + "s", Y)                 .replace(' ', '0');           // Base cases         if (n == 0)             return 0;         if (n == 1)             return Integer.parseInt(X)                 * Integer.parseInt(Y);           int fh = n / 2; // First half of string         int sh = n - fh; // Second half of string           // Find the first half and second half of first         // string.         String Xl = X.substring(0, fh);         String Xr = X.substring(fh);           // Find the first half and second half of second         // string         String Yl = Y.substring(0, fh);         String Yr = Y.substring(fh);           // Recursively calculate the three products of         // inputs of size n/2         long P1 = multiply(Xl, Yl);         long P2 = multiply(Xr, Yr);         long P3 = multiply(Integer.toBinaryString(                                Integer.parseInt(Xl, 2)                                + Integer.parseInt(Xr, 2)),                            Integer.toBinaryString(                                Integer.parseInt(Yl, 2)                                + Integer.parseInt(Yr, 2)));           // Combine the three products to get the final         // result.         return P1 * (1L << (2 * sh))             + (P3 - P1 - P2) * (1L << sh) + P2;     } }

## Python3

 # Python implementation of Karatsuba algorithm for bit string multiplication.   # 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 length def make_equal_length(str1, str2):     len1 = len(str1)     len2 = len(str2)     if len1 < len2:         for i in range(len2 - len1):             str1 = '0' + str1         return len2     elif len1 > len2:         for i in range(len1 - len2):             str2 = '0' + str2     return len1 # If len1 >= len2   # The main function that adds two bit sequences and returns the addition def add_bit_strings(first, second):     result = ""  # To store the sum bits       # make the lengths same before adding     length = make_equal_length(first, second)     carry = 0  # Initialize carry       # Add all bits one by one     for i in range(length-1, -1, -1):         first_bit = int(first[i])         second_bit = int(second[i])           # boolean expression for sum of 3 bits         sum = (first_bit ^ second_bit ^ carry) + ord('0')           result = chr(sum) + result           # boolean expression for 3-bit addition         carry = (first_bit & second_bit) | (second_bit & carry) | (first_bit & 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 b def multiply_single_bit(a, b):     return int(a[0]) * int(b[0])   # The main function that multiplies two bit strings X and Y and returns # result as long integer def multiply(X, Y):     # Find the maximum of lengths of x and Y and make length     # of smaller string same as that of larger string     n = max(len(X), len(Y))     X = X.zfill(n)     Y = Y.zfill(n)       # Base cases     if n == 0: return 0     if n == 1: return int(X[0])*int(Y[0])       fh = n//2  # First half of string     sh = n - fh  # Second half of string       # Find the first half and second half of first string.     Xl = X[:fh]     Xr = X[fh:]       # Find the first half and second half of second string     Yl = Y[:fh]     Yr = Y[fh:]       # Recursively calculate the three products of inputs of size n/2     P1 = multiply(Xl, Yl)     P2 = multiply(Xr, Yr)     P3 = multiply(str(int(Xl, 2) + int(Xr, 2)), str(int(Yl, 2) + int(Yr, 2)))       # Combine the three products to get the final result.     return P1*(1<<(2*sh)) + (P3 - P1 - P2)*(1<

## Javascript

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.

Auxiliary Space: O(n)

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.

Solution:
Multiplication 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

## Java

 import java.math.BigInteger;   public class BinaryMultiplication {       // Method to add binary strings     public static String addBinary(String a, String b) {         BigInteger aBigInt = new BigInteger(a, 2);         BigInteger bBigInt = new BigInteger(b, 2);         BigInteger sum = aBigInt.add(bBigInt);         return sum.toString(2);     }       // Method to shift binary string     public static String shiftLeft(String str, int n) {         return str + "0".repeat(n);     }       // Classical binary multiplication     public static String classicalMultiply(String str1, String str2) {         String result = "0";         int n = str2.length();         for (int i = 0; i < n; i++) {             if (str2.charAt(n - 1 - i) == '1') {                 result = addBinary(result, shiftLeft(str1, i));             }         }         return result;     }       // Karatsuba multiplication     public static String karatsubaMultiply(String X, String Y) {         int n = Math.max(X.length(), Y.length());           // Make the lengths equal         X = String.format("%" + n + "s", X).replace(' ', '0');         Y = String.format("%" + n + "s", Y).replace(' ', '0');           if (n == 1) {             return Integer.toString(Integer.parseInt(X) * Integer.parseInt(Y));         }           int m = n / 2;           String Xl = X.substring(0, m);         String Xr = X.substring(m);         String Yl = Y.substring(0, m);         String Yr = Y.substring(m);           String P1 = karatsubaMultiply(Xl, Yl);         String P2 = karatsubaMultiply(Xr, Yr);         String P3 = karatsubaMultiply(addBinary(Xl, Xr), addBinary(Yl, Yr));           String C1 = shiftLeft(P1, 2 * (n - m));         String C2 = shiftLeft(addBinary(subtractBinary(P3, addBinary(P1, P2)), P2), n - m);           return addBinary(addBinary(C1, C2), P2);     }       // Subtract binary strings     public static String subtractBinary(String a, String b) {         BigInteger aBigInt = new BigInteger(a, 2);         BigInteger bBigInt = new BigInteger(b, 2);         BigInteger difference = aBigInt.subtract(bBigInt);         return difference.toString(2);     }       public static void main(String[] args) {         String firstNumber = "011011010100";         String secondNumber = "10111010111";           System.out.println("Classical Algorithm:");         String classicResult = classicalMultiply(firstNumber, secondNumber);         System.out.println("Binary Result: " + classicResult);         System.out.println("Decimal Result: " + new BigInteger(classicResult, 2).toString(10));           System.out.println("Karatsuba Algorithm:");         String karatsubaResult = karatsubaMultiply(firstNumber, secondNumber);         System.out.println("Binary Result: " + karatsubaResult);         System.out.println("Decimal Result: " + new BigInteger(karatsubaResult, 2).toString(10));     } }

## Python3

 # Importing required module import math   def addBinary(a, b):     a_bigint = int(a, 2)     b_bigint = int(b, 2)     result = bin(a_bigint + b_bigint)[2:]     return result   def shiftLeft(string, n):     return string + '0' * n   def classicalMultiply(str1, str2):     result = '0'     n = len(str2)     for i in range(n):         if str2[n - 1 - i] == '1':             result = addBinary(result, shiftLeft(str1, i))     return result   def karatsubaMultiply(X, Y):     n = max(len(X), len(Y))       # Make the lengths equal     X = X.rjust(n, '0')     Y = Y.rjust(n, '0')       if n == 1:         return bin(int(X, 2) * int(Y, 2))[2:]       m = n // 2       Xl = X[:m]     Xr = X[m:]     Yl = Y[:m]     Yr = Y[m:]       P1 = karatsubaMultiply(Xl, Yl)     P2 = karatsubaMultiply(Xr, Yr)     P3 = karatsubaMultiply(addBinary(Xl, Xr), addBinary(Yl, Yr))       C1 = shiftLeft(P1, 2 * (n - m))     C2 = shiftLeft(addBinary(subtractBinary(P3, addBinary(P1, P2)), P2), n - m)       return addBinary(addBinary(C1, C2), P2)   def subtractBinary(a, b):     a_bigint = int(a, 2)     b_bigint = int(b, 2)     result = bin(a_bigint - b_bigint & (2 ** (max(len(a), len(b))) - 1))[2:]     return result   if __name__ == "__main__":     # Given binary numbers     first_number = "011011010100"     second_number = "10111010111"       # Classical Algorithm     print("Classical Algorithm:")     classic_result = classicalMultiply(first_number, second_number)     print("Binary Result:", classic_result)     print("Decimal Result:", int(classic_result, 2))       # Karatsuba Algorithm     print("\nKaratsuba Algorithm:")     karatsuba_result = karatsubaMultiply(first_number, second_number)     print("Binary Result:", karatsuba_result)     print("Decimal Result:", int(karatsuba_result, 2))

## Javascript

Time Complexity:
The time complexity of both Classical and Karatsuba methods of binary string multiplication is O(n^2).

In the classical method, the time complexity is O(n^2) because the loop is iterated n times. The time complexity of the addBinary() method is constant because the loop runs with a maximum of two iterations.

In the Karatsuba method, the time complexity is O(n^2) because the ‘multiply’ method of the Karatsuba class is called recursively for each of the three products. The time complexity of the addStrings() method is constant because the loop runs with a maximum of two iterations.

Auxiliary Space :
The Auxiliary Space of both Classical and Karatsuba methods of binary string multiplication is O(n).

In the classical method, the Auxiliary Space is O(n) because the loop is iterated n times and a single string is used to store the result. The space complexity of the addBinary() method is constant because the loop runs with a maximum of two iterations.

In the Karatsuba method, the auxiliary Space is O(n) because the ‘multiply’ method of the Karatsuba class is called recursively for each of the three products.Â

Related Article :Â
Multiply Large Numbers Represented as Strings

Previous
Next