How many primes are there below a million?
Euclid proved that the primes never run out. But how thinly are they spread? Around 1792, the teenage Carl Friedrich Gauss counted the primes in blocks of a thousand from tables and observed that near about one number in is prime (natural logarithm). So the number of primes up to should be roughly
For : , , and . Proving that — the prime number theorem — took a century and a new idea: Bernhard Riemann's 1859 memoir, which studied the primes through the complex function
and showed that the distribution of primes is governed by the zeros of . The theorem was proved in 1896 by Jacques Hadamard and Charles de la Vallée Poussin. Riemann also conjectured that all the non-trivial zeros lie on the line ; this Riemann hypothesis is still open, and it is equivalent to the statement that — the primes are as regular as they could possibly be.
Analytic number theory answers arithmetic questions with analysis: generating functions (Dirichlet series), complex integration, Fourier analysis and estimates. This lesson assumes elementary number theory and complex analysis (holomorphic functions, analytic continuation, the residue theorem).
Three ideas
- Unique factorisation is an Euler product. Expanding produces each exactly once, because each factors uniquely into primes. So the analytic properties of — a pole at , where diverges — say something about primes: there must be infinitely many, and must diverge.
- Logarithmic derivatives count primes. Taking of the Euler product gives , where if and otherwise. The poles of are the pole and the zeros of , and complex integration (Perron's formula) turns them into terms of : the pole at gives the main term , and each zero gives a term .
- Zeros are frequencies. If , then oscillates in with frequency and amplitude . The primes are the "sound" and the zeros its "spectrum" (an analogy; the precise statement is von Mangoldt's explicit formula, Theorem 9). Zeros with would destroy the prime number theorem; zeros with give the smallest possible error terms.
A heuristic that gets many predictions right is Cramér's model: pretend each is prime independently with probability . It predicts with fluctuations of size about (up to logarithms) — consistent with the Riemann hypothesis. It is a model, not a theorem, and it is known to be wrong in some details (Maier, 1985).
Definitions
Definition— arithmetic functions and Dirichlet convolution
An arithmetic function is a map . The Dirichlet convolution is . The function is multiplicative if whenever (and ), and completely multiplicative if this holds for all . Important examples: (the identity for ), , , , Euler's , the divisor function , the Möbius function if is a product of distinct primes and if has a square factor, and the von Mangoldt function if () and otherwise.
Definition— Dirichlet series
The Dirichlet series of is , . If it converges absolutely at , it converges absolutely and uniformly on and defines a holomorphic function on . The Riemann zeta function is for .
Definition— prime-counting functions
, , , and (principal value at ). We write if , or if for some constant , and if .
Definition— Dirichlet characters and L-functions
A Dirichlet character modulo is a homomorphism , extended to by if and otherwise. It is completely multiplicative and periodic. The principal character is on all coprime to ; there are characters modulo . The Dirichlet -function is for .
Examples
Example— Dirichlet series of familiar functions
For (or where noted), Proposition 2 below gives
from the convolution identities , , and (formalised below). For instance says .
Example— Characters modulo 4 and modulo 5
Modulo there are two characters: and with , . Then
the series of Mādhava and Leibniz. Modulo , is cyclic of order , generated by , and a character is determined by : two characters are real ( and the Legendre symbol ) and two are complex conjugates.
Example— Special values of ζ
Euler showed (the Basel problem, 1734) and more generally with Bernoulli numbers . The continued function also has values at negative integers: , , and for (the trivial zeros). These are values of the analytic continuation — the series diverges for . The first non-trivial zero is .
The main theorems
Theorem— Proposition 1 (the ring of arithmetic functions; Möbius inversion)
Under pointwise addition and Dirichlet convolution, arithmetic functions form a commutative ring with identity ; is invertible if and only if . Convolutions of multiplicative functions are multiplicative. Moreover , that is, for , and consequently
Proof
Commutativity and associativity follow from writing and ; distributivity and are immediate. If , the equations and for determine and then, recursively, . If , divisors of are uniquely products with , , which gives multiplicativity of . Since is multiplicative, it suffices to check prime powers: for . Finally, if then , and conversely if then .
∎
Theorem— Proposition 2 (Dirichlet series multiply)
If and converge absolutely, then so does , and .
Proof
is an absolutely convergent double series, so it may be summed in any order; grouping the terms with gives , and the same grouping of bounds .
∎
Theorem— Theorem 3 (Euler product; Euler's theorem on Σ 1/p)
For , , the product converging absolutely; in particular there. More generally for multiplicative with convergent. Consequently , and there are infinitely many primes.
Proof
Fix with and let . Multiplying out finitely many absolutely convergent series, where is the set of all of whose prime factors are — each such appears exactly once by unique factorisation. Every is in , so . The product converges absolutely because , and a convergent product of non-zero factors with this property is non-zero. The same argument works for multiplicative .
For the divergence, let . Taking logarithms, , where . Since as , is unbounded, so by monotone convergence.
∎
Differentiating the logarithm of the Euler product termwise gives , matching and Proposition 2.
Theorem— Theorem 4 (Chebyshev's bounds)
For all and integers :
- , hence ;
- , hence .
Consequently , and .
Proof
(1) Induction on (Erdős's argument). For it is clear; for even the product equals that for . For : every prime with divides (it divides the numerator but not the denominator), and are two terms of , so . Hence . Taking logarithms gives , and .
(2) The exponent of a prime in is the largest with , so . Each binomial coefficient divides : by Kummer's theorem the exponent of in is the number of carries when adding and in base , which is at most the number of base- digits of beyond the first, i.e. at most . So ; take logarithms.
For the consequences: , and (there are at most non-zero terms, each ), so both are . Then , and gives .
∎
Theorem— Theorem 5 (analytic continuation to Re s > 0)
For ,
where . The right-hand side is meromorphic on with a single simple pole, at , of residue . So continues meromorphically to , and is holomorphic there.
Proof
For , partial summation gives
(the boundary terms ). Writing and gives the formula. Since , the integral converges absolutely and locally uniformly on , so it is holomorphic there. Finally .
∎
Theorem— Theorem 6 (the functional equation; proof sketched)
The completed zeta function extends to a meromorphic function on whose only poles are simple poles at and , and
Hence is meromorphic on with a single simple pole at ; for (the poles of must cancel); and all other zeros — the non-trivial zeros — lie in the critical strip , symmetric about the real axis and about the critical line .
Proof sketch (Riemann's second proof). Let for . Poisson summation applied to the Gaussian , whose Fourier transform is , gives . For , , so . Splitting the integral at and using on gives
in which the integral is entire (the integrand decays like ) and the whole expression is visibly symmetric under . Zeros of in come from poles of only, and on (Theorem 3), so by symmetry the non-trivial zeros lie in . Details: Davenport, Chapter 8; Titchmarsh, Chapter II. Mathlib proves the functional equation (below).
Theorem— Theorem 7 (no zeros on the line Re s = 1)
for every real .
Proof
At there is a pole. For , the Euler product gives , so
Therefore, with ,
because (Mertens' trick, formalised below). So for all .
Suppose with . As : (simple pole of residue , Theorem 5); (a zero at , and is holomorphic there); and stays bounded, because is holomorphic at . So the product is , contradicting the lower bound .
∎
Theorem— Theorem 8 (the prime number theorem; proof outlined)
Outline of a proof (D. J. Newman's, as presented by Zagier, 1997).
- — Theorem 4.
- The function differs from by , which is holomorphic for . By Theorems 5 and 7, has a simple pole with residue at and no other poles on . So extends holomorphically to a neighbourhood of .
- Newman's analytic theorem: if is bounded and locally integrable on and its Laplace transform extends holomorphically to a neighbourhood of , then converges (and equals ). The proof is a single clever contour integral.
- Apply it to , bounded by step 1, whose Laplace transform is , holomorphic on by step 2. So converges.
- If for some and arbitrarily large , then since is increasing, for those , contradicting convergence; similarly for . So .
- Partial summation: , and the integral is , so ; also by L'Hôpital.
Complete proof: Zagier, Newman's short proof of the prime number theorem, Amer. Math. Monthly 104 (1997). The classical proofs use the zero-free region (de la Vallée Poussin, 1899), which gives the error term .
Theorem— Theorem 9 (von Mangoldt's explicit formula; stated)
For not a prime power,
where runs over the non-trivial zeros of with multiplicity, the sum taken as .
This is proved by integrating around a large rectangle and collecting residues: from the pole at , from each zero, from , and from the trivial zeros (Davenport, Chapter 17). It explains everything:
- If every zero has real part at most , then ; conversely a zero with real part forces errors of size infinitely often. The prime number theorem is equivalent to "no zeros on ".
- The Riemann hypothesis () is equivalent to , and to (von Koch, 1901).
- Each pair of zeros contributes the wave , of amplitude about ; the visualisation below adds these waves one by one.
What is known and what is open. Proved: infinitely many zeros lie on the critical line (Hardy, 1914), in fact a positive proportion (Selberg, 1942), more than (Conrey, 1989); every zero with is on the line (Platt and Trudgian, 2021); changes sign infinitely often (Littlewood, 1914). Open: the Riemann hypothesis itself; even the existence of some with no zeros in ; the twin prime conjecture; Goldbach's conjecture for even numbers.
Theorem— Theorem 10 (Dirichlet's theorem on primes in arithmetic progressions)
If , there are infinitely many primes ; in fact .
Proof for , and the general strategy. For and a character mod , the Euler product of gives, as in Theorem 3,
Take . For , , so . For , the alternating series converges for and is continuous in , and , so stays bounded as . Adding and subtracting,
For general , the orthogonality relation (for ) gives
The principal character contributes , and the theorem follows once for every (each such is holomorphic on by partial summation). For complex this follows from (a ---type argument); for real it is the deep point, proved by Dirichlet via his class number formula ( for imaginary quadratic characters with , and ). Full proof: Apostol, Chapter 7; Davenport, Chapters 1–4. Mathlib proves the theorem (below).
With more work (the zero-free region for -functions) one gets the prime number theorem for arithmetic progressions: — the primes are equidistributed among the admissible classes.
Explore: building the staircase from zeros
The plot shows the right-hand side of the explicit formula (Theorem 9) truncated to the first pairs of zeros , against the line . With you see the smooth main term . Increase : every zero adds a wave of amplitude about , and the waves conspire to build a staircase that jumps by at each prime power — at — exactly as the true does. Seven zeros already locate the small primes; with infinitely many the staircase is exact. The second experiment computes the zeros and compares the same truncation with the true .
ψ(x) rebuilt from the zeros of ζ
The true ψ(x) jumps by log p at every prime power pᵏ; watch the jumps appear as N grows.
- explicit formula with N pairs of zeros
- y = x
- explicit formula with N pairs of zeros: y = −0.0006
- y = x: y = 1.5
Where the pole lives. The series converges exactly for ; at its partial sums grow like . This divergence is the pole of at , which by Theorem 3 forces infinitely many primes. Move towards and watch the convergence slow down.
Partial sums of ζ(p) = Σ 1/nᵖ
Convergent for p > 1 (ζ(2) = π²/6 ≈ 1.645); divergent like log N at p = 1.
- partial sums Sₙ
- terms aₙ
S400 = a1 + … + a400 = 2.512438
Partial sums on a screen are evidence, never proof: a series can look settled and still diverge, as the harmonic series does.
Analytic number theory in Lean and Mathlib
Mathlib has a substantial library of analytic number theory: arithmetic functions, -series, the Riemann zeta function on all of , Chebyshev's functions and Dirichlet's theorem. We start with Proposition 1, proved by the three-line ring computation of the text, and Mathlib's version with sums:
/-- **Möbius inversion**, proved in the ring of arithmetic functions under Dirichlet convolution
`*`. If `g = f * ζ` — that is, `g(n) = ∑_{d ∣ n} f(d)` — then `f = g * μ`, i.e.
`f(n) = ∑_{d ∣ n} μ(d) g(n/d)`. The proof is three lines of algebra, using associativity of `*` and
the identity `ζ * μ = 1` (Mathlib: `coe_zeta_mul_moebius`), which says `∑_{d ∣ n} μ(d) = 0` for
`n > 1`. -/
theorem moebius_inversion (f g : ArithmeticFunction ℤ) (h : g = f * ζ) : f = g * μ :=
calc f = f * 1 := (mul_one f).symm
_ = f * ((ζ : ArithmeticFunction ℤ) * μ) := by rw [coe_zeta_mul_moebius]
_ = (f * ζ) * μ := (mul_assoc _ _ _).symm
_ = g * μ := by rw [h]/-- **Möbius inversion, written with sums**: `∑_{d ∣ n} f(d) = g(n)` for all `n > 0` if and only if
`f(n) = ∑_{ab = n} μ(a) g(b)` for all `n > 0`. Mathlib:
`ArithmeticFunction.sum_eq_iff_sum_mul_moebius_eq`. -/
theorem moebius_inversion_sums {f g : ℕ → ℤ} :
(∀ n > 0, ∑ i ∈ n.divisors, f i = g n) ↔
∀ n > 0, ∑ x ∈ n.divisorsAntidiagonal, (μ x.fst : ℤ) * g x.snd = f n :=
sum_eq_iff_sum_mul_moebius_eq/-- **`∑_{d ∣ n} Λ(d) = log n`** — unique factorisation, written additively: if
`n = ∏ pᵢ^{aᵢ}` then the prime powers dividing `n` contribute `∑ aᵢ log pᵢ = log n`. As Dirichlet
series this is `−ζ'(s)/ζ(s) = ∑ Λ(n) n^{−s}`. Mathlib: `ArithmeticFunction.vonMangoldt_sum`. -/
theorem von_mangoldt_sum (n : ℕ) : ∑ i ∈ n.divisors, Λ i = Real.log n :=
vonMangoldt_sumTheorem 3 and Euler's divergence theorem, the Basel problem, Theorem 5's residue, Theorem 6 and the trivial zeros:
/-- **Euler product**: for `Re s > 1`, `∏_p (1 − p^{−s})^{−1} = ζ(s)`, the analytic form of unique
factorisation. Mathlib: `riemannZeta_eulerProduct_tprod`. -/
theorem euler_product {s : ℂ} (hs : 1 < s.re) :
∏' p : Nat.Primes, (1 - (p : ℂ) ^ (-s))⁻¹ = riemannZeta s :=
riemannZeta_eulerProduct_tprod hs/-- **Euler: `∑ 1/p` diverges**, so there are infinitely many primes, and they are not too sparse.
Mathlib: `Nat.Primes.not_summable_one_div` (proved by Erdős's elementary argument). -/
theorem sum_inv_primes_diverges : ¬ Summable (fun p : Nat.Primes ↦ (1 / p : ℝ)) :=
Nat.Primes.not_summable_one_div/-- **The Basel problem**: `ζ(2) = π²/6` (Euler, 1734). Mathlib: `riemannZeta_two`. -/
theorem zeta_two : riemannZeta 2 = (Real.pi : ℂ) ^ 2 / 6 :=
riemannZeta_two/-- **`ζ` has a simple pole with residue `1` at `s = 1`**: `(s − 1) ζ(s) → 1` as `s → 1`. Mathlib:
`riemannZeta_residue_one`. -/
theorem zeta_residue :
Filter.Tendsto (fun s ↦ (s - 1) * riemannZeta s) (nhdsWithin 1 {1}ᶜ) (nhds 1) :=
riemannZeta_residue_one/-- **The functional equation** (Riemann, 1859): the completed zeta function
`Λ(s) = π^{−s/2} Γ(s/2) ζ(s)` satisfies `Λ(1 − s) = Λ(s)` for every complex `s`. Mathlib:
`completedRiemannZeta_one_sub`. -/
theorem functional_equation (s : ℂ) : completedRiemannZeta (1 - s) = completedRiemannZeta s :=
completedRiemannZeta_one_sub s/-- **The trivial zeros**: `ζ(−2), ζ(−4), ζ(−6), …` all vanish (the poles of `Γ(s/2)` must be
cancelled in the functional equation). Mathlib: `riemannZeta_neg_two_mul_nat_add_one`. -/
theorem trivial_zeros (n : ℕ) : riemannZeta (-2 * (n + 1)) = 0 :=
riemannZeta_neg_two_mul_nat_add_one nTheorem 7: the trigonometric heart of the proof, proved here, and Mathlib's theorem (which extends to ):
/-- **The `3–4–1` inequality** (Mertens, 1898): `3 + 4 cos θ + cos 2θ ≥ 0` for all real `θ`, because
`3 + 4 cos θ + cos 2θ = 2 (1 + cos θ)²`. Applied with `θ = t log n` it gives
`|ζ(σ)³ ζ(σ + it)⁴ ζ(σ + 2it)| ≥ 1` for `σ > 1`, which rules out a zero on the line `Re s = 1`. -/
theorem three_four_one (θ : ℝ) : 0 ≤ 3 + 4 * Real.cos θ + Real.cos (2 * θ) := by
rw [Real.cos_two_mul]
nlinarith [sq_nonneg (1 + Real.cos θ)]/-- **`ζ(s) ≠ 0` for `Re s ≥ 1`** (Hadamard and de la Vallée Poussin, 1896) — the analytic heart of
the prime number theorem. Mathlib: `riemannZeta_ne_zero_of_one_le_re`, proved with the `3–4–1`
trick. -/
theorem zeta_ne_zero_one_line {s : ℂ} (hs : 1 ≤ s.re) : riemannZeta s ≠ 0 :=
riemannZeta_ne_zero_of_one_le_re hsThe Riemann hypothesis can be stated in Lean. We write it in the words of the lesson and check that it is literally Mathlib's RiemannHypothesis. Nobody can prove either:
/-- **The Riemann hypothesis, stated** (an open problem since 1859): every zero of `ζ` other than
the trivial zeros `−2, −4, −6, …` lies on the critical line `Re s = 1/2`. (The condition `s ≠ 1`
is harmless: Mathlib assigns `ζ(1)` a junk value.) Lean can *state* this precisely; no one can
prove it. -/
def CriticalLineHypothesis : Prop :=
∀ s : ℂ, riemannZeta s = 0 → (¬ ∃ n : ℕ, s = -2 * (n + 1)) → s ≠ 1 → s.re = 1 / 2/-- **Our statement agrees with Mathlib's `RiemannHypothesis`** — a check that we have read the
definition correctly, not a proof of anything about zeros. -/
theorem critical_line_iff : CriticalLineHypothesis ↔ RiemannHypothesis :=
Iff.rflTheorem 4: part (2) proved as in the text from the divisibility , and part (1) from Mathlib:
/-- **`2ⁿ ≤ (n + 1) · lcm(1, 2, …, n)`.** Human proof: `2ⁿ = ∑_{k=0}^{n} C(n, k)` has `n + 1` terms,
and each binomial coefficient `C(n, k)` divides `lcm(1, …, n)` (Mathlib:
`Chebyshev.choose_dvd_lcmUpto`, a `p`-adic valuation argument), so is at most it. -/
theorem two_pow_le_lcm (n : ℕ) : 2 ^ n ≤ (n + 1) * Nat.lcmUpto n :=
calc 2 ^ n = ∑ k ∈ Finset.range (n + 1), n.choose k := (Nat.sum_range_choose n).symm
_ ≤ ∑ _k ∈ Finset.range (n + 1), Nat.lcmUpto n := by
apply Finset.sum_le_sum
intro k hk
have hkn : k ≤ n := Nat.lt_succ_iff.mp (Finset.mem_range.mp hk)
exact Nat.le_of_dvd (Nat.lcmUpto_pos n) (Chebyshev.choose_dvd_lcmUpto hkn)
_ = (n + 1) * Nat.lcmUpto n := by simp/-- **Chebyshev's lower bound**: `ψ(n) ≥ n log 2 − log(n + 1)`, so `ψ(x) ≫ x`. Take logarithms in
`two_pow_le_lcm` and use `ψ(n) = log lcm(1, …, n)` (Mathlib: `Chebyshev.psi_eq_log_lcmUpto`) — the
exponent of `p` in `lcm(1, …, n)` is the number of powers of `p` up to `n`. -/
theorem chebyshev_lower (n : ℕ) : n * Real.log 2 - Real.log (n + 1) ≤ Chebyshev.psi n := by
have hpos : (0 : ℝ) < Nat.lcmUpto n := by exact_mod_cast Nat.lcmUpto_pos n
have hkey : ((2 : ℝ) ^ n) ≤ (n + 1) * Nat.lcmUpto n := by exact_mod_cast two_pow_le_lcm n
have hlog := Real.log_le_log (by positivity) hkey
rw [Real.log_pow, Real.log_mul (by positivity) hpos.ne'] at hlog
rw [Chebyshev.psi_eq_log_lcmUpto]
linarith/-- **Chebyshev's upper bound**: `θ(x) = ∑_{p ≤ x} log p ≤ x log 4`, from `∏_{p ≤ n} p ≤ 4ⁿ`
(Mathlib: `primorial_le_four_pow`). Mathlib: `Chebyshev.theta_le_log4_mul_x`. -/
theorem chebyshev_upper {x : ℝ} (hx : 0 ≤ x) : Chebyshev.theta x ≤ Real.log 4 * x :=
Chebyshev.theta_le_log4_mul_x hxTheorem 10, in full generality:
/-- **Dirichlet's theorem** (1837): if `a` is a unit modulo `q`, there are infinitely many primes
`p ≡ a (mod q)`. The proof needs `L(1, χ) ≠ 0` for every non-trivial Dirichlet character `χ`.
Mathlib: `Nat.infinite_setOfPred_prime_and_eq_mod`. -/
theorem dirichlet_theorem {q : ℕ} [NeZero q] {a : ZMod q} (ha : IsUnit a) :
{p : ℕ | p.Prime ∧ (p : ZMod q) = a}.Infinite :=
Nat.infinite_setOfPred_prime_and_eq_mod haWhat is and is not formalised. Proved here: Möbius inversion from , the -- inequality, and Chebyshev's lower bound for . From Mathlib: the Euler product, , , the residue at , the functional equation, the trivial zeros, non-vanishing on , Chebyshev's upper bound and Dirichlet's theorem; and the statement of the Riemann hypothesis. Not formalised: the prime number theorem (not in Mathlib at this version; it was formalised in Isabelle by Avigad, Donnelly, Gray and Raff (2007) and in HOL Light by Harrison (2009), and it is a goal of the Lean project PrimeNumberTheoremAnd, from which Mathlib's Chebyshev file was partly upstreamed), the explicit formula, and the zero-free region.
Primes, counted and synthesised
The first experiment sieves the primes up to and compares with and : the ratio creeps towards while the difference grows, is far closer, and is already within of . It also runs the race between primes and : the second team leads for a long time (Chebyshev's bias) and the first lead change happens at , though both counts are asymptotically equal (Theorem 10 and its refinement).
"""Counting primes: pi(x) against x/log x and li(x), and a prime race.
We sieve the primes up to one million and compare the prime-counting
function pi(x) with Gauss's and Legendre's approximations x/log x and the
logarithmic integral li(x) at x = 10, 100, ..., 10^6. The prime number
theorem says pi(x) / (x / log x) -> 1; the table shows how slowly, and that
the *difference* pi(x) - x/log x grows without bound even though the ratio
tends to 1. We also compute Chebyshev's psi(x) = sum of log p over prime
powers p^k <= x, for which psi(x)/x -> 1 is the same theorem, and the race
between primes 1 and 3 mod 4: primes 3 mod 4 lead for a long time (Chebyshev's
bias), the first lead change is at 26861 (Leech, 1957), and the ratio of the
two counts tends to 1 (Dirichlet; de la Vallee Poussin). Exact counts, with
li(x) computed from a convergent series in floating point; numerical
evidence about finitely many x, not a proof of any limit.
"""
import itertools
import math
EULER_GAMMA = 0.5772156649015329
def sieve(n):
"""Bytearray flags: flags[k] == 1 exactly when k is prime (0 <= k <= n)."""
flags = bytearray([1]) * (n + 1)
flags[0:2] = b"\x00\x00"[: min(2, n + 1)]
for p in range(2, math.isqrt(n) + 1):
if flags[p]:
flags[p * p::p] = bytes(len(range(p * p, n + 1, p)))
return flags
def prime_counts(flags, points):
"""pi(x) for each x in the increasing list points."""
out, count, i = [], 0, 0
for x in points:
while i <= x:
count += flags[i]
i += 1
out.append(count)
return out
def li(x):
"""Logarithmic integral li(x) = gamma + log log x + sum (log x)^n/(n n!)."""
t = math.log(x)
total, term, n = 0.0, 1.0, 1
while True:
term *= t / n
add = term / n
total += add
if add < 1e-17 * total:
break
n += 1
return EULER_GAMMA + math.log(t) + total
def psi(flags, x):
"""Chebyshev psi(x) = sum of log p over prime powers p^k <= x."""
total = 0.0
for p in itertools.compress(range(x + 1), flags[:x + 1]):
k, q = 0, p
while q <= x:
k, q = k + 1, q * p
total += k * math.log(p)
return total
def first_lead_change(flags, n):
"""First x <= n with #{p <= x : p = 1 mod 4} > #{p <= x : p = 3 mod 4}."""
ones = threes = 0
for x in range(3, n + 1):
if flags[x]:
if x % 4 == 1:
ones += 1
else:
threes += 1
if ones > threes:
return x
return None
def main():
n = 10 ** 6
flags = sieve(n)
points = [10 ** k for k in range(1, 7)]
counts = prime_counts(flags, points)
print(f"{'x':>8} {'pi(x)':>7} {'x/log x':>10} {'ratio':>7} "
f"{'pi - x/log x':>12} {'li(x) - pi':>10}")
for x, c in zip(points, counts):
approx = x / math.log(x)
print(f"{x:>8} {c:>7} {approx:>10.1f} {c / approx:>7.4f} "
f"{c - approx:>12.1f} {li(x) - c:>10.1f}")
print("psi(x)/x at x = 10^3, 10^4, 10^5, 10^6:",
" ".join(f"{psi(flags, x) / x:.4f}" for x in points[2:]))
ones = sum(flags[5::4])
threes = sum(flags[3::4])
print(f"Primes below 10^6: {ones} are 1 mod 4, {threes} are 3 mod 4 "
f"(ratio {ones / threes:.4f})")
print("First x where primes 1 mod 4 take the lead:",
first_lead_change(flags, 100000))
print()
print("Epistemic status: exact prime counts below 10^6 and floating-point")
print("values of x/log x and li(x). The trends (ratio -> 1, difference")
print("unbounded, li(x) closer) are numerical evidence; the limits are the")
print("prime number theorem and Dirichlet's theorem, proved analytically.")
if __name__ == "__main__":
main()The second experiment evaluates with Euler–Maclaurin summation, locates the first ten zeros on the critical line as sign changes of Hardy's real function , compares them with the published values, and feeds them into the explicit formula.
"""Zeros of the Riemann zeta function, and how they generate the primes.
We evaluate zeta(s) for complex s by Euler-Maclaurin summation (valid for all
s != 1, error far below 1e-10 in the range used), check it against
zeta(2) = pi^2/6, and locate the zeros on the critical line Re s = 1/2 as sign
changes of Hardy's real function Z(t) = exp(i theta(t)) zeta(1/2 + it),
refined by bisection. The first ten zeros are compared with the published
values (Odlyzko's tables), and their number with the Riemann-von Mangoldt
formula N(T) ~ (T/2 pi) log(T/2 pi) - T/2 pi + 7/8. Finally the explicit
formula of von Mangoldt,
psi(x) = x - sum_rho x^rho / rho - log(2 pi) - log(1 - x^-2) / 2,
truncated to these ten pairs of zeros, is compared with the true psi(x):
each zero contributes a wave, and together they start to carve the staircase
of prime powers. Numerical evidence only: locating zeros on the line says
nothing about zeros off it, and the Riemann hypothesis remains open.
"""
import cmath
import math
#: Bernoulli numbers B_2, B_4, ..., B_16.
BERNOULLI = [1 / 6, -1 / 30, 1 / 42, -1 / 30, 5 / 66, -691 / 2730, 7 / 6,
-3617 / 510]
#: First ten ordinates of zeros on the critical line (Odlyzko's tables).
KNOWN_ZEROS = [14.134725142, 21.022039639, 25.010857580, 30.424876126,
32.935061588, 37.586178159, 40.918719012, 43.327073281,
48.005150881, 49.773832478]
def zeta(s, n_terms=30):
"""Riemann zeta(s), s != 1, by Euler-Maclaurin summation."""
s = complex(s)
n = n_terms
total = sum(k ** -s for k in range(1, n))
total += n ** (1 - s) / (s - 1) + n ** -s / 2
rising = s # s (s+1) ... (s + 2j - 2)
fact = 2.0 # (2j)!
for j, b in enumerate(BERNOULLI, start=1):
total += b / fact * rising * n ** (-s - 2 * j + 1)
rising *= (s + 2 * j - 1) * (s + 2 * j)
fact *= (2 * j + 1) * (2 * j + 2)
return total
def theta(t):
"""Riemann-Siegel theta function, asymptotic series (accurate for t > 10)."""
return (t / 2 * math.log(t / (2 * math.pi)) - t / 2 - math.pi / 8
+ 1 / (48 * t) + 7 / (5760 * t ** 3) + 31 / (80640 * t ** 5))
def hardy_z(t):
"""Hardy's Z(t) = exp(i theta(t)) zeta(1/2 + it), real for real t."""
return (cmath.exp(1j * theta(t)) * zeta(0.5 + 1j * t)).real
def zeros_on_line(t_min, t_max, step=0.05):
"""Ordinates of sign changes of Z in [t_min, t_max], refined by bisection."""
found = []
t, z_old = t_min, hardy_z(t_min)
while t < t_max:
t_new = t + step
z_new = hardy_z(t_new)
if z_old * z_new < 0:
lo, hi = t, t_new
for _ in range(40):
mid = (lo + hi) / 2
if hardy_z(lo) * hardy_z(mid) <= 0:
hi = mid
else:
lo = mid
found.append((lo + hi) / 2)
t, z_old = t_new, z_new
return found
def riemann_von_mangoldt(T):
"""Main terms of N(T), the number of zeros with 0 < Im rho < T."""
u = T / (2 * math.pi)
return u * math.log(u) - u + 7 / 8
def psi_exact(x):
"""Chebyshev psi(x) = sum of log p over prime powers p^k <= x."""
total = 0.0
for p in range(2, int(x) + 1):
if all(p % q for q in range(2, math.isqrt(p) + 1)):
q = p
while q <= x:
total += math.log(p)
q *= p
return total
def psi_explicit(x, zeros):
"""von Mangoldt's explicit formula truncated to rho = 1/2 +- i gamma."""
total = x - math.log(2 * math.pi) - 0.5 * math.log(1 - x ** -2)
for g in zeros:
rho = complex(0.5, g)
total -= 2 * (x ** rho / rho).real
return total
def main():
z2 = zeta(2)
print(f"zeta(2) = {z2.real:.12f}, pi^2/6 = {math.pi ** 2 / 6:.12f}")
print(f"|zeta(1/2 + 14.134725i)| = {abs(zeta(0.5 + 14.134725j)):.1e}")
zeros = zeros_on_line(10, 50)
print(f"Zeros of Z(t) in 10 < t < 50: {len(zeros)} found")
err = max(abs(a - b) for a, b in zip(zeros, KNOWN_ZEROS))
for start in (0, 5):
print(" " + " ".join(f"{g:.6f}" for g in zeros[start:start + 5]))
print(f" largest difference from published values: {err:.1e}")
print(f" Riemann-von Mangoldt main term N(50) = "
f"{riemann_von_mangoldt(50):.2f}")
print("Explicit formula with these 10 pairs of zeros:")
print(f" {'x':>5} {'psi(x)':>8} {'from zeros':>11}")
for x in (7.5, 10.5, 15.5, 20.5, 25.5, 30.5):
print(f" {x:>5} {psi_exact(x):>8.3f} {psi_explicit(x, zeros):>11.3f}")
print()
print("Epistemic status: numerical computation in double precision. It")
print("finds zeros on the critical line in one window; it cannot rule out")
print("zeros off the line, so it is evidence for, not a proof of, the")
print("Riemann hypothesis, which is open.")
if __name__ == "__main__":
main()Exercises
1. Analytic number theory
Warm-upHumanCompute , where is the Möbius function.
2. Analytic number theory
Warm-upHumanWhich Dirichlet series equals for ?
3. Analytic number theory
CoreHumanThe prime number theorem says . Which statement is a correct reading of it?
4. Analytic number theory
CoreHumanIn the proof that (Theorem 7), why does stay bounded as (for fixed )?
5. Analytic number theory
CoreHumanGauss's identity says . Möbius inversion gives . Compute .
6. Analytic number theory
CoreHumanEvery non-trivial zero of with has been verified to lie on the critical line. What follows?
7. Analytic number theory
StretchHumanSuppose (hypothetically) that had a zero . Using the explicit formula, what is the best possible error term one could then hope for in ?
8. Analytic number theory
CoreFormalWhich Lean proposition correctly states the Riemann hypothesis (for Mathlib's riemannZeta : ℂ → ℂ, which assigns a junk value at the pole )?
9. Analytic number theory
StretchComputationalWrite mobius(n) for : return if has a repeated prime factor, and if is a product of distinct primes. Then check numerically that for (Proposition 1).
Common misconceptions
Misconception
"Billions of zeros have been checked, so the Riemann hypothesis is essentially proved." More than zeros are known to lie on the critical line, but that is a finite amount of evidence about infinitely many zeros, and number theory has famous examples of patterns that fail only at enormous heights: holds for every ever computed, yet Littlewood proved that it fails infinitely often. Numerical verification is evidence, not proof; the Riemann hypothesis is open.
Misconception
" means ." It means the ratio tends to . The difference grows without bound: it is at and is of order . Even the much better approximation has an error that is unbounded (by Littlewood) and, under the Riemann hypothesis, of order at most .
Misconception
" because ." The series converges only for ; is the value at of the unique analytic continuation, not a sum of the divergent series. (Regularisation methods in physics do assign to it, but that is a different, carefully specified procedure.)
Misconception
"Primes in progressions are as easy as primes: just repeat Euclid's argument." Euclid-style arguments exist for some progressions — , , and more generally exactly the classes with (Schur, 1912; Murty, 1988) — but not for all. Dirichlet needed -functions and the non-vanishing of . (Selberg found an elementary proof in 1949, but it is not a variation on Euclid.)
Viewpoints
Analysis— Fourier duality between primes and zeros
The explicit formula is a Fourier-type duality, made precise by Weil: schematically, for a nice even test function with Fourier transform ,
summing over the ordinates of non-trivial zeros on one side and prime powers on the other — the analogue of the Poisson summation formula that proves the functional equation. Analysis supplies the tools: Mellin and Laplace transforms, contour integration, Tauberian theorems (Wiener–Ikehara gives the prime number theorem from Theorem 7 alone), and sieve methods, which bound primes in sets where -functions say nothing. Modern highlights mix these: Yitang Zhang (2013) proved bounded gaps between primes, and Maynard and Tao (2013) found a simpler sieve; the best bound, , is due to the Polymath project.
Probability— Primes as a random-looking set
Cramér's model (each prime with probability ) is a heuristic that predicts the size of prime gaps (Cramér conjectured ) and the Riemann-hypothesis-sized error term. Some probabilistic statements are theorems: the Erdős–Kac theorem (1940) says that the number of distinct prime factors of a random is asymptotically normal with mean and variance — a central limit theorem for arithmetic. The Möbius function behaves like a random sign: the prime number theorem is equivalent to , and the Riemann hypothesis to , the square-root cancellation of a random walk. Sarnak's Möbius disjointness conjecture and Chowla's conjecture make " is random" precise; Tao and Teräväinen have proved cases of them.
Physics— Zeros and random matrices
In 1972 Hugh Montgomery computed (assuming the Riemann hypothesis, and for a restricted class of test functions) the pair correlation of the zeros : normalised spacings are distributed with density . Freeman Dyson recognised this as the pair correlation of eigenvalues of large random Hermitian matrices (the Gaussian Unitary Ensemble), which models energy levels of heavy nuclei and quantum chaotic systems. Andrew Odlyzko's computations of zeros near the -th zero (1980s onwards) match GUE statistics with striking precision. This suggests the Hilbert–Pólya idea that the 's are the eigenvalues of a self-adjoint operator, which would imply the Riemann hypothesis. All of this is conjecture supported by numerical evidence and partial theorems; no such operator is known. Random matrix theory also predicts moments of on the critical line (Keating and Snaith, 2000), again conjecturally.
Geometry— The Riemann hypothesis for curves is a theorem
For a smooth projective curve of genus over , the zeta function is a rational function with , it satisfies a functional equation, and all zeros of have absolute value — the exact analogue of the Riemann hypothesis, proved by Weil in the 1940s (Hasse had done in 1933) and generalised to all varieties by Deligne (1974). Geometrically, is a Lefschetz fixed-point formula for the Frobenius map acting on cohomology, and is a positivity statement (the Hodge index theorem on ). In the analogy, is a "curve" whose missing cohomology theory would prove the Riemann hypothesis; this analogy has guided research for eighty years but is not a proof strategy anyone has completed (see algebraic geometry and arithmetic geometry).
Algebra— Characters are Fourier analysis on a finite group
Dirichlet characters are the characters (one-dimensional representations) of the finite abelian group , and the orthogonality relations used in Theorem 10 are Fourier analysis on that group: the indicator function of a residue class is . Replacing by Galois groups leads to Artin -functions of Galois representations , whose analytic properties encode the splitting of primes in number fields (Chebotarev's density theorem generalises Dirichlet's theorem). The quadratic character satisfies , so is a statement about class numbers (see algebraic number theory). The Langlands programme predicts that all such -functions are automorphic (see the Langlands programme).
Wonder Trail
The trail starts where this lesson does — Euclid's proof and a table of primes — and is a good companion for younger readers.
Wonder Trail · Optional · never gradedPrimes forever2, 3, 5, 7, 11, 13, … The primes get rarer as the numbers grow. Do they ever run out? And how rare do they get?Follow the trailHistory and global context
- Mādhava of Sangamagrāma (Kerala, c. 1400) found the series , rediscovered by Gregory and Leibniz; it is . Leonhard Euler found the Euler product and proved that diverges (1737), and evaluated (1734).
- Gauss (from about 1792, described in an 1849 letter to Encke) and Legendre (1798) conjectured the prime number theorem. Dirichlet (1837) proved his theorem on primes in progressions, inventing characters and -functions. Pafnuty Chebyshev (1848–52) proved with explicit constants and Bertrand's postulate.
- Bernhard Riemann's eight-page memoir Ueber die Anzahl der Primzahlen unter einer gegebenen Grösse (1859) introduced as a complex function, proved the functional equation, sketched the explicit formula and stated the Riemann hypothesis. Hans von Mangoldt proved the explicit formula (1895); Jacques Hadamard and Charles de la Vallée Poussin independently proved the prime number theorem (1896).
- G. H. Hardy proved that infinitely many zeros lie on the critical line, and J. E. Littlewood that changes sign infinitely often (both 1914). Srinivasa Ramanujan and Hardy developed the circle method (1918), which I. M. Vinogradov used to prove that every sufficiently large odd number is a sum of three primes (1937); Harald Helfgott completed this ternary Goldbach theorem (2013). Chen Jingrun proved that every sufficiently large even number is a prime plus a product of at most two primes (1973).
- Atle Selberg and Paul Erdős found elementary proofs of the prime number theorem (1948–49). Ben Green and Terence Tao proved that the primes contain arbitrarily long arithmetic progressions (2004), and Yitang Zhang proved bounded gaps between primes (2013), improved by James Maynard, Tao and the Polymath project.
Further reading
- Tom M. Apostol, Introduction to Analytic Number Theory (1976) — arithmetic functions, Dirichlet series, Dirichlet's theorem and the prime number theorem, very carefully.
- Harold Davenport, Multiplicative Number Theory (1967; 3rd ed. revised by H. L. Montgomery, 2000) — the classical theory of and -functions, the explicit formula and zero-free regions, in short chapters.
- Hugh L. Montgomery and Robert C. Vaughan, Multiplicative Number Theory I: Classical Theory (2007) — a modern graduate course.
- H. M. Edwards, Riemann's Zeta Function (1974) — Riemann's memoir read line by line, with its history.
- E. C. Titchmarsh, The Theory of the Riemann Zeta-Function (1951; 2nd ed. revised by D. R. Heath-Brown, 1986) — the standard reference on .
- Don Zagier, Newman's short proof of the prime number theorem, American Mathematical Monthly 104 (1997) — four pages.
- Henryk Iwaniec and Emmanuel Kowalski, Analytic Number Theory (2004) — the research-level panorama.