Skip to main content

Divisors, GCD and Primes

Editorial

The test everyone writes

Is 2,147,483,647 prime? It's the largest 32-bit integer, and it is. Here's the test most people write: try every divisor up to the square root, stopping once i × i passes n:

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) {                  // the test everyone writes first
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return false;
    return true;
}

int main() {
    printf("%s %s\n", isPrime(97) ? "true" : "false", isPrime(2147483647) ? "true" : "false");
}

// true false
def is_prime(n):                      # the test everyone writes first
    if n < 2:
        return False
    i = 2
    while i * i <= n:
        if n % i == 0:
            return False
        i += 1
    return True


print(is_prime(97), is_prime(2147483647))

# True True
public class Main {
    static boolean isPrime(int n) {    // the test everyone writes first
        if (n < 2) return false;
        for (int i = 2; i * i <= n; i++)
            if (n % i == 0) return false;
        return true;
    }

    public static void main(String[] args) {
        System.out.println(isPrime(97) + " " + isPrime(2147483647));
    }
}

// true false
function isPrime(n) {                  // the test everyone writes first
    if (n < 2) return false;
    for (let i = 2; i * i <= n; i++)
        if (n % i === 0) return false;
    return true;
}

console.log(isPrime(97), isPrime(2147483647));

// true true

Python and JavaScript said yes. Java said no, after 10 seconds, and C++ said no at once. In 32 bits, i × i passes the largest int at i = 46,341 and wraps around to a negative number, so i * i <= n never turns false: no int is bigger than 2,147,483,647. Java ran on until i reached n itself and found that n divides n. In C++ the overflow is undefined behaviour, and the compiled program printed false without the wait. Python's ints don't overflow, and JavaScript's numbers are exact far past this size.

The idea under the test is right, and it's the idea of this lesson: divisors come in pairs, and one of each pair is at most √n. That bound finds every divisor of a number and tests it for primality; Euclid's algorithm finds the greatest common divisor of two numbers in a few dozen steps; and the sieve finds every prime below n at once.

Two dashed panels for 36. Left, d from 1 to 6: six rows, each an orange tile d, a times sign and a blue tile with its partner 36 / d, reading 1 × 36, 2 × 18, 3 × 12 and 4 × 9 = 36; the 5 row is pale, with 36 % 5 = 1: not one; the 6 row, 6 × 6 = 36, is ringed in green and kept once. Right, the divisors, in order: the small halves as found, 1, 2, 3, 4 and 6, in orange; the large halves reversed, 9, 12, 18 and 36, in blue; together, sorted, 1 2 3 4 6 9 12 18 36; 9 divisors from 6 tries, not 36. Focal line: stop at √n: every divisor above it pairs with one below. Title: divisors come in pairs.

Every divisor in √n steps

If d divides n, so does n / d, and the two sit on either side of √n: d × (n / d) = n, so both can't be bigger than √n. Checking d from 1 up to √n and keeping both halves of each pair finds every divisor. The test d <= n / d says the same as d * d <= n, and can't overflow:

#include <bits/stdc++.h>
using namespace std;

vector<long long> divisors(long long n) {   // every divisor of n >= 1, in order
    vector<long long> small, large;
    for (long long d = 1; d <= n / d; d++) {   // d <= n / d is d * d <= n, without the overflow
        if (n % d != 0) continue;
        small.push_back(d);
        if (d != n / d) large.push_back(n / d);   // a square's root pairs with itself: once
    }
    small.insert(small.end(), large.rbegin(), large.rend());
    return small;
}

int main() {
    for (long long n : {36LL, 97LL}) {
        printf("%lld:", n);
        for (long long d : divisors(n)) printf(" %lld", d);
        printf("\n");
    }
    printf("720720: %zu divisors, d up to %lld\n", divisors(720720).size(), (long long) sqrt(720720.0));
}

// 36: 1 2 3 4 6 9 12 18 36
// 97: 1 97
// 720720: 240 divisors, d up to 848
import math


