19Number theoryMaths

GCD, primes and the sieve

Count primes below n by crossing out multiples; get a GCD by taking remainders until one is zero.

Clues: count the primes below n, or find the GCD of two numbers. Worked example: LeetCode 204, Count Primes (Medium): count the primes strictly less than n. Free references: GeeksforGeeks, "Sieve of Eratosthenes" and "Euclidean algorithms (Basic and Extended)".

The idea

The sieve is a roll call. Students 2 to 19 stand up. The first one standing is prime: that's 2, because no smaller number sat it down. It sits down all its multiples: 4, 6, 8 and so on up to 18. Repeat with the next one standing. Whoever never sits down is prime, and you never divide.

Start crossing out at p × p. After 2's turn it's 3's turn, and 6 = 3 × 2 already sat down for 2, so 3 starts at 3 × 3 = 9. Stop once p × p ≥ n: for n = 20, p = 5 gives 25, so stop.

GCD by Euclid. The GCD is the biggest number that divides both. The rule: gcd(a, b) = gcd(b, a mod b), where a mod b is what's left after fitting in as many b's as you can. It's safe because 18 = 12 + 6, so anything that divides 18 and 12 also divides 6.

The walk

Primes below 20: 2, 3, 5, 7, 11, 13, 17, 19, so 8. Quick check from the post: there are 10 primes below 30 (2, 3, 5, 7, 11, 13, 17, 19, 23, 29).

gcd(18, 12), one row per turn of the loop:

aba mod b
18126
1260
60stop: return a

The GCD is 6. gcd(12, 18) works too: the first turn just swaps them.

Pseudocode

Pseudocode · any language
function countPrimes(n):
    if n < 3:  // only 0 and 1 are below n
        return 0
    isPrime = array of n values, all true
    set isPrime[0] and isPrime[1] to false
    p = 2
    while p * p < n:
        if isPrime[p]:
            m = p * p
            while m < n:
                isPrime[m] = false
                m = m + p
        p = p + 1
    return number of true values in isPrime

function gcd(a, b):
    while b != 0:
        remainder = a mod b
        a = b
        b = remainder
    return a

The trap: mark 0 and 1. They start as true; forget the line that sets them to false and they get counted. countPrimes(10) then returns 6 instead of 4 (only 2, 3, 5 and 7 are prime).

Code

class Solution {
    public int countPrimes(int n) {
        if (n < 3) return 0;  // only 0 and 1 are below n
        boolean[] isPrime = new boolean[n];
        java.util.Arrays.fill(isPrime, true);
        isPrime[0] = false;
        isPrime[1] = false;
        long p = 2;
        while (p * p < n) {
            if (isPrime[(int) p]) {
                long m = p * p;
                while (m < n) {
                    isPrime[(int) m] = false;
                    m = m + p;
                }
            }
            p = p + 1;
        }
        int count = 0;
        for (boolean b : isPrime) if (b) count++;
        return count;
    }

    public static int gcd(int a, int b) {
        while (b != 0) {
            int remainder = a % b;
            a = b;
            b = remainder;
        }
        return a;
    }
}

Complexity

FunctionTimeExtra space
countPrimesO(n log log n)O(n)
gcdO(log(min(a, b)))O(1)

Each prime p crosses out about n / p numbers, and the sum of n / p over primes up to n grows like n log log n: for n = 1,000,000 that's about 2.1 million crossings in all. In gcd, the numbers at least halve every two turns.

How it was tested

The slide checks run on this code: primes below 20 = 8, primes below 30 = 10, countPrimes(10) = 4 (and 6 if 0 and 1 are left marked true), gcd(18, 12) = 6. On 31 October 2026 both functions in all four languages were run on 3,008 inputs (3,000 random n from 0 to 2,000 with random pairs up to 1,000, the slide examples, the edge cases n = 0, 1, 2 and 3 and gcd(6, 0), and n = 5,000,000 with a pair near 10⁹) and matched trial division and a reference GCD on every one.

Practice

Try these in order. Open the worked example first if you got stuck on the slides.