Number Theory & Algebra contents
Primitive Roots
Find a generator of the multiplicative group modulo n: when it exists, how to test a candidate, and how many there are.
Read first: Euler's Totient Function, Integer Factorization
The multiplicative order of modulo (with ) is the smallest with . By Euler's theorem the order divides .
A primitive root modulo is a number whose order is exactly . Then the powers are all the residues coprime to , each exactly once: generates the whole group. Primitive roots turn multiplication into addition of exponents, which is what makes discrete roots tractable.
Existence (Gauss)
A primitive root exists if and only if is one of
For example and have none.
Testing a candidate
is a primitive root modulo exactly when for every prime divisor of
Reason: the order of divides , and if it were a proper divisor it would divide for some prime , giving .
Finding one
Primitive roots are plentiful: there are of them, and the smallest one is usually tiny (for primes up to , it is at most a few hundred in practice; a proven bound is ). So just try and test.
from math import gcd
def prime_factors(n):
ps, d = [], 2
while d * d <= n:
if n % d == 0:
ps.append(d)
while n % d == 0:
n //= d
d += 1
if n > 1:
ps.append(n)
return ps
def phi(n):
result = n
for p in prime_factors(n):
result -= result // p
return result
def has_primitive_root(n):
if n in (1, 2, 4):
return True
if n % 2 == 0:
n //= 2 # 2 p^k -> p^k
ps = prime_factors(n)
return len(ps) == 1 and ps[0] != 2
def primitive_root(n):
"""Smallest primitive root modulo n, or None if none exists."""
if not has_primitive_root(n):
return None
if n == 1:
return 0
order = phi(n)
factors = prime_factors(order)
for g in range(1, n):
if gcd(g, n) == 1 and all(pow(g, order // q, n) != 1 for q in factors):
return g
return None
assert primitive_root(7) == 3 and primitive_root(11) == 2 and primitive_root(998244353) == 3
assert primitive_root(8) is None and primitive_root(12) is None
assert primitive_root(9) == 2 and primitive_root(18) == 5
assert primitive_root(10 ** 9 + 7) == 5Checking against the definition
Compute the multiplicative order of every unit by brute force and check both the existence criterion and the count :
def order(g, n):
k, x = 1, g % n
while x != 1 % n:
x = x * g % n
k += 1
return k
for n in range(2, 300):
units = [g for g in range(1, n) if gcd(g, n) == 1]
roots = [g for g in units if order(g, n) == len(units)]
assert bool(roots) == has_primitive_root(n), n
if roots:
assert roots[0] == primitive_root(n)
assert len(roots) == phi(phi(n)) or n in (2,), n # exactly phi(phi(n)) of themUsing a primitive root
If is a primitive root modulo a prime , every non-zero residue is for a unique , its index (discrete logarithm). Then:
- : multiplication becomes addition of indices modulo ,
- : powers become multiplications.
p = 101
g = primitive_root(p)
index = {pow(g, i, p): i for i in range(p - 1)}
a, b = 37, 55
assert a * b % p == pow(g, (index[a] + index[b]) % (p - 1), p)
assert pow(a, 12345, p) == pow(g, index[a] * 12345 % (p - 1), p)Other places you will see primitive roots: the Number Theoretic Transform (the modulus 998244353 has primitive root 3, and ), pseudo-random number generators, and constructing cyclic sequences.