def divisors(n):                      # every divisor of n >= 1, in order
    small, large = [], []
    d = 1
    while d <= n // d:                # d <= n / d is d * d <= n, without the overflow
        if n % d == 0:
            small.append(d)
            if d != n // d:           # a square's root pairs with itself: once
                large.append(n // d)
        d += 1
    return small + large[::-1]


for n in (36, 97):
    print(f"{n}:", " ".join(map(str, divisors(n))))
print(f"720720: {len(divisors(720720))} divisors, d up to {math.isqrt(720720)}")

# 36: 1 2 3 4 6 9 12 18 36
# 97: 1 97
# 720720: 240 divisors, d up to 848
import java.util.*;

public class Main {
    static List<Long> divisors(long n) {   // every divisor of n >= 1, in order
        List<Long> small = new ArrayList<>(), large = new ArrayList<>();
        for (long d = 1; d <= n / d; d++) {    // d <= n / d is d * d <= n, without the overflow
            if (n % d != 0) continue;
            small.add(d);
            if (d != n / d) large.add(n / d);  // a square's root pairs with itself: once
        }
        Collections.reverse(large);
        small.addAll(large);
        return small;
    }

    public static void main(String[] args) {
        for (long n : new long[]{36, 97}) {
            StringBuilder line = new StringBuilder(n + ":");
            for (long d : divisors(n)) line.append(" ").append(d);
            System.out.println(line);
        }
        System.out.println("720720: " + divisors(720720).size() + " divisors, d up to " + (long) Math.sqrt(720720));
    }
}

// 36: 1 2 3 4 6 9 12 18 36
// 97: 1 97
// 720720: 240 divisors, d up to 848
function divisors(n) {                 // every divisor of n >= 1, in order
    const small = [], large = [];
    for (let d = 1; d <= Math.floor(n / d); d++) {   // d <= n / d is d * d <= n, without the overflow
        if (n % d !== 0) continue;
        small.push(d);
        if (d !== n / d) large.push(n / d);          // a square's root pairs with itself: once
    }
    return small.concat(large.reverse());
}

for (const n of [36, 97]) console.log(`${n}: ${divisors(n).join(" ")}`);
console.log(`720720: ${divisors(720720).length} divisors, d up to ${Math.floor(Math.sqrt(720720))}`);

// 36: 1 2 3 4 6 9 12 18 36
// 97: 1 97
// 720720: 240 divisors, d up to 848

36 has nine divisors: four pairs and 6, the square root, which pairs with itself and is kept once. 97 has only 1 and itself, so it's prime. 720,720 has 240 divisors, found by trying d up to 848, where trying every number up to n would take 720,720 tries. O(√n) time.

The small halves come out in increasing order and the large halves in decreasing order, so the large ones are put after the small ones reversed, and the list is sorted with no sort.

The greatest common divisor: Euclid

The greatest common divisor of a and b, gcd(a, b), is the largest number that divides both: gcd(12, 18) = 6. Listing the divisors of both would work; Euclid's algorithm is far faster. It replaces the pair (a, b) by (b, a mod b) until the second number is 0, and the first is the answer.

Why it's right. Any number that divides a and b also divides a − q × b, which is a mod b. Any number that divides b and a mod b also divides q × b + a mod b, which is a. So (a, b) and (b, a mod b) have exactly the same common divisors, and the same greatest one. And gcd(a, 0) = a, because every number divides 0.

Two dashed panels. Left, gcd(1071, 462): four rows of a blue tile a and an orange tile b, joined by down arrows: 1071 and 462 with 1071 = 2 × 462 + 147, then 462 and 147 with 462 = 3 × 147 + 21, then 147 and 21 with 147 = 7 × 21 + 0, each remainder in green, and last 21, ringed in green, beside a pale 0 and b is 0: gcd = 21. Right, check: a ticked card reading gcd = 21, 1071 = 51 × 21, 462 = 22 × 21 and 3 steps, over the words each step keeps every common divisor, so the last pair's answer is the first pair's too. Focal line: (a, b) → (b, a mod b) until b is 0. Title: Euclid shrinks the pair.
#include <bits/stdc++.h>
using namespace std;

long long gcd(long long a, long long b) {   // Euclid: (a, b) becomes (b, a mod b) until b is 0
    while (b != 0) {
        long long r = a % b;
        a = b;
        b = r;
    }
    return a;
}

int steps(long long a, long long b) {    // how many times the loop above runs
    int k = 0;
    for (; b != 0; k++) {
        long long r = a % b;
        a = b;
        b = r;
    }
    return k;
}

int main() {
    printf("gcd(1071, 462) = %lld in %d steps\n", gcd(1071, 462), steps(1071, 462));
    printf("gcd(0, 5) = %lld\n", gcd(0, 5));
    printf("gcd(1836311903, 1134903170) = %lld in %d steps\n", gcd(1836311903, 1134903170),
           steps(1836311903, 1134903170));
}

// gcd(1071, 462) = 21 in 3 steps
// gcd(0, 5) = 5
// gcd(1836311903, 1134903170) = 1 in 44 steps
def gcd(a, b):                        # Euclid: (a, b) becomes (b, a mod b) until b is 0
    while b != 0:
        a, b = b, a % b
    return a


def steps(a, b):                      # how many times the loop above runs
    k = 0
    while b != 0:
        a, b = b, a % b
        k += 1
    return k


print(f"gcd(1071, 462) = {gcd(1071, 462)} in {steps(1071, 462)} steps")
print(f"gcd(0, 5) = {gcd(0, 5)}")
print(f"gcd(1836311903, 1134903170) = {gcd(1836311903, 1134903170)} in {steps(1836311903, 1134903170)} steps")

# gcd(1071, 462) = 21 in 3 steps
# gcd(0, 5) = 5
# gcd(1836311903, 1134903170) = 1 in 44 steps
public class Main {
    static long gcd(long a, long b) {  // Euclid: (a, b) becomes (b, a mod b) until b is 0
        while (b != 0) {
            long r = a % b;
            a = b;
            b = r;
        }
        return a;
    }

    static int steps(long a, long b) { // how many times the loop above runs
        int k = 0;
        for (; b != 0; k++) {
            long r = a % b;
            a = b;
            b = r;
        }
        return k;
    }

    public static void main(String[] args) {
        System.out.println("gcd(1071, 462) = " + gcd(1071, 462) + " in " + steps(1071, 462) + " steps");
        System.out.println("gcd(0, 5) = " + gcd(0, 5));
        System.out.println("gcd(1836311903, 1134903170) = " + gcd(1836311903L, 1134903170L) + " in "
            + steps(1836311903L, 1134903170L) + " steps");
    }
}

// gcd(1071, 462) = 21 in 3 steps
// gcd(0, 5) = 5
// gcd(1836311903, 1134903170) = 1 in 44 steps
function gcd(a, b) {                   // Euclid: (a, b) becomes (b, a mod b) until b is 0
    while (b !== 0) [a, b] = [b, a % b];
    return a;
}

function steps(a, b) {                 // how many times the loop above runs
    let k = 0;
    for (; b !== 0; k++) [a, b] = [b, a % b];
    return k;
}

console.log(`gcd(1071, 462) = ${gcd(1071, 462)} in ${steps(1071, 462)} steps`);
console.log(`gcd(0, 5) = ${gcd(0, 5)}`);
console.log(`gcd(1836311903, 1134903170) = ${gcd(1836311903, 1134903170)} in ${steps(1836311903, 1134903170)} steps`);

// gcd(1071, 462) = 21 in 3 steps
// gcd(0, 5) = 5
// gcd(1836311903, 1134903170) = 1 in 44 steps

1,071 and 462 took 3 steps: 1,071 mod 462 = 147, 462 mod 147 = 21, 147 mod 21 = 0, so the answer is 21. Every two steps at least halve the smaller number, so the steps are O(log min(a, b)). The worst case is two neighbouring Fibonacci numbers, whose remainders shrink as slowly as possible: the largest pair below 2³¹, 1,836,311,903 and 1,134,903,170, took 44 steps. For more than two numbers, fold: gcd(a, b, c) = gcd(gcd(a, b), c), and a running gcd that reaches 1 can stop there.

The least common multiple is the smallest number both divide: lcm(4, 6) = 12. It's a × b / gcd(a, b), but written that way a × b can overflow before the division brings it back down. gcd(a, b) divides a exactly, so divide first:

#include <bits/stdc++.h>
using namespace std;

long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); }

