Definitions of primes, divisors, and multiples
A prime is called “prime” in English. If $p$ is prime, then $p^k$ is called a prime power.
For integers $a, b$, we say $a$ divides $b$ if there exists an integer $n$ such that $b = a n$. In this case $b$ is called a multiple of $a$.
A natural number $p \ge 2$ is a prime if its only divisors are $1$ and $p$.
Primality test
Start from the definition.
Test all divisors from $1$ to $N$. If exactly two numbers divide $N$, then $N$ is prime.
Equivalently, try dividing $N$ by each integer from $2$ to $N-1$. If any divides evenly, $N$ is composite; if none do, $N$ is prime.
| |
This c++ function checks whether a number is prime.
Since it starts from 2, you must handle the case when the input is 1.
The loop checks all numbers from $2$ to $N-1$, so the time complexity is $\bigo(N)$.
Optimizing the primality test
Let $d$ be the smallest divisor of a composite $N$ other than $1$.
Then $\frac{N}{d}$ is also a non-unit divisor of $N$, so $d \le \frac{N}{d}$.
Rearranging gives $d^2 \le N$, hence $d \le \sqrt{N}$.
So it suffices to test divisors only up to $\sqrt{N}$.
You could write the loop as (int i = 2; i <= sqrt(n); i++), but sqrt uses floating-point arithmetic and may introduce error. Prefer for (int i = 2; i*i <= n; i++).
| |
The time complexity is $\bigo(\sqrt{N})$.
Practice problems for primality testing
Finding divisors
A small tweak lets us list divisors.
Iterate $i$ from $1$ to $\sqrt{n}$ and push each divisor into a vector.
If a natural number $d$ divides $n$, then $n/d$ is also a divisor.
Be careful when $n$ is a perfect square $m^2$: $\sqrt{n} = m$ would be counted twice unless you check.
| |
This lists all divisors in $\bigo(\sqrt{N})$ time.
After collecting divisors we sort them. The STL sort runs in $\bigo(N\log N)$.
You can roughly estimate the upper bound on the number of divisors within the long long range as about $\sqrt[3]{N}$.
So even in the worst case the sorting cost is at most $\bigo(\sqrt[3]{N}\log \sqrt[3]{N})$.
Since for positive $N$ we have $\sqrt[3]{N}\log \sqrt[3]{N} < \sqrt{N}$, the overall complexity remains $\bigo(\sqrt{N})$.
Optimizing divisor listing
We can avoid sorting.
Instead of v.push_back(d);, push $d$ into a temporary vector and then append that vector in reverse to get sorted divisors.
| |
Practice problems for divisors
Listing all primes in a range
Brute-force primality checks will time out for large ranges.
Use the Sieve of Eratosthenes.
For the algorithm and optimizations, see here. Code only below:
| |
Prime factorization
With the above, prime factorization is straightforward.factorize factors a given $N$ efficiently.
| |
Bonus algorithms
Number theory includes many challenging but interesting algorithms. Not strictly necessary, but worth noting.
Pollard’s Rho factorization
The above factorization is fine for int, but for long long it can TLE.
For problems like this with $1 \le N \le 10^{18}$, implement Pollard’s Rho with Miller–Rabin primality testing, factor $N$, then enumerate divisors via DFS.
I may cover these algorithms later.
Prime counting function
The prime counting function $\pi(n)$ gives the number of primes $\le n$.
It satisfies the approximation
$$\pi(n) \approx \frac{n}{\ln(n)}.$$
For example, an approximation for $\pi(10^6)$ is $72382$, while the exact value is $78498$.
This function lets you estimate time complexity in problems involving the count of primes.
