Prime Factorization
Decompose n into prime powers by trial division up to sqrt(n), or in O(log n) per query using a precomputed smallest-prime-factor table.
Overview
Every integer n ≥ 2 is a unique product of primes: 360 = 2³ · 3² · 5. Trial division finds it by dividing out each candidate d = 2, 3, 4, … while d² ≤ n; whatever remains above 1 at the end is a prime. Cost O(√n), fine for a single n ≤ 10^12.
With a smallest-prime-factor sieve (see Sieve of Eratosthenes) each factorization is O(log n): repeatedly divide n by spf[n]. This is the tool when thousands of numbers up to 10^7 must be factored.
The factorization unlocks divisor counting (Π (e_i + 1)), divisor sums (Π (p^(e+1) - 1)/(p - 1)), Euler's totient (n · Π (1 - 1/p)), and GCD (Euclidean Algorithm)/LCM (Least Common Multiple) via per-prime min/max exponents.
Intuition
A mental model before the formal terms.
Peeling an onion: keep pulling off the smallest layer that comes away cleanly. Pull 2 off 360 as many times as it divides (three times, leaving 45), then 3 (twice, leaving 5). Once the remaining core is smaller than the square of the next layer size, the core itself must be a single prime — no two factors ≥ d could multiply to something below d².
How it works
- Trial division: for
dfrom 2 whiled·d ≤ n: whilen mod d == 0, recorddand setn = n / d. After the loop, ifn > 1, recordn. - Speedup: handle
d = 2separately, then only test oddd(halves the work); or test only primes from a small sieve up to√n. - SPF: while
n > 1:p = spf[n]; count how many timespdividesn, dividing each time; record(p, count). - Divisor enumeration: from the factor list, generate all products
Π p_i^(k_i)with0 ≤ k_i ≤ e_iby nested loops or recursion.
Why it works
Because factors are removed in increasing order, when d divides n no smaller number does, so d is prime — composites have already been stripped via their own prime factors.
If n > 1 after the loop, all its prime factors are > √(original remaining n). Two such factors would multiply to more than n, so exactly one remains, and it is prime.
SPF correctness follows from the sieve's guarantee that spf[x] is the smallest prime dividing x; dividing it out repeatedly enumerates the factors in increasing order. Each division at least halves n, hence O(log n) steps.
Recognition
How to tell a problem wants this.
- "Number of divisors", "sum of divisors", "distinct prime factors", "is
na perfect power". - Problems on
gcd/lcmof many numbers, or "count elements sharing a prime factor" (union-find over primes). - Constraints with
n ≤ 10^12and one number (trial division) versus10^5numbers≤ 10^6(SPF).
Interactive visualization
Play, step, change the input. ← → and space work too.
Showing the closely related Sieve of Eratosthenes visualization.
1isPrime = [true] * (n + 1)2for p in 2 .. floor(sqrt(n)):3 if isPrime[p]:4 for m in p*p, p*p+p, .. n:5 isPrime[m] = false6primes = [p for p in 2..n if isPrime[p]]Pseudocode
1factor(n):2 factors = []3 d = 24 while d * d <= n:5 while n mod d == 0: factors.append(d); n = n / d6 d += 17 if n > 1: factors.append(n)8 return factors9factor_spf(n): while n > 1: p = spf[n]; record p; n = n / pImplementations
1import math2 3 4def factorize(n: int) -> list[tuple[int, int]]:5 """Trial division up to sqrt(n): every composite has a factor at or below6 its square root, so the loop stops there and whatever remains is prime."""7 out: list[tuple[int, int]] = []8 91 · Peel off 2 first so the loop can then step by 210 twos = 011 while n % 2 == 0:12 n //= 213 twos += 114 if twos:15 out.append((2, twos))16 172 · Only odd divisors remain, and only up to sqrt(n)18 d = 319 while d * d <= n:20 count = 021 while n % d == 0:22 n //= d23 count += 124 if count:25 out.append((d, count))26 d += 227 283 · Anything left above 1 is a prime larger than sqrt(original)29 if n > 1:30 out.append((n, 1))31 return out32 33 344 · With a smallest-prime-factor table, factorisation is O(log n) divisions35def factorize_with_spf(n: int, spf: list[int]) -> list[tuple[int, int]]:36 out: list[tuple[int, int]] = []37 while n > 1:38 p = spf[n]39 count = 040 while n % p == 0:41 n //= p42 count += 143 out.append((p, count))44 return out45 46 475 · The divisor count follows straight from the exponents48def divisor_count(factors: list[tuple[int, int]]) -> int:49 total = 150 for _, e in factors:51 total *= e + 152 return totaln //= 2is integer division; using/=would produce a float and break every subsequent%test.- Python integers are unbounded, so this factorises arbitrarily large numbers correctly — slowly, but never wrongly.
while d * d <= nwith a manuald += 2replaces the C-stylefor, since Pythonrangecannot express a bound that shrinks during iteration.if twos:andif count:use integer truthiness, which is idiomatic Python for "is non-zero".for _, e in factorsunpacks and discards the prime, sincedivisor_countonly needs the exponents.
Because integers are unbounded, the cost of each division grows with the size of n — the O(sqrt(n)) count is in operations, not constant-time steps.
//is floor division and returns anint;/always returns afloatand would silently break the algorithm past 2^53.sympy.factorint(n)returns a{prime: exponent}dict and uses Pollard rho plus elliptic-curve methods for large inputs.math.isqrt(n)would give an exact loop bound, though comparingd * d <= navoids recomputing it asnshrinks.- Arbitrary-precision integers mean there is no overflow to guard against, at the cost of each operation scaling with the number of digits.
- Using
/=instead of//=, which turnsninto a float and makes every later%test unreliable. - Writing
for d in range(3, int(n ** 0.5) + 1, 2), which fixes the bound at the *original*nand does far more work than necessary. - Forgetting the trailing
if n > 1and dropping the largest prime factor.
- Integer division is the dividing line: Python needs
//(and/silently returns a float), C++/on integers already truncates, and JS/TS have only float division, exact just to 2^53. - Range: Python factorises arbitrarily large integers correctly; C++ tops out at 64 bits without GMP; JS/TS need
BigIntpast 2^53, which cannot mix withnumber. - Library escape hatches:
sympy.factorintin Python and GMP in C++ handle inputs where trial division is hopeless; JS/TS have no standard equivalent. - The loop shape differs because Python
rangecannot express a bound that shrinks mid-loop, so thewhile d * d <= nform is mandatory there and merely idiomatic elsewhere.
Complexity
Trial division is O(sqrt n) per number (worst when n is prime). With an SPF table of size O(N): O(N log log N) preprocessing, then O(log n) per query.
Compare growth rates in the Complexity Explorer →When to use — and when not to
- One or a few numbers up to
~10^12— trial division (optionally only over primes ≤10^6). - Many numbers bounded by
~10^7— precompute SPF once. - Deriving divisor counts, divisor sums, totients, or per-prime exponents for gcd/lcm arguments.
- Numbers around
10^18or larger:√n = 10^9trial divisions is too slow; use Pollard rho with Miller–Rabin. - When only primality is needed for one number — Miller–Rabin answers without factoring.
- When the SPF table would not fit (
n > 10^8) — fall back to trial division with a prime list.
Alternatives
Common mistakes
- Forgetting the final
if n > 1— loses the largest prime factor (e.g. 14 → only2). - Looping
d ≤ √nwith a fixed√ncomputed before dividing: still correct, but slower; recompute against the shrinkingnor used * d <= n. d * doverflowing 32-bit ints whennis near2^31; use 64-bitd.- SPF lookup with
nlarger than the table. - Generating divisors with nested loops that recompute
p^kbyMath.powon floats in JavaScript — accumulate integer powers instead.
Interview patterns
- Largest Component Size by Common Factor: union every number with each of its prime factors.
- Count divisors of every number up to
n(SPF + multiplicativity) or of a single largen(trial division). - Check if
nis a perfect power or "ugly number" (only factors 2, 3, 5) by dividing out allowed primes. - Number of trailing zeros of
n!= exponent of 5 inn!, via Legendre's formulaΣ n / 5^k.