long long lcm(long long a, long long b) {   // divide first: gcd divides a exactly
    return a / gcd(a, b) * b;
}

int main() {
    printf("%lld %lld %lld\n", lcm(4, 6), lcm(21, 6), lcm(60000, 90000));
}

// 12 42 180000
def gcd(a, b):
    return a if b == 0 else gcd(b, a % b)


def lcm(a, b):                        # divide first: gcd divides a exactly
    return a // gcd(a, b) * b


print(lcm(4, 6), lcm(21, 6), lcm(60000, 90000))

# 12 42 180000
public class Main {
    static long gcd(long a, long b) { return b == 0 ? a : gcd(b, a % b); }

    static long lcm(long a, long b) {  // divide first: gcd divides a exactly
        return a / gcd(a, b) * b;
    }

    public static void main(String[] args) {
        System.out.println(lcm(4, 6) + " " + lcm(21, 6) + " " + lcm(60000, 90000));
    }
}

// 12 42 180000
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));

function lcm(a, b) {                   // divide first: gcd divides a exactly
    return a / gcd(a, b) * b;
}

console.log(lcm(4, 6), lcm(21, 6), lcm(60000, 90000));

// 12 42 180000

lcm(60,000, 90,000) = 180,000 fits easily in 32 bits, but 60,000 × 90,000 = 5,400,000,000 doesn't (Mistakes that cost an interview shows what multiplying first prints).

The GCD of two strings. A string x divides s if s is x repeated, like "AB" divides "ABABAB". Greatest Common Divisor of Strings asks for the longest x that divides both s and t. If some x builds both, then s + t and t + s are the same string, x repeated; and if s + t equals t + s, both are built from the prefix whose length is gcd(len(s), len(t)). So one comparison decides whether there is an answer, and Euclid gives its length:

#include <bits/stdc++.h>
using namespace std;

string gcdOfStrings(const string& s, const string& t) {
    if (s + t != t + s) return "";         // no common building block
    return s.substr(0, __gcd(s.size(), t.size()));   // the block is gcd(lengths) long
}

int main() {
    printf("[%s] [%s] [%s]\n", gcdOfStrings("ABCABC", "ABC").c_str(), gcdOfStrings("ABABAB", "ABAB").c_str(),
           gcdOfStrings("LEET", "CODE").c_str());
}

// [ABC] [AB] []
import math


def gcd_of_strings(s, t):
    if s + t != t + s:
        return ""                     # no common building block
    return s[:math.gcd(len(s), len(t))]   # the block is gcd(lengths) long


print(f"[{gcd_of_strings('ABCABC', 'ABC')}] [{gcd_of_strings('ABABAB', 'ABAB')}] [{gcd_of_strings('LEET', 'CODE')}]")

# [ABC] [AB] []
public class Main {
    static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

    static String gcdOfStrings(String s, String t) {
        if (!(s + t).equals(t + s)) return "";   // no common building block
        return s.substring(0, gcd(s.length(), t.length()));   // the block is gcd(lengths) long
    }

    public static void main(String[] args) {
        System.out.println("[" + gcdOfStrings("ABCABC", "ABC") + "] [" + gcdOfStrings("ABABAB", "ABAB") + "] ["
            + gcdOfStrings("LEET", "CODE") + "]");
    }
}

// [ABC] [AB] []
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));

