Prime Factorisation — 10^9 Calls Killed API
- Every integer has a unique prime factorization — Fundamental Theorem of Arithmetic.
- Trial division: O(√n) per number — sufficient for n up to ~10^12.
- From factorization: divisor count = Π(exponent+1), totient = n × Π(1-1/p).
- Prime factorization breaks numbers into unique primes: 360 = 2³ × 3² × 5
- Tau(n) = ∏(exponent+1) gives divisor count; sigma(n) = ∏(p^(a+1)-1)/(p-1) gives sum
- Trial division O(√n) per number — fine for n ≤ 10¹², deadly above
- SPF sieve drops per-number cost to O(log n) after O(n log log n) preprocessing
- Pollard's rho factors composite numbers up to 10¹⁸ in seconds — but has failure modes
Quick Factorisation Debug Cheat Sheet
Pollard's rho never returns
is_prime = all(n % d != 0 for d in range(2, int(n**0.5)+1))Add Miller-Rabin primality test for n > 10^12SPF sieve array too large for memory
mem_gb = (max_n * 4) / (1024**3)If >0.5 GB, use segmented sieve or reduce rangeTrial division loop is too slow for a single large number
Add early exit: if n == 1: breakAfter checking up to sqrt(original_n), leftover n is primeProduction Incident
Production Debug GuideDiagnose why your factorisation routine is taking too long or returning wrong results
RSA's security depends on one fact: factoring the product of two large primes is computationally hard. The best known classical algorithm (General Number Field Sieve) runs in sub-exponential but super-polynomial time. A 2048-bit RSA modulus would take longer than the age of the universe to factor on current hardware.
For small numbers — up to ~10^12 — trial division works fine. For competitive programming — up to ~10^6 per query — the SPF sieve turns factorization from O(√n) per query to O(log n). Knowing which regime you're in and choosing the right tool is the practical skill.
The real trap? Assuming one algorithm works for all sizes. Engineers waste hours because they don't detect when trial division becomes impractical or when Pollard's rho loops indefinitely on a prime input.
Trial Division
Check divisibility by all primes up to √n. O(√n) per number. This is the simplest and most intuitive algorithm. For a single n ≤ 10^12, trial division completes in microseconds. But scale it to 10^6 numbers and you're looking at days.
The key optimization: after checking 2, only check odd numbers. That halves the work. For even better performance, precompute a list of primes up to √max_n using a simple sieve, then only divide by those primes.
def prime_factors(n: int) -> dict[int, int]: """Returns {prime: exponent} factorization.""" factors = {} d = 2 while d * d <= n: while n % d == 0: factors[d] = factors.get(d, 0) + 1 n //= d d += 1 if n > 1: factors[n] = factors.get(n, 0) + 1 return factors print(prime_factors(360)) # {2:3, 3:2, 5:1} print(prime_factors(1001)) # {7:1, 11:1, 13:1} print(prime_factors(97)) # {97:1} — prime
{7: 1, 11: 1, 13: 1}
{97: 1}
Number of Divisors and Sum of Divisors
- Number of divisors: τ(n) = (a1+1)(a2+1)...(ak+1)
- Sum of divisors: σ(n) = Π (p^(a+1)
- 1) / (p
- 1)
- Euler's totient: φ(n) = n × Π (1
- 1/p) for each prime p dividing n
These formulas are direct consequences of the multiplicative property. For example, τ(360) = τ(2³)τ(3²)τ(5¹) = (3+1)(2+1)(1+1) = 4×3×2 = 24 divisors. The sum σ(360) = (2^(3+1)-1)/(2-1) × (3^(2+1)-1)/(3-1) × (5^(1+1)-1)/(5-1) = (16-1)/1 × (27-1)/2 × (25-1)/4 = 15 × 13 × 6 = 1170.
def num_divisors(n: int) -> int: factors = prime_factors(n) result = 1 for exp in factors.values(): result *= (exp + 1) return result def sum_divisors(n: int) -> int: factors = prime_factors(n) result = 1 for p, exp in factors.items(): result *= (p**(exp+1) - 1) // (p - 1) return result def euler_totient(n: int) -> int: factors = prime_factors(n) result = n for p in factors: result -= result // p return result print(num_divisors(360)) # 24 print(sum_divisors(360)) # 1170 print(euler_totient(36)) # 12
1170
12
Listing All Divisors
To list all divisors, iterate up to √n and collect pairs. Complexity O(√n) – but you can't beat that because output size is O(τ(n)) which is at most O(√n). For n=10^12, τ(n) is at most ~6720, so enumerating divisors is feasible.
The algorithm: for each divisor d ≤ √n, if n % d == 0, add d and n//d. Sort the result at the end.
def all_divisors(n: int) -> list[int]: divisors = [] for i in range(1, int(n**0.5) + 1): if n % i == 0: divisors.append(i) if i != n // i: divisors.append(n // i) return sorted(divisors) print(all_divisors(36)) # [1,2,3,4,6,9,12,18,36] print(len(all_divisors(360))) # 24
24
SPF Sieve for Batch Factorization
When factorizing many numbers up to n, precompute the Smallest Prime Factor (SPF) sieve. Each factorization then takes O(log n) instead of O(√n).
Construction: Create an array spf[0..n] filled with 0. For i from 2 to n, if spf[i]==0 (i is prime), set spf[i]=i and for j in range(i*i, n+1, i), if spf[j]==0, set spf[j]=i. Then to factor a number x, repeatedly divide by spf[x] and record the count.
This is the standard for competitive programming and any scenario where you need to factorise a batch of numbers up to ~10^7.
def spf_sieve(n: int) -> list[int]: spf = [0] * (n + 1) for i in range(2, n + 1): if spf[i] == 0: spf[i] = i if i * i <= n: for j in range(i * i, n + 1, i): if spf[j] == 0: spf[j] = i return spf def factorize_with_spf(x: int, spf: list[int]) -> dict[int, int]: factors = {} while x > 1: p = spf[x] factors[p] = factors.get(p, 0) + 1 x //= p return factors spf = spf_sieve(100) print(factorize_with_spf(72, spf)) # {2:3, 3:2}
Pollard's Rho Algorithm for Large Composite Numbers
When n exceeds ~10^12 and you need to factorise a single number, trial division becomes impractical. Pollard's rho is a probabilistic algorithm that finds a non-trivial factor in O(n^(1/4)) expected time.
It works by iterating a pseudo-random function f(x) = (x^2 + c) mod n and detecting cycles using Floyd's cycle detection. When a cycle is found, gcd(|x-y|, n) gives a factor.
It's not guaranteed – for prime n it will loop forever. Always pair it with a primality test (Miller-Rabin) before running rho.
import math def pollard_rho(n: int) -> int: if n % 2 == 0: return 2 c = 1 x = 2 y = 2 d = 1 while d == 1: x = (x * x + c) % n y = (y * y + c) % n y = (y * y + c) % n d = math.gcd(abs(x - y), n) if d == n: c += 1 x = 2 y = 2 d = 1 return d n = 1000000000000000003 # a large semiprime print(pollard_rho(n))
If not, a factor like: 1000000000000000003 // factor
- The function f(x) = x^2 + c mod n produces a sequence that must eventually repeat.
- Once it repeats, the difference between two pointers (tortoise and hare) often shares a factor with n.
- If the sequence repeats without hitting a factor, change c and restart.
- Expected number of steps before finding a factor is O(√p) where p is the smallest prime factor.
| Algorithm | Best For | Time Complexity | Space Complexity | Notes |
|---|---|---|---|---|
| Trial Division | n ≤ 10^12 (single query) | O(√n) | O(1) | Simple, no setup |
| SPF Sieve | Many queries up to 10^7 | O(log n) per query after O(n log log n) prep | O(n) | Memory heavy in Python |
| Pollard's Rho | Single n > 10^12 | O(n^(1/4)) expected | O(1) | Probabilistic, needs primality test |
| Quadratic Sieve | n ~ 10^50 | exp(O(√(log n log log n))) | subexponential | Not for beginners |
| General Number Field Sieve | n > 10^100 (RSA) | subexponential | massive | Only for cryptographers |
🎯 Key Takeaways
- Every integer has a unique prime factorization — Fundamental Theorem of Arithmetic.
- Trial division: O(√n) per number — sufficient for n up to ~10^12.
- From factorization: divisor count = Π(exponent+1), totient = n × Π(1-1/p).
- SPF sieve enables O(log n) factorization after O(n log log n) preprocessing.
- RSA security depends on the hardness of factorizing large semiprimes (products of two large primes).
- Pollard's rho: O(n^(1/4)) for large composites – always pair with a primality test.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QHow many divisors does 10^6 have? How do you compute this from its factorization?JuniorReveal
- QWhat is Euler's totient function and how is it computed from prime factorization?JuniorReveal
- QFor what values of n is Euler's totient equal to n-1?Mid-levelReveal
- QHow does the SPF sieve speed up batch factorization?Mid-levelReveal
- QExplain Pollard's rho algorithm and its failure modes.SeniorReveal
Frequently Asked Questions
What is a perfect number?
A perfect number equals the sum of its proper divisors (divisors excluding itself). 6 = 1+2+3, 28 = 1+2+4+7+14. Equivalently, σ(n) = 2n. All known perfect numbers are even and correspond to Mersenne primes.
How do I find all divisors of a number efficiently?
Iterate up to √n, collect divisor pairs. For each i where n % i == 0, add i and n//i (if different). Then sort. Complexity O(√n). The number of divisors τ(n) is usually small, so sorting is cheap.
What is the difference between divisor count and divisor sum?
Divisor count τ(n) = number of positive divisors. Divisor sum σ(n) = sum of all positive divisors. Both are multiplicative functions computed from the prime factorization. τ uses product of (exponent+1); σ uses product of (p^(a+1)-1)/(p-1).
When should I use Pollard's rho over trial division?
Use Pollard's rho when n > 10^12 and you only need to factorise a single number. For smaller numbers, trial division is simpler and faster. For batch factorization, use SPF sieve.
What is the time complexity of the SPF sieve?
Construction is O(n log log n). Each factorization after that is O(log n) because you follow the spf pointers. Total for k queries: O(n log log n + k log n).
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.