Introduction
Typically, multiplying two numbers with n
-digits involves n^2
multiplication operations. This is the approach most people use intuitively. Consider the example below for clarity:
|
|
It is well known that the result is 180. We can break this calculation into simpler steps. Multiplying 12 × 15
is slightly more complex than 10 × 15
, but multiplying by 10 is straightforward since we just append a zero to the number, making 10 × 15 = 150
.
To find 12 × 15
, we treat it as (10 × 15) + (2 × 15)
, which simplifies to 150 + 30 = 180
.
These smaller multiplications and additions make the calculation manageable for small numbers. However, the difficulty increases with larger numbers. For instance, multiplying 65 × 97
requires a more structured approach.
Read more in detail: Multiplication of Two Numbers
Through education, students are introduced to a traditional algorithm that systematically handles such calculations. This method requires four separate multiplications and additional summations for two-digit numbers, as shown diagrammatically below. It becomes inefficient as numbers grow in size, making it laborious for computations like:
|
|
History
In 1960, renowned Russian mathematician Andrey Kolmogorov hypothesised that it was impossible to multiply two n
-digit numbers in fewer than n^2
operations. Shortly afterward, a young 23-year-old researcher, Anatolii A. Karatsuba, refuted this claim by introducing a sophisticated divide-and-conquer method to compute products using n^{lg(3)}
multiplications—a significant optimisation.
The Algorithm Designer’s Mantra
Algorithm designers live by one core question:
Can this be improved?
This thought becomes particularly important when dealing with straightforward or naive solutions to problems.
The Karatsuba Framework
Karatsuba’s approach efficiently computes the product of two n
-digit numbers, x
and y
, by breaking them into smaller components. Assuming a base B
(commonly 10 in the decimal system), we can express the numbers as:
|
|
Here:
x1
andy1
are the higher parts (most significant digits) of the numbers.x2
andy2
are the lower parts (least significant digits).B^m
is the base raised to the power ofm
, wherem
is half the number of digits inx
ory
.
When multiplied, the product xy
can be expanded as:
|
|
Expanding this results in:
|
|
To simplify:
|
|
Combining these results gives:
|
|
While this involves four multiplications, Karatsuba ingeniously reduces it to three by computing b
as:
|
|
which we can derive as:
|
|
Finally, combining the results:
|
|
This reduction forms the basis of Karatsuba’s divide-and-conquer approach, which recursively applies the same process to smaller sub-problems, exemplifying the divide-and-conquer paradigm.
Steps
-
- Divide:
- Split the numbers
x
andy
intox1
,x2
,y1
, andy2
(high and low parts).
- Split the numbers
- Recurse:
- Compute
a
,c
, andd
using recursive calls to smaller sub-problems.
- Compute
- Combine:
- Calculate
b
asd - a - c
. - Compute the final result using the formula:
xy = a × B^(2m) + b × B^m + c
- Calculate
- Divide:
Example Calculation 1
Let’s compute 47 × 78
using Karatsuba’s method:
|
|
Now the thing is that 11 * 15 it’s again a multiplication between 2-digit numbers, but fortunately we can apply the same rules two them. This makes the algorithm of Karatsuba a perfect example of the “divide and conquer” algorithm.
Example Calculation 2
Let’s calculate x = 1234
and y = 5678
:
- Split the numbers:
x1 = 12
,x2 = 34
y1 = 56
,y2 = 78
- Recursive calls:
a = karatsuba(12, 56) = 672
c = karatsuba(34, 78) = 2652
d = karatsuba((12 + 34), (56 + 78)) = karatsuba(46, 134) = 6164
- Calculate b:
b = d - a - c = 6164 - 672 - 2652 = 2840
- Combine results:
xy = a × 10^(2 × 2) + b × 10^2 + c
xy = 672 × 10000 + 2840 × 100 + 2652
xy = 7006652
Pseudocode
|
|
Code
C++
|
|
Java
|
|
Python
|
|
Complexity Analysis
By substituting two multiplications with a single efficient operation, Karatsuba accelerates calculations. The algorithm improves upon the naive O(n^2)
complexity, achieving O(n^{lg(3)}) = O(n^1.59)
. This exponential improvement becomes particularly significant for large inputs, as shown in the graph comparing the two complexities.
Applications
This algorithm is widely used in integer multiplications due to its efficiency. Beyond its use for numbers, the approach is also valuable for polynomial multiplications in computational mathematics.