function gcdOfStrings(s, t) {
    if (s + t !== t + s) return "";    // no common building block
    return s.slice(0, gcd(s.length, t.length));   // the block is gcd(lengths) long
}

console.log(`[${gcdOfStrings("ABCABC", "ABC")}] [${gcdOfStrings("ABABAB", "ABAB")}] [${gcdOfStrings("LEET", "CODE")}]`);

// [ABC] [AB] []

"ABCABC" and "ABC" give "ABC"; "ABABAB" and "ABAB" give "AB", the gcd of 6 and 4 being 2; "LEET" and "CODE" give the empty string, because "LEETCODE" isn't "CODELEET".

Is n prime?

n is prime if it's at least 2 and nothing from 2 to √n divides it: a divisor above √n would pair with one below. The same loop as the first one, with the bound that can't overflow:

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) {
    if (n < 2) return false;               // 0 and 1 aren't prime
    for (int i = 2; i <= n / i; i++)       // i <= n / i: the same as i * i <= n, and it can't overflow
        if (n % i == 0) return false;
    return true;
}

int main() {
    for (int n : {1, 2, 97, 2147483647, 2147483646})
        printf("%d %s\n", n, isPrime(n) ? "prime" : "not prime");
}

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime
def is_prime(n):
    if n < 2:
        return False                  # 0 and 1 aren't prime
    i = 2
    while i <= n // i:                # i <= n / i: the same as i * i <= n, and it can't overflow
        if n % i == 0:
            return False
        i += 1
    return True


for n in (1, 2, 97, 2147483647, 2147483646):
    print(n, "prime" if is_prime(n) else "not prime")

# 1 not prime
# 2 prime
# 97 prime
# 2147483647 prime
# 2147483646 not prime
public class Main {
    static boolean isPrime(int n) {
        if (n < 2) return false;           // 0 and 1 aren't prime
        for (int i = 2; i <= n / i; i++)   // i <= n / i: the same as i * i <= n, and it can't overflow
            if (n % i == 0) return false;
        return true;
    }

    public static void main(String[] args) {
        for (int n : new int[]{1, 2, 97, 2147483647, 2147483646})
            System.out.println(n + " " + (isPrime(n) ? "prime" : "not prime"));
    }
}

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime
function isPrime(n) {
    if (n < 2) return false;               // 0 and 1 aren't prime
    for (let i = 2; i <= Math.floor(n / i); i++)   // i <= n / i: the same as i * i <= n
        if (n % i === 0) return false;
    return true;
}

for (const n of [1, 2, 97, 2147483647, 2147483646])
    console.log(n, isPrime(n) ? "prime" : "not prime");

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime

2,147,483,647 is prime in all four now, after 46,339 tries. 0 and 1 aren't prime, and 2 is the only even one.

Every prime above 3 is one more or one less than a multiple of 6: 6k, 6k + 2 and 6k + 4 are even, and 6k + 3 is a multiple of 3. So after 2 and 3, only 6k − 1 and 6k + 1 need trying:

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) {                     // 2 and 3, then only 6k - 1 and 6k + 1
    if (n < 4) return n >= 2;              // 2 and 3 are prime; 0 and 1 aren't
    if (n % 2 == 0 || n % 3 == 0) return false;
    for (int i = 5; i <= n / i; i += 6)    // i is 6k - 1, and i + 2 is 6k + 1
        if (n % i == 0 || n % (i + 2) == 0) return false;
    return true;
}

int main() {
    for (int n : {1, 2, 97, 2147483647, 2147483646})
        printf("%d %s\n", n, isPrime(n) ? "prime" : "not prime");
}

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime
def is_prime(n):                      # 2 and 3, then only 6k - 1 and 6k + 1
    if n < 4:
        return n >= 2                 # 2 and 3 are prime; 0 and 1 aren't
    if n % 2 == 0 or n % 3 == 0:
        return False
    i = 5
    while i <= n // i:                # i is 6k - 1, and i + 2 is 6k + 1
        if n % i == 0 or n % (i + 2) == 0:
            return False
        i += 6
    return True


for n in (1, 2, 97, 2147483647, 2147483646):
    print(n, "prime" if is_prime(n) else "not prime")

# 1 not prime
# 2 prime
# 97 prime
# 2147483647 prime
# 2147483646 not prime
public class Main {
    static boolean isPrime(int n) {        // 2 and 3, then only 6k - 1 and 6k + 1
        if (n < 4) return n >= 2;          // 2 and 3 are prime; 0 and 1 aren't
        if (n % 2 == 0 || n % 3 == 0) return false;
        for (int i = 5; i <= n / i; i += 6)   // i is 6k - 1, and i + 2 is 6k + 1
            if (n % i == 0 || n % (i + 2) == 0) return false;
        return true;
    }

    public static void main(String[] args) {
        for (int n : new int[]{1, 2, 97, 2147483647, 2147483646})
            System.out.println(n + " " + (isPrime(n) ? "prime" : "not prime"));
    }
}

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime
function isPrime(n) {                     // 2 and 3, then only 6k - 1 and 6k + 1
    if (n < 4) return n >= 2;              // 2 and 3 are prime; 0 and 1 aren't
    if (n % 2 === 0 || n % 3 === 0) return false;
    for (let i = 5; i <= Math.floor(n / i); i += 6)   // i is 6k - 1, and i + 2 is 6k + 1
        if (n % i === 0 || n % (i + 2) === 0) return false;
    return true;
}

