Sieve of Eratosthenes¶
List every prime up to \(n\) by crossing out multiples: start with 2, cross out its multiples, move to the next number still standing, and repeat. What is never crossed out is prime.
| Time | Extra space | Output |
|---|---|---|
| \(O(n \log \log n)\) | \(O(n)\) bits | which numbers up to \(n\) are prime; smallest prime factors |
The idea¶
A prime is a whole number above 1 whose only divisors are 1 and itself: 2, 3, 5, 7, 11, ... Testing each number by trial division costs up to \(\sqrt k\) divisions per number. The sieve, described by Eratosthenes of Cyrene (about 240 BC, the same man who measured the size of the Earth), does the opposite: instead of asking "what divides \(k\)?", each prime marks its multiples as not prime.
Two shortcuts make it fast:
- Start crossing out at \(p^2\): smaller multiples \(2p, 3p, \dots\) already have a smaller prime factor and were crossed out earlier.
- Stop when \(p^2 > n\): every composite number up to \(n\) has a prime factor at most \(\sqrt n\).
A small change gives more: store, for each number, the smallest prime that crossed it out. Then any number up to \(n\) can be factored by dividing by its smallest prime factor again and again, in \(O(\log k)\) steps.
How it works¶
- Make a list
is_prime[0..n], all true except 0 and 1. - For \(p = 2, 3, \dots\) while \(p^2 \le n\): if
is_prime[p], setis_prime[k] = falsefor \(k = p^2, p^2 + p, p^2 + 2p, \dots \le n\). - The numbers still marked true are the primes.
Code¶
def sieve(n):
"""Sieve of Eratosthenes: is_prime[k] for every k from 0 to n."""
is_prime = [True] * (n + 1)
is_prime[0] = False
if n >= 1:
is_prime[1] = False
p = 2
while p * p <= n:
if is_prime[p]:
for k in range(p * p, n + 1, p): # smaller multiples were crossed out by smaller primes
is_prime[k] = False
p += 1
return is_prime
def smallest_factors(n):
"""spf[k] = the smallest prime that divides k (for k >= 2): factor any k <= n in O(log k) steps."""
spf = list(range(n + 1))
p = 2
while p * p <= n:
if spf[p] == p: # p is prime
for k in range(p * p, n + 1, p):
if spf[k] == k:
spf[k] = p
p += 1
return spf
def factorize(k, spf):
out = []
while k > 1:
out.append(spf[k])
k //= spf[k]
return out
if __name__ == "__main__":
is_p = sieve(50)
print([k for k in range(51) if is_p[k]])
print(sum(sieve(1_000_000)))
spf = smallest_factors(10_000)
print(factorize(360, spf), factorize(9973, spf), factorize(2024, spf))
import java.util.ArrayList;
import java.util.List;
public class Sieve {
/** Sieve of Eratosthenes: isPrime[k] for every k from 0 to n. */
static boolean[] sieve(int n) {
boolean[] isPrime = new boolean[n + 1];
for (int k = 2; k <= n; k++) isPrime[k] = true;
for (long p = 2; p * p <= n; p++)
if (isPrime[(int) p])
for (long k = p * p; k <= n; k += p) isPrime[(int) k] = false; // smaller multiples are done
return isPrime;
}
/** spf[k] = the smallest prime that divides k (for k >= 2): factor any k <= n in O(log k) steps. */
static int[] smallestFactors(int n) {
int[] spf = new int[n + 1];
for (int k = 0; k <= n; k++) spf[k] = k;
for (long p = 2; p * p <= n; p++)
if (spf[(int) p] == p) // p is prime
for (long k = p * p; k <= n; k += p)
if (spf[(int) k] == k) spf[(int) k] = (int) p;
return spf;
}
static List<Integer> factorize(int k, int[] spf) {
List<Integer> out = new ArrayList<>();
while (k > 1) { out.add(spf[k]); k /= spf[k]; }
return out;
}
public static void main(String[] args) {
boolean[] isP = sieve(50);
List<Integer> primes = new ArrayList<>();
for (int k = 0; k <= 50; k++) if (isP[k]) primes.add(k);
System.out.println(primes);
int count = 0;
for (boolean b : sieve(1_000_000)) if (b) count++;
System.out.println(count);
int[] spf = smallestFactors(10_000);
System.out.println(factorize(360, spf) + " " + factorize(9973, spf) + " " + factorize(2024, spf));
}
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Sieve of Eratosthenes: isPrime[k] for every k from 0 to n.
vector<bool> sieve(int n) {
vector<bool> isPrime(n + 1, true);
isPrime[0] = false;
if (n >= 1) isPrime[1] = false;
for (long long p = 2; p * p <= n; p++)
if (isPrime[p])
for (long long k = p * p; k <= n; k += p) isPrime[k] = false; // smaller multiples are done
return isPrime;
}
// spf[k] = the smallest prime that divides k (for k >= 2): factor any k <= n in O(log k) steps.
vector<int> smallestFactors(int n) {
vector<int> spf(n + 1);
for (int k = 0; k <= n; k++) spf[k] = k;
for (long long p = 2; p * p <= n; p++)
if (spf[p] == p) // p is prime
for (long long k = p * p; k <= n; k += p)
if (spf[k] == k) spf[k] = p;
return spf;
}
string factorize(int k, const vector<int>& spf) {
string s = "[";
for (bool first = true; k > 1; k /= spf[k], first = false) s += (first ? "" : ", ") + to_string(spf[k]);
return s + "]";
}
int main() {
vector<bool> isP = sieve(50);
string s = "[";
for (int k = 0; k <= 50; k++) if (isP[k]) s += (s.size() > 1 ? ", " : "") + to_string(k);
cout << s << "]\n";
int count = 0;
for (bool b : sieve(1000000)) if (b) count++;
cout << count << "\n";
vector<int> spf = smallestFactors(10000);
cout << factorize(360, spf) << " " << factorize(9973, spf) << " " << factorize(2024, spf) << "\n";
}
Output: the 15 primes up to 50; 78498, the number of primes below one million; then the factorizations
\(360 = 2^3 \cdot 3^2 \cdot 5\), \(9973\) (prime) and \(2024 = 2^3 \cdot 11 \cdot 23\).
When to use it¶
- Many prime questions up to a limit (say \(10^7\) or \(10^8\)): one sieve, then each answer is a look-up.
- Factoring many small numbers: the smallest-prime-factor table.
- Computing number-theory functions for all \(k \le n\) (Euler's \(\varphi\), number of divisors, Möbius) with the same crossing-out pattern.
- Not for one huge number: to test whether a 300-digit number is prime, use a probabilistic test such as Miller–Rabin (see fast exponentiation).
Common mistakes¶
- Marking 0 and 1 as prime.
- Overflow in \(p \cdot p\): for \(n\) near \(2^{31}\), compute \(p^2\) in 64 bits (the code uses
long). - Memory: a list of a billion Python booleans does not fit; use a
bytearray, a bit set, or a segmented sieve. - Starting at \(2p\) instead of \(p^2\): still correct, just slower.
Going deeper¶
Each part below starts closed; open the ones you want.
Sample walkthrough: up to 50
| Prime | Crossed out (starting at \(p^2\)) |
|---|---|
| 2 | 4, 6, 8, 10, ..., 50 (24 numbers) |
| 3 | 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48 (some already gone) |
| 5 | 25, 30, 35, 40, 45, 50 |
| 7 | 49 |
| 11 | \(11^2 = 121 > 50\): stop |
Left standing: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47.
The math: why it is \(O(n \log \log n)\), and how many primes there are
Work. Each prime \(p \le \sqrt n\) crosses out at most \(n / p\) numbers, so the total is at most
using Mertens' theorem (1874): the sum of \(1/p\) over primes up to \(x\) is \(\ln \ln x + 0.2615\ldots + o(1)\). \(\log \log n\) grows so slowly (about 3 for \(n = 10^9\)) that the sieve is nearly linear.
How many primes? Euclid proved there are infinitely many: if \(p_1, \dots, p_k\) were all of them, \(p_1 p_2 \cdots p_k + 1\) would have a prime factor not in the list. The prime number theorem (Hadamard and de la Vallée Poussin, 1896) says the number of primes up to \(x\), \(\pi(x)\), is about \(x / \ln x\). For \(x = 10^6\): \(10^6 / \ln 10^6 \approx 72{,}382\), against the true 78,498 that the program counted; the integral \(\int_2^x \frac{dt}{\ln t} \approx 78{,}626\) is much closer.
Why stopping at \(\sqrt n\) is enough. If \(k = ab\) with \(1 < a \le b\), then \(a^2 \le k\), so \(a \le \sqrt k\); the smallest prime factor of a composite \(k\) is at most \(\sqrt k \le \sqrt n\).
Watch it step by step
Red: the multiples about to be crossed out. Green: primes found. Faded: crossed out.
Variations and related ideas
- Linear sieve: crosses each composite out exactly once (by its smallest prime factor), \(O(n)\), and builds the smallest-factor table at the same time.
- Segmented sieve: sieve a window \([L, R]\) using the primes up to \(\sqrt R\); needs only \(O(\sqrt R)\) memory and finds primes near \(10^{12}\).
- Odd numbers only / wheel: skip multiples of 2 (or 2, 3, 5) to halve (or more) the memory and time.
- Sieve of Atkin: \(O(n / \log \log n)\) in theory, rarely faster in practice.
See also¶
- Euclid's GCD
- Fast exponentiation: primality tests for big numbers
- Big-O
References¶
- Nicomachus of Gerasa, Introduction to Arithmetic, Book I, Chapter 13 (about 100 AD; the oldest surviving description, credited to Eratosthenes).
- G. H. Hardy and E. M. Wright, An Introduction to the Theory of Numbers, 6th ed., Oxford, 2008, Chapters 1–2 and 22.
- P. Pritchard, "Linear prime-number sieves: a family tree", Science of Computer Programming 9(1), 1987.