Number Theory & Algebra contents
Linear Sieve and Multiplicative Functions
The O(n) sieve, and how to compute φ, μ, d and σ for every number up to n in one pass.
Read first: Sieve of Eratosthenes, Number and Sum of Divisors
The sieve of Eratosthenes runs in because a composite such as is crossed out by each of its prime factors , and . The linear sieve ensures every composite is visited exactly once, through its smallest prime factor .
Idea
Every composite can be written uniquely as where and satisfies . So process in order, and for each prime (with ) mark with .
def linear_sieve(n):
lp = [0] * (n + 1) # smallest prime factor, 0 = not set
primes = []
for i in range(2, n + 1):
if lp[i] == 0: # nothing marked it: i is prime
lp[i] = i
primes.append(i)
for p in primes:
if p > lp[i] or i * p > n:
break
lp[i * p] = p
return lp, primes
lp, primes = linear_sieve(100)
assert primes[:10] == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] and len(primes) == 25
assert lp[91] == 7 and lp[64] == 2 and lp[97] == 97The inner loop writes each composite exactly once, giving time. The p > lp[i] check is the whole trick: it stops us from generating with larger than the smallest prime factor of , which some smaller prime would have generated.
In pure Python this loop-heavy version is slower than the slice-based sieve for plain primality (see the earlier article), but it produces much more than a bit per number.
Multiplicative functions in one pass
A function is multiplicative if for coprime . Values at (with ) follow from in two cases:
- (): and are coprime, so .
- (): a rule specific to using the exponent of in .
Euler's totient
if , and if .
Möbius function
if has a squared prime factor, otherwise for distinct primes. if , else .
Number of divisors and their sum
Keep = the exponent of in :
- : , ;
- : and .
For keep (the factor of that belongs to the smallest prime). If then ; otherwise replace the old factor by .
def multiplicative_tables(n):
lp = [0] * (n + 1)
primes = []
phi = [0] * (n + 1)
mu = [0] * (n + 1)
d = [0] * (n + 1)
cnt = [0] * (n + 1) # exponent of lp[m] in m
sigma = [0] * (n + 1)
geo = [0] * (n + 1) # 1 + p + ... + p^cnt for p = lp[m]
phi[1] = mu[1] = d[1] = sigma[1] = geo[1] = 1
for i in range(2, n + 1):
if lp[i] == 0:
lp[i] = i
primes.append(i)
phi[i], mu[i], d[i], cnt[i], sigma[i], geo[i] = i - 1, -1, 2, 1, i + 1, i + 1
for p in primes:
m = i * p
if p > lp[i] or m > n:
break
lp[m] = p
if p < lp[i]: # p does not divide i: coprime
phi[m] = phi[i] * (p - 1)
mu[m] = -mu[i]
d[m] = d[i] * 2
cnt[m] = 1
geo[m] = p + 1
sigma[m] = sigma[i] * (p + 1)
else: # p == lp[i]
phi[m] = phi[i] * p
mu[m] = 0
cnt[m] = cnt[i] + 1
d[m] = d[i] // (cnt[i] + 1) * (cnt[i] + 2)
geo[m] = geo[i] * p + 1
sigma[m] = sigma[i] // geo[i] * geo[m]
return phi, mu, d, sigma
phi, mu, d, sigma = multiplicative_tables(1000)
from math import gcd
assert phi[1:11] == [1, 1, 2, 2, 4, 2, 6, 4, 6, 4]
assert mu[1:11] == [1, -1, -1, 0, -1, 1, -1, 0, 0, 1]
assert d[1:11] == [1, 2, 2, 3, 2, 4, 2, 4, 3, 4]
assert sigma[1:11] == [1, 3, 4, 7, 6, 12, 8, 15, 13, 18]
for m in range(1, 1001):
divs = [x for x in range(1, m + 1) if m % x == 0]
assert d[m] == len(divs) and sigma[m] == sum(divs)
assert phi[m] == sum(gcd(x, m) == 1 for x in range(1, m + 1))All four tables come from a single pass.
Möbius inversion in practice
The Möbius function satisfies , which gives a formula for counting coprime pairs:
def coprime_pairs(n):
return sum(mu[k] * (n // k) ** 2 for k in range(1, n + 1))
assert coprime_pairs(10) == sum(gcd(a, b) == 1 for a in range(1, 11) for b in range(1, 11)) == 63
assert coprime_pairs(300) == sum(gcd(a, b) == 1 for a in range(1, 301) for b in range(1, 301))