for (const n of [1, 2, 97, 2147483647, 2147483646])
    console.log(n, isPrime(n) ? "prime" : "not prime");

// 1 not prime
// 2 prime
// 97 prime
// 2147483647 prime
// 2147483646 not prime

The same answers, with 15,448 divisions for 2,147,483,647 instead of 46,339, about a third. Still O(√n) per number: fine for one number, too slow for every number below a million, which is the sieve's job.

Every prime below n: the sieve

To find every prime below n, the Sieve of Eratosthenes starts with every number from 2 marked as a candidate. The smallest unmarked number is prime; cross out its multiples, then take the next unmarked number, and repeat.

Each prime p starts crossing at p × p, not at 2p. A smaller multiple, k × p with k below p, has a prime factor smaller than p (some prime factor of k), so it was crossed out already. And once p × p reaches n, p crosses out nothing, so the loop can stop: every composite below n has a prime factor no bigger than its square root, and has been crossed out by then.

Two dashed panels. Left, 1 to 50, as 5 begins: a ten-by-five grid of tiles where 2, 3 and 5 are green, the multiples of 2 from 4 are pale with a dashed blue edge, the odd multiples of 3 from 9 pale with a dashed purple edge, 25 and 35 are orange, new for 5, with 25 ringed, and the rest grey; a key reads crossed by 2, crossed by 3, new for 5. Right, multiples of 5: 10 = 2 × 5, crossed by 2; 15 = 3 × 5, crossed by 3; 20 = 4 × 5, crossed by 2; 25 = 5 × 5, ringed in orange, the start: new; then 35; 45 and 50 are gone. Focal line: k × p with k below p has a smaller prime factor. Title: start crossing at p × p.
#include <bits/stdc++.h>
using namespace std;

int countPrimes(int n) {                  // how many primes are below n
    vector<bool> composite(max(n, 2), false);
    for (long long p = 2; p * p < n; p++) {   // a prime with p * p >= n crosses nothing out
        if (composite[p]) continue;
        for (long long m = p * p; m < n; m += p)   // smaller multiples have a smaller prime factor
            composite[m] = true;
    }
    int count = 0;
    for (int k = 2; k < n; k++) count += !composite[k];
    return count;
}

int main() {
    printf("%d %d %d %d\n", countPrimes(10), countPrimes(100), countPrimes(1000000), countPrimes(5000000));
}

// 4 25 78498 348513
def count_primes(n):                  # how many primes are below n
    composite = [False] * max(n, 2)
    p = 2
    while p * p < n:                  # a prime with p * p >= n crosses nothing out
        if not composite[p]:
            for m in range(p * p, n, p):   # smaller multiples have a smaller prime factor
                composite[m] = True
        p += 1
    return sum(1 for k in range(2, n) if not composite[k])


print(count_primes(10), count_primes(100), count_primes(1000000), count_primes(5000000))

# 4 25 78498 348513
public class Main {
    static int countPrimes(int n) {       // how many primes are below n
        boolean[] composite = new boolean[Math.max(n, 2)];
        for (long p = 2; p * p < n; p++) {   // a prime with p * p >= n crosses nothing out
            if (composite[(int) p]) continue;
            for (long m = p * p; m < n; m += p)   // smaller multiples have a smaller prime factor
                composite[(int) m] = true;
        }
        int count = 0;
        for (int k = 2; k < n; k++) if (!composite[k]) count++;
        return count;
    }

    public static void main(String[] args) {
        System.out.println(countPrimes(10) + " " + countPrimes(100) + " " + countPrimes(1000000) + " "
            + countPrimes(5000000));
    }
}

// 4 25 78498 348513
function countPrimes(n) {              // how many primes are below n
    const composite = new Uint8Array(Math.max(n, 2));
    for (let p = 2; p * p < n; p++) {  // a prime with p * p >= n crosses nothing out
        if (composite[p]) continue;
        for (let m = p * p; m < n; m += p) composite[m] = 1;   // smaller multiples have a smaller prime factor
    }
    let count = 0;
    for (let k = 2; k < n; k++) if (!composite[k]) count++;
    return count;
}

console.log(countPrimes(10), countPrimes(100), countPrimes(1000000), countPrimes(5000000));

// 4 25 78498 348513

There are 4 primes below 10, 25 below 100, 78,498 below a million and 348,513 below five million, the largest n that Count Primes allows. Each prime p crosses out about n / p numbers, and the sum over the primes is O(n log log n), close to linear. The price is memory: one cell per number.

Against trial division of every number, timed with the two functions above:

#include <bits/stdc++.h>
using namespace std;

bool isPrime(int n) {
    if (n < 2) return false;               // 0 and 1 aren't prime
    for (int i = 2; i <= n / i; i++)       // i <= n / i: the same as i * i <= n, and it can't overflow
        if (n % i == 0) return false;
    return true;
}

int countPrimes(int n) {                  // how many primes are below n
    vector<bool> composite(max(n, 2), false);
    for (long long p = 2; p * p < n; p++) {   // a prime with p * p >= n crosses nothing out
        if (composite[p]) continue;
        for (long long m = p * p; m < n; m += p)   // smaller multiples have a smaller prime factor
            composite[m] = true;
    }
    int count = 0;
    for (int k = 2; k < n; k++) count += !composite[k];
    return count;
}

int main() {
    int n = 10000000;
    auto t = chrono::steady_clock::now();
    int trial = 0;
    for (int k = 2; k < n; k++) trial += isPrime(k);
    double a = chrono::duration<double>(chrono::steady_clock::now() - t).count();
    t = chrono::steady_clock::now();
    int sieve = countPrimes(n);
    double b = chrono::duration<double>(chrono::steady_clock::now() - t).count();
    printf("below %d: trial division %d primes in %.3fs, the sieve %d in %.3fs\n", n, trial, a, sieve, b);
}

// below 10000000: trial division 664579 primes in 5.175s, the sieve 664579 in 0.038s
def is_prime(n):
    if n < 2:
        return False                  # 0 and 1 aren't prime
    i = 2
    while i <= n // i:                # i <= n / i: the same as i * i <= n, and it can't overflow
        if n % i == 0:
            return False
        i += 1
    return True



def count_primes(n):                  # how many primes are below n
    composite = [False] * max(n, 2)
    p = 2
    while p * p < n:                  # a prime with p * p >= n crosses nothing out
        if not composite[p]:
            for m in range(p * p, n, p):   # smaller multiples have a smaller prime factor
                composite[m] = True
        p += 1
    return sum(1 for k in range(2, n) if not composite[k])



import time

n = 1000000
start = time.perf_counter()
trial = sum(1 for k in range(2, n) if is_prime(k))
a = time.perf_counter() - start
start = time.perf_counter()
sieve = count_primes(n)
b = time.perf_counter() - start
print(f"below {n}: trial division {trial} primes in {a:.3f}s, the sieve {sieve} in {b:.3f}s")

# below 1000000: trial division 78498 primes in 8.203s, the sieve 78498 in 0.182s
public class Main {
    static boolean isPrime(int n) {
        if (n < 2) return false;           // 0 and 1 aren't prime
        for (int i = 2; i <= n / i; i++)   // i <= n / i: the same as i * i <= n, and it can't overflow
            if (n % i == 0) return false;
        return true;
    }

    static int countPrimes(int n) {       // how many primes are below n
        boolean[] composite = new boolean[Math.max(n, 2)];
        for (long p = 2; p * p < n; p++) {   // a prime with p * p >= n crosses nothing out
            if (composite[(int) p]) continue;
            for (long m = p * p; m < n; m += p)   // smaller multiples have a smaller prime factor
                composite[(int) m] = true;
        }
        int count = 0;
        for (int k = 2; k < n; k++) if (!composite[k]) count++;
        return count;
    }

    public static void main(String[] args) {
        int n = 10000000;
        long t = System.nanoTime();
        int trial = 0;
        for (int k = 2; k < n; k++) if (isPrime(k)) trial++;
        double a = (System.nanoTime() - t) / 1e9;
        t = System.nanoTime();
        int sieve = countPrimes(n);
        double b = (System.nanoTime() - t) / 1e9;
        System.out.printf("below %d: trial division %d primes in %.3fs, the sieve %d in %.3fs%n", n, trial, a, sieve, b);
    }
}

// below 10000000: trial division 664579 primes in 10.731s, the sieve 664579 in 0.076s
function isPrime(n) {
    if (n < 2) return false;               // 0 and 1 aren't prime
    for (let i = 2; i <= Math.floor(n / i); i++)   // i <= n / i: the same as i * i <= n
        if (n % i === 0) return false;
    return true;
}

function countPrimes(n) {              // how many primes are below n
    const composite = new Uint8Array(Math.max(n, 2));
    for (let p = 2; p * p < n; p++) {  // a prime with p * p >= n crosses nothing out
        if (composite[p]) continue;
        for (let m = p * p; m < n; m += p) composite[m] = 1;   // smaller multiples have a smaller prime factor
    }
    let count = 0;
    for (let k = 2; k < n; k++) if (!composite[k]) count++;
    return count;
}

const n = 10000000;
let t = Date.now();
let trial = 0;
for (let k = 2; k < n; k++) if (isPrime(k)) trial++;
const a = (Date.now() - t) / 1000;
t = Date.now();
const sieve = countPrimes(n);
const b = (Date.now() - t) / 1000;
console.log(`below ${n}: trial division ${trial} primes in ${a.toFixed(3)}s, the sieve ${sieve} in ${b.toFixed(3)}s`);

// below 10000000: trial division 664579 primes in 10.918s, the sieve 664579 in 0.087s

Below ten million, C++ took 5.17 s by trial division and 0.038 s with the sieve; Java 10.73 s and 0.076 s; JavaScript 10.92 s and 0.087 s. Python stopped at a million, where trial division took 8.20 s and the sieve 0.182 s. Each found the same primes.

Cross Out the Multiples. Run the sieve on 2 to 100 yourself: pick each next prime, decide where its crossing starts, and decide when to stop.

Cross Out the Multiples: pick each prime, start at its square, stop at √100

Factorizing

