Number Theory & Algebra contents
Montgomery Multiplication
Multiply modulo n without dividing by n, by working in a representation where division is a bit shift.
Read first: Binary Exponentiation, Bit Manipulation
Modular multiplication costs a division by , which is slow compared to a multiplication or a shift. Montgomery multiplication avoids it: numbers are kept in a special form in which the reduction modulo is done with one multiplication and a shift by a power of two. It is standard in cryptographic libraries, and useful in C++ when you do millions of modular multiplications (for example in Miller-Rabin with 64-bit moduli).
Montgomery representation
Let be odd and choose (a power of two, so division by is a shift). The Montgomery form of is
Adding, subtracting and comparing Montgomery forms work as usual (mod ). For multiplication, , but we want , so we must divide by modulo . That operation is the Montgomery reduction:
Montgomery reduction
We need for without a division by .
Precompute , so that . Let
Then (since ), so the division by is exact — a shift. And because is a multiple of . Finally , so one conditional subtraction gives the answer in .
class Montgomery:
def __init__(self, n, bits=None):
assert n % 2 == 1 and n > 1, "modulus must be odd"
self.n = n
self.bits = bits or n.bit_length()
self.R = 1 << self.bits
self.mask = self.R - 1
self.n_prime = (-pow(n, -1, self.R)) & self.mask # -n^{-1} mod R
self.r2 = self.R * self.R % n # R^2 mod n, to enter the form
def reduce(self, T):
"""T * R^{-1} mod n for 0 <= T < n*R."""
m = ((T & self.mask) * self.n_prime) & self.mask
t = (T + m * self.n) >> self.bits
return t - self.n if t >= self.n else t
def to_mont(self, x):
return self.reduce((x % self.n) * self.r2) # x * R^2 / R = x R
def from_mont(self, x_bar):
return self.reduce(x_bar)
def mul(self, a_bar, b_bar):
return self.reduce(a_bar * b_bar)
def pow(self, base, exponent):
result = self.to_mont(1)
b = self.to_mont(base)
while exponent:
if exponent & 1:
result = self.mul(result, b)
b = self.mul(b, b)
exponent >>= 1
return self.from_mont(result)
M = Montgomery(1_000_000_007)
a, b = 123456789, 987654321
assert M.from_mont(M.mul(M.to_mont(a), M.to_mont(b))) == a * b % 1_000_000_007
import random
random.seed(1)
for n in (3, 7, 101, 65537, 1_000_000_007, 998_244_353, (1 << 61) - 1, (1 << 63) - 25):
mont = Montgomery(n)
for _ in range(200):
x, y = random.randrange(n), random.randrange(n)
assert mont.from_mont(mont.mul(mont.to_mont(x), mont.to_mont(y))) == x * y % n
assert mont.pow(random.randrange(n), random.randrange(10 ** 6)) is not None
z, e = random.randrange(1, n), random.randrange(10 ** 5)
assert mont.pow(z, e) == pow(z, e, n)to_mont(x) multiplies by and reduces, giving . from_mont is one reduction of the form itself: .
The fast inverse trick
Computing does not need the extended Euclid algorithm. Newton's method doubles the number of correct bits per step: if , then satisfies . Since every odd satisfies , start from (3 correct bits) and iterate:
def inverse_mod_pow2(n, bits):
x = n # correct to 3 bits (n*n = 1 mod 8)
correct = 3
mask = (1 << bits) - 1
while correct < bits:
x = x * (2 - n * x) & mask
correct *= 2
return x
for n in (3, 7, 101, 65537, 1_000_000_007, (1 << 61) - 1):
for bits in (16, 32, 64, 128):
inv = inverse_mod_pow2(n, bits)
assert inv * n % (1 << bits) == 1For 64 bits this takes 5 iterations of a multiply-subtract-multiply, so setup is cheap.
Why this is worthwhile in C++
For 64-bit moduli, a * b % n needs a 128-bit intermediate and a 128-by-64-bit division (tens of cycles). Montgomery's REDC uses two 64x64 multiplications, a shift and a compare (a handful of cycles). In a modular-exponentiation-heavy algorithm (Miller-Rabin, Pollard's rho) the saving is a large constant factor.