“Multiply two integers without using the multiplication operator.”
A few months ago I had a technical interview for a data science consulting firm. I was only asked one question, and it seemed so outlandishly removed from the job role that I wondered whether the interviewers had mistaken me for a candidate for another vacancy.
It was simply this:
“Write a function that multiplies two integers (positive or negative) without using the multiplication operator.”
The interviewers said they were fine with pseudocode.
I was sharing my screen on one of those snazzy online coding platforms. Within seconds, they said they wanted working code.
I chose to use Python – it was in the job requirements after all – and promptly wrote the most obvious code imaginable.
def multiply_by_summation(a, b):
result = 0
for _ in range(abs(b)):
result += a
return -result if b < 0 else result
They asked whether I could optimise this, so I added a line to find the smaller of the two absolute values of a and b, and loop over the smaller one. I was asked to make it faster than O(|b|).
Then they seemed to get tired of the whole thing, and that was that.
I didn’t get the job.
The classic interview answer is of course so-called Russian peasant multiplication. It is huge misnomer. The method was used in Ancient Egypt, and why Russian peasant? Because of Vladimir Golenischev?
Anyway, here it is. The method relies on successive halving and doubling. It runs in O(log b) time.
def multiply_egyptian(a, b):
if a == 0 or b == 0:
return 0
# Make a positive, remember sign
negative = (a < 0) ^ (b < 0)
a, b = abs(a), abs(b)
result = 0
while b > 0:
if b & 1: # if b is odd
result += a
a <<= 1 # a = a * 2
b >>= 1 # b = b // 2
return -result if negative else result
An even faster algorithm is Karatsuba multiplication, which should rightfully be called Russian multiplicaiton, since it was invented by the Russian Anatoly Karatsuba. This has a time complexity of O(n1.58n ), where n is the number of digits.
def add_n_times(base, times):
"""Add 'base' to itself 'times' times: base * times without '*'."""
result = 0
for _ in range(times):
result += base
return result
def pow10(n):
"""Compute 10**n using only addition (no '*', no '**')."""
if n == 0:
return 1
# Build iteratively: pow10(n) = pow10(n-1) * 10 = add pow10(n-1) ten times
result = 1
for _ in range(n):
result = add_n_times(result, 10) # result * 10
return result
def multiply_bit_only(a, b):
"""O(log n) multiplication using only bits/addition (for base case)."""
if a == 0 or b == 0:
return 0
negative = (a < 0) != (b < 0)
a, b = abs(a), abs(b)
result = 0
while b > 0:
if b & 1:
result += a
a <<= 1 # a *= 2 (bit shift allowed, as it's not '*')
b >>= 1 # b //= 2
return -result if negative else result
def karatsuba_no_multiply(x, y):
"""Full Karatsuba without any '*': recursive, O(n^1.58) asymptotically."""
# Base case: small numbers use bit multiplication
if abs(x) < 10 or abs(y) < 10:
return multiply_bit_only(x, y)
# Handle signs
negative = (x < 0) != (y < 0)
x, y = abs(x), abs(y)
if x < y:
x, y = y, x
# Digits in smaller number
n = len(str(y))
half = (n + 1) // 2 # Ceiling of n/2
# Split using div/mod
divider = pow10(half)
x_high = x // divider
x_low = x % divider
y_high = y // divider
y_low = y % divider
# 3 recursions instead of 4
z0 = karatsuba_no_multiply(x_low, y_low)
z2 = karatsuba_no_multiply(x_high, y_high)
z1 = karatsuba_no_multiply(x_low + x_high, y_low + y_high) - z2 - z0
# Combine without '*': use add_n_times for the "shifts" (powers of 10)
pow_half = divider # Already 10**half
pow_double = add_n_times(pow_half, pow_half) #
double_half = add_n_times(half, 2)
pow_double = pow10(double_half)
# Now "z2 * pow_double" = add z2, pow_double times
part_high = add_n_times(z2, pow_double)
# "z1 * pow_half" = add z1, pow_half times
part_mid = add_n_times(z1, pow_half)
# Total: part_high + part_mid + z0
result = part_high + part_mid + z0
return -result if negative else result