Every n ≥ 2 is a product of primes in exactly one way. To find them, try d from 2 up: divide out every copy of d before moving on, so a d that still divides n is always prime. Stop at √n of what's left; if anything bigger than 1 remains, it's one last prime, larger than the root:

#include <bits/stdc++.h>
using namespace std;

string factorize(long long n) {           // n >= 2 as primes with their powers
    string out;
    for (long long d = 2; d <= n / d; d++) {
        int power = 0;
        while (n % d == 0) {               // divide out every copy of d
            n /= d;
            power++;
        }
        if (power) out += (out.empty() ? "" : " * ") + to_string(d) + (power > 1 ? "^" + to_string(power) : "");
    }
    if (n > 1) out += (out.empty() ? "" : " * ") + to_string(n);   // what's left is a prime above the root
    return out;
}

int main() {
    for (long long n : {360LL, 97LL, 2147483646LL})
        printf("%lld = %s\n", n, factorize(n).c_str());
}

// 360 = 2^3 * 3^2 * 5
// 97 = 97
// 2147483646 = 2 * 3^2 * 7 * 11 * 31 * 151 * 331
def factorize(n):                     # n >= 2 as primes with their powers
    parts = []
    d = 2
    while d <= n // d:
        power = 0
        while n % d == 0:             # divide out every copy of d
            n //= d
            power += 1
        if power:
            parts.append(f"{d}^{power}" if power > 1 else str(d))
        d += 1
    if n > 1:
        parts.append(str(n))          # what's left is a prime above the root
    return " * ".join(parts)


for n in (360, 97, 2147483646):
    print(f"{n} = {factorize(n)}")

# 360 = 2^3 * 3^2 * 5
# 97 = 97
# 2147483646 = 2 * 3^2 * 7 * 11 * 31 * 151 * 331
public class Main {
    static String factorize(long n) {  // n >= 2 as primes with their powers
        StringBuilder out = new StringBuilder();
        for (long d = 2; d <= n / d; d++) {
            int power = 0;
            while (n % d == 0) {           // divide out every copy of d
                n /= d;
                power++;
            }
            if (power > 0) out.append(out.length() > 0 ? " * " : "").append(d).append(power > 1 ? "^" + power : "");
        }
        if (n > 1) out.append(out.length() > 0 ? " * " : "").append(n);   // what's left is a prime above the root
        return out.toString();
    }

    public static void main(String[] args) {
        for (long n : new long[]{360, 97, 2147483646L})
            System.out.println(n + " = " + factorize(n));
    }
}

// 360 = 2^3 * 3^2 * 5
// 97 = 97
// 2147483646 = 2 * 3^2 * 7 * 11 * 31 * 151 * 331
function factorize(n) {                // n >= 2 as primes with their powers
    const parts = [];
    for (let d = 2; d <= Math.floor(n / d); d++) {
        let power = 0;
        while (n % d === 0) {              // divide out every copy of d
            n /= d;
            power++;
        }
        if (power) parts.push(power > 1 ? `${d}^${power}` : `${d}`);
    }
    if (n > 1) parts.push(`${n}`);         // what's left is a prime above the root
    return parts.join(" * ");
}

for (const n of [360, 97, 2147483646]) console.log(`${n} = ${factorize(n)}`);

// 360 = 2^3 * 3^2 * 5
// 97 = 97
// 2147483646 = 2 * 3^2 * 7 * 11 * 31 * 151 * 331

360 = 2³ × 3² × 5. 2,147,483,646, one less than the prime above, factors as 2 × 3² × 7 × 11 × 31 × 151 × 331. O(√n) per number.

For many numbers up to a limit, sieve once more, keeping each number's smallest prime factor. Then factorizing x is dividing by spf[x] until 1 is left, one division per prime factor, at most log₂ x of them:

#include <bits/stdc++.h>
using namespace std;

const int N = 1000000;
vector<int> spf(N + 1, 0);                 // spf[x]: the smallest prime that divides x

void buildSpf() {
    for (long long p = 2; p <= N; p++) {
        if (spf[p] != 0) continue;         // already has a smaller prime factor
        for (long long m = p; m <= N; m += p)
            if (spf[m] == 0) spf[m] = p;
    }
}

string factorize(int x) {                  // x <= N: one division per prime factor
    string out;
    while (x > 1) {
        int p = spf[x];
        out += (out.empty() ? "" : " * ") + to_string(p);
        x /= p;
    }
    return out;
}

int main() {
    buildSpf();
    for (int x : {360, 997, 720720}) printf("%d = %s\n", x, factorize(x).c_str());
}

// 360 = 2 * 2 * 2 * 3 * 3 * 5
// 997 = 997
// 720720 = 2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13
N = 1000000
spf = [0] * (N + 1)                   # spf[x]: the smallest prime that divides x
for p in range(2, N + 1):
    if spf[p] == 0:                   # nothing smaller divides p: it's prime
        for m in range(p, N + 1, p):
            if spf[m] == 0:
                spf[m] = p


def factorize(x):                     # x <= N: one division per prime factor
    parts = []
    while x > 1:
        parts.append(spf[x])
        x //= spf[x]
    return " * ".join(map(str, parts))


for x in (360, 997, 720720):
    print(f"{x} = {factorize(x)}")

# 360 = 2 * 2 * 2 * 3 * 3 * 5
# 997 = 997
# 720720 = 2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13
public class Main {
    static final int N = 1000000;
    static int[] spf = new int[N + 1];     // spf[x]: the smallest prime that divides x

