PyInfo
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.

Advanced5 min readlinear sievemultiplicative functionsmobiustotient

Read first: Sieve of Eratosthenes, Number and Sum of Divisors

The sieve of Eratosthenes runs in O(nlog⁡log⁡n)O(n \log\log n) because a composite such as 3030 is crossed out by each of its prime factors 22, 33 and 55. The linear sieve ensures every composite is visited exactly once, through its smallest prime factor lp(m)\text{lp}(m).

Idea

Every composite mm can be written uniquely as m=i⋅pm = i \cdot p where p=lp(m)p = \text{lp}(m) and i=m/pi = m/p satisfies lp(i)≥p\text{lp}(i) \ge p. So process i=2,3,…,ni = 2, 3, \dots, n in order, and for each prime p≤lp(i)p \le \text{lp}(i) (with ip≤nip \le n) mark ipip with lp(ip)=p\text{lp}(ip) = p.

python
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] == 97

The inner loop writes each composite exactly once, giving O(n)O(n) time. The p > lp[i] check is the whole trick: it stops us from generating i⋅pi \cdot p with pp larger than the smallest prime factor of ii, 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 ff is multiplicative if f(ab)=f(a)f(b)f(ab) = f(a)f(b) for coprime a,ba, b. Values at m=i⋅pm = i \cdot p (with p=lp(m)p = \text{lp}(m)) follow from f(i)f(i) in two cases:

  • p∤ip \nmid i (p<lp(i)p < \text{lp}(i)): ii and pp are coprime, so f(ip)=f(i)f(p)f(ip) = f(i) f(p).
  • p∣ip \mid i (p=lp(i)p = \text{lp}(i)): a rule specific to ff using the exponent of pp in ii.

Euler's totient φ\varphi

φ(ip)=φ(i) (p−1)\varphi(ip) = \varphi(i)\,(p-1) if p∤ip \nmid i, and φ(ip)=φ(i) p\varphi(ip) = \varphi(i)\,p if p∣ip \mid i.

Möbius function μ\mu

μ(n)=0\mu(n) = 0 if nn has a squared prime factor, otherwise (−1)k(-1)^k for kk distinct primes. μ(ip)=−μ(i)\mu(ip) = -\mu(i) if p∤ip \nmid i, else 00.

Number of divisors dd and their sum σ\sigma

Keep cnt[m]\text{cnt}[m] = the exponent of lp(m)\text{lp}(m) in mm:

  • p∤ip \nmid i: d(ip)=2 d(i)d(ip) = 2\,d(i), cnt[ip]=1\text{cnt}[ip] = 1;
  • p∣ip \mid i: cnt[ip]=cnt[i]+1\text{cnt}[ip] = \text{cnt}[i] + 1 and d(ip)=d(i)/(cnt[i]+1)⋅(cnt[i]+2)d(ip) = d(i) / (\text{cnt}[i] + 1) \cdot (\text{cnt}[i] + 2).

For σ\sigma keep geo[m]=1+p+⋯+pcnt[m]\text{geo}[m] = 1 + p + \dots + p^{\text{cnt}[m]} (the factor of σ\sigma that belongs to the smallest prime). If p∤ip \nmid i then σ(ip)=σ(i)(p+1)\sigma(ip) = \sigma(i)(p + 1); otherwise replace the old factor geo[i]\text{geo}[i] by geo[ip]=p⋅geo[i]+1\text{geo}[ip] = p \cdot \text{geo}[i] + 1.

python
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 O(n)O(n) pass.

Möbius inversion in practice

The Möbius function satisfies ∑d∣nμ(d)=[n=1]\sum_{d \mid n} \mu(d) = [n = 1], which gives a formula for counting coprime pairs:

#{(a,b):1≤a,b≤n, gcd⁡(a,b)=1}=∑d=1nμ(d)⌊nd⌋2\#\{(a, b) : 1 \le a, b \le n,\ \gcd(a, b) = 1\} = \sum_{d=1}^{n} \mu(d) \left\lfloor \frac{n}{d} \right\rfloor^2
python
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))

This article is a Python adaptation of “Linear Sieve” from cp-algorithms.com, licensed under CC BY-SA 4.0. The text was condensed and rewritten and the C++ code was reimplemented in Python; this adaptation is shared under the same license.