    static String factorize(int x) {       // x <= N: one division per prime factor
        StringBuilder out = new StringBuilder();
        while (x > 1) {
            int p = spf[x];
            out.append(out.length() > 0 ? " * " : "").append(p);
            x /= p;
        }
        return out.toString();
    }

    public static void main(String[] args) {
        for (int p = 2; p <= N; p++) {
            if (spf[p] != 0) continue;     // already has a smaller prime factor
            for (int m = p; m <= N; m += p)
                if (spf[m] == 0) spf[m] = p;
        }
        for (int x : new int[]{360, 997, 720720}) System.out.println(x + " = " + factorize(x));
    }
}

// 360 = 2 * 2 * 2 * 3 * 3 * 5
// 997 = 997
// 720720 = 2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13
const N = 1000000;
const spf = new Int32Array(N + 1);     // spf[x]: the smallest prime that divides x
for (let p = 2; p <= N; p++) {
    if (spf[p] !== 0) continue;        // already has a smaller prime factor
    for (let m = p; m <= N; m += p) if (spf[m] === 0) spf[m] = p;
}

function factorize(x) {                // x <= N: one division per prime factor
    const parts = [];
    while (x > 1) {
        parts.push(spf[x]);
        x /= spf[x];
    }
    return parts.join(" * ");
}

for (const x of [360, 997, 720720]) console.log(`${x} = ${factorize(x)}`);

// 360 = 2 * 2 * 2 * 3 * 3 * 5
// 997 = 997
// 720720 = 2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13

720,720 came apart in 10 divisions, one per prime factor, and no x takes more than log₂ x of them; trial division needs about √x tries when x is prime. The table takes O(n log log n) to build and a cell per number, so it pays off for many numbers up to one limit; Sieve-Based Prime Factorization is this.

Spotting it

The clues: "divisors" or "factors", "prime", "coprime", "greatest common divisor" or "least common multiple", a fraction to reduce, a pattern that divides two strings, or many numbers to test or factor up to one limit (the sieve). Near misses:

  • Power of Two asks whether one prime, 2, is the only factor, and a single bit operation answers it (Bit Manipulation).
  • Pow(x, n) and Count Good Numbers multiply, not divide: fast power (Modular Arithmetic, Fast Power and Counting).

Mistakes that cost an interview

i * i <= n in 32 bits. The first test said 2,147,483,647 isn't prime, in 10 seconds in Java and at once in C++. Write i <= n / i, or square in 64 bits.

Counting a square's root twice. 36's divisors listed with 6 twice makes ten, not nine. Keep n / d only when it's a different number.

Calling 1 prime. It has one divisor, not two. Start the test with n < 2.

Multiplying first in an LCM. In 32 bits:

#include <bits/stdc++.h>
using namespace std;

int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

int main() {
    int a = 60000, b = 90000;
    // a * b wraps first (multiplied as unsigned: an int overflow would be undefined behaviour)
    printf("a * b / gcd: %d\n", (int) ((unsigned) a * b) / gcd(a, b));
    printf("a / gcd * b: %d\n", a / gcd(a, b) * b);
}

// a * b / gcd: 36834
// a / gcd * b: 180000
public class Main {
    static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

    public static void main(String[] args) {
        int a = 60000, b = 90000;
        System.out.println("a * b / gcd: " + a * b / gcd(a, b));   // a * b wraps first
        System.out.println("a / gcd * b: " + a / gcd(a, b) * b);
    }
}

// a * b / gcd: 36834
// a / gcd * b: 180000

5,400,000,000 wrapped around 2³² before the division, and 36,834 came out. Divide by the gcd first.

Crossing out from p * p for every prime, in an int. Run for every prime below n, the start wraps negative once p passes 46,340. Count Primes at five million:

public class Main {
    static int countPrimes(int n) {
        boolean[] composite = new boolean[Math.max(n, 2)];
        int count = 0;
        for (int p = 2; p < n; p++) {
            if (composite[p]) continue;
            count++;
            for (int m = p * p; m < n; m += p) composite[m] = true;   // p * p wraps once p passes 46,340
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(countPrimes(5000000));
    }
}

It threw ArrayIndexOutOfBoundsException at index −2,146,737,495, in an array of length 5,000,000. Stop the outer loop once p × p reaches n, which also skips work, or compute the start in 64 bits.

Check yourself

Quiz

1 / 6

Multiple choice

This counts the d it tries for n = 1,000,000. What does it print?
int count = 0;
for (int d = 1; d <= n / d; d++)
    count++;

Practice

In this section, easy to hard:

  • Finding All Divisors and All divisors of a Number: pairs up to √n, the root once.
  • Greatest Common Divisor of 2 numbers: Euclid.
  • Greatest Common Divisor of Strings: s + t against t + s, then gcd of the lengths.
  • Primality Check: trial division with a bound that can't overflow.
  • Count Primes: the sieve, from p × p.
  • Prime Factorization: divide out each d up to √n.
  • Sieve-Based Prime Factorization: the smallest prime factor table.

Next: Modular Arithmetic, Fast Power and Counting, where numbers get too big to hold and the answer is wanted mod 10⁹ + 7.