GCD, LCM, and Modular Arithmetic
The previous page gave you the tools to decompose numbers into primes. This page shows you what to do with that decomposition: computing GCDs and LCMs, working under a modulus, and Euler's Totient Function — the count of numbers coprime to \(M\).
These aren't abstract math concepts. Modular inverse shows up every time a problem says "output the answer modulo \(10^9 + 7\)." GCD and LCM appear in fraction arithmetic, cycle detection, and scheduling problems. Euler's Totient connects to Fermat's theorem and is the foundation for RSA cryptography.
GCD and LCM as Set Operations
Here's the cleanest way to think about GCD and LCM: they're set operations on prime factorizations.
Write two numbers using their prime factors:
Now:
GCD = intersection. For each prime, take the minimum exponent.
LCM = union. For each prime, take the maximum exponent.
This gives an elegant identity:
Because \(\min(a, b) + \max(a, b) = a + b\) for every exponent. The product of GCD and LCM equals the product of the original numbers. Useful for computing LCM when you already have the GCD:
The Euclidean Algorithm
In practice, you don't factorize numbers to compute GCD. You use the Euclidean algorithm:
Repeat until \(b = 0\). The remaining \(a\) is the GCD.
This runs in \(O(\log(\min(a, b)))\) — much faster than factorizing. Most languages have a built-in: __gcd(a, b) in C++, math.gcd(a, b) in Python.
The set interpretation is for understanding. The Euclidean algorithm is for computing.
Modular Arithmetic
When a problem says "output the answer modulo \(10^9 + 7\)," it's asking you to work in modular arithmetic. The key properties:
Addition and multiplication "pass through" the modulus. You can take the mod at any point during computation without affecting the result. This prevents overflow when working with large numbers.
Subtraction works too, but watch for negatives:
The + m ensures a non-negative result.
Division is where things get tricky. You can't just divide under a modulus. Instead, you multiply by the modular inverse — and that's where Fermat's theorem comes in.
Modular Inverse and Fermat's Little Theorem
The Problem
You need to compute \(\frac{a}{b} \bmod p\). Division doesn't work directly with modular arithmetic. You need \(b^{-1} \bmod p\) — a number \(x\) such that \(b \times x \equiv 1 \pmod{p}\).
Then \(\frac{a}{b} \bmod p = a \times b^{-1} \bmod p\).
Fermat's Little Theorem
If \(p\) is prime and \(\gcd(a, p) = 1\) (i.e., \(p\) doesn't divide \(a\)), then:
This means:
So \(a^{-1} \equiv a^{p-2} \pmod{p}\).
The modular inverse of \(a\) is just \(a^{p-2} \bmod p\). Compute it with fast exponentiation.
Fast Exponentiation
Computing \(a^{p-2}\) naively would take \(O(p)\) multiplications — way too slow for \(p = 10^9 + 7\). But you can do it in \(O(\log p)\) using repeated squaring:
long long power(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1)
result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
// Modular inverse (p must be prime, gcd(a, p) = 1)
long long modInverse(long long a, long long p) {
return power(a, p - 2, p);
}
Example: compute \(3^{-1} \bmod 7\).
\(3^{7-2} = 3^5 = 243\). \(243 \bmod 7 = 243 - 34 \times 7 = 243 - 238 = 5\).
Check: \(3 \times 5 = 15\). \(15 \bmod 7 = 1\). ✓
When Does This Work?
Fermat's theorem requires \(p\) to be prime. The modulus \(10^9 + 7\) is prime — that's why competitive programming uses it. If the modulus isn't prime, you need the Extended Euclidean Algorithm instead (which works whenever \(\gcd(a, m) = 1\)).
Cyclicity of Powers
Here's a pattern worth knowing. Take a base \(a\) and a prime modulus \(p\), and compute \(a^1, a^2, a^3, \ldots\) modulo \(p\):
For \(a = 2, p = 7\):
| \(k\) | \(2^k \bmod 7\) |
|---|---|
| 1 | 2 |
| 2 | 4 |
| 3 | 1 |
| 4 | 2 |
| 5 | 4 |
| 6 | 1 |
The sequence repeats: \(2, 4, 1, 2, 4, 1, \ldots\) with period 3.
By Fermat's theorem, \(a^{p-1} \equiv 1 \pmod{p}\), so the cycle length always divides \(p - 1\). Here, \(p - 1 = 6\), and the cycle length 3 divides 6.
This cyclicity is useful for problems that ask "what is \(a^n \bmod p\) for astronomically large \(n\)?" — reduce \(n\) modulo the cycle length first.
Euler's Totient Function
What It Counts
\(\phi(M)\) is the count of integers from 1 to \(M\) that are coprime to \(M\) (i.e., \(\gcd(k, M) = 1\)).
Examples:
- \(\phi(1) = 1\). Just the number 1 itself.
- \(\phi(6) = 2\). The numbers 1 and 5 are coprime to 6. (2, 3, 4 share a factor with 6.)
- \(\phi(7) = 6\). Since 7 is prime, every number from 1 to 6 is coprime to it.
- \(\phi(p) = p - 1\) for any prime \(p\). (This is exactly Fermat's theorem — there are \(p-1\) numbers coprime to \(p\).)
The Formula
If \(M = p_1^{a_1} \times p_2^{a_2} \times \ldots \times p_k^{a_k}\), then:
For \(M = 12 = 2^2 \times 3\):
Check: the numbers coprime to 12 from 1 to 12 are 1, 5, 7, 11. Four of them. ✓
Why the Formula Works
The formula uses inclusion-exclusion, but the multiplicative form hides it nicely.
Start with \(M\) numbers (1 through \(M\)). Remove the multiples of each prime factor:
- Remove multiples of \(p_1\): subtract \(M/p_1\).
- Remove multiples of \(p_2\): subtract \(M/p_2\).
- But multiples of both \(p_1\) and \(p_2\) were removed twice — add back \(M/(p_1 p_2)\).
- Continue for all subsets of prime factors.
That's inclusion-exclusion. But notice what happens when you expand the product:
That's exactly inclusion-exclusion. The product form is just a compact way to write it. And the order of multiplication doesn't matter — the result is the same regardless of which prime factor you process first.
Computing phi(M) for a Single Number
Use the factorization approach: find all prime factors, apply the formula.
int eulerTotient(int n) {
int result = n;
for (int i = 2; (long long)i * i <= n; i++) {
if (n % i == 0) {
while (n % i == 0) n /= i;
result -= result / i; // multiply by (1 - 1/i)
}
}
if (n > 1) result -= result / n;
return result;
}
The key line is result -= result / i, which is equivalent to result = result * (1 - 1/i) using integer arithmetic. This works because at each step, result is guaranteed to be divisible by \(i\) — a consequence of the prime structure.
Complexity: \(O(\sqrt{M})\), since we iterate over potential factors up to \(\sqrt{M}\).
Computing phi for All Numbers Up to N: The Sieve
If you need \(\phi(k)\) for every \(k\) from 1 to \(N\), a sieve approach runs in \(O(N \log N)\):
vector<int> phi(N + 1);
iota(phi.begin(), phi.end(), 0); // phi[i] = i initially
for (int i = 2; i <= N; i++) {
if (phi[i] == i) { // i is prime (hasn't been modified)
for (int j = i; j <= N; j += i) {
phi[j] -= phi[j] / i; // multiply by (1 - 1/i)
}
}
}
How it works:
- Initialize \(\phi[i] = i\) for all \(i\).
- For each prime \(i\) (detected by \(\phi[i] == i\) — it hasn't been modified by any smaller prime), iterate through all multiples of \(i\) and apply the factor \((1 - 1/i)\).
- By the time you finish, every number has been visited by all its prime factors, and \(\phi[k]\) holds the correct value.
Let's trace for \(N = 10\):
| After prime | \(\phi[1..10]\) |
|---|---|
| Initial | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 |
| \(p = 2\) | 1, 1, 3, 2, 5, 3, 7, 4, 9, 5 |
| \(p = 3\) | 1, 1, 2, 2, 5, 2, 7, 4, 6, 5 |
| \(p = 5\) | 1, 1, 2, 2, 4, 2, 7, 4, 6, 4 |
| \(p = 7\) | 1, 1, 2, 2, 4, 2, 6, 4, 6, 4 |
Final: \(\phi = [-, 1, 1, 2, 2, 4, 2, 6, 4, 6, 4]\). All correct.
A Limitation: No Incremental Extension
What if you've computed \(\phi\) for all numbers up to \(N\) and now need \(\phi(N+1)\)? Can you extend the sieve by one entry?
No — not efficiently. The sieve works by iterating from primes to their multiples. Given a number, you can't efficiently find which primes generated it without factorizing it. So for individual numbers outside the sieved range, the \(O(\sqrt{M})\) factorization approach is the best you can do.
Euler's Theorem (Generalized Fermat)
Fermat's Little Theorem says \(a^{p-1} \equiv 1 \pmod{p}\) when \(p\) is prime. Euler's theorem generalizes this to any modulus:
When \(m\) is prime, \(\phi(m) = m - 1\), and this reduces to Fermat's theorem.
This means the modular inverse of \(a\) modulo \(m\) (not necessarily prime) is \(a^{\phi(m) - 1} \bmod m\) — as long as \(\gcd(a, m) = 1\).
Quick Reference
| Tool | What It Does | Complexity |
|---|---|---|
| Euclidean algorithm | \(\gcd(a, b)\) | \(O(\log \min(a, b))\) |
| LCM via GCD | \(\text{lcm}(a, b) = ab / \gcd(a,b)\) | \(O(\log \min(a, b))\) |
| Fast exponentiation | \(a^n \bmod m\) | \(O(\log n)\) |
| Modular inverse (prime \(p\)) | \(a^{-1} \equiv a^{p-2} \pmod{p}\) | \(O(\log p)\) |
| Euler's totient (single \(M\)) | \(\phi(M)\) | \(O(\sqrt{M})\) |
| Euler's totient sieve (1 to \(N\)) | \(\phi(k)\) for all \(k \le N\) | \(O(N \log N)\) |
| Primality test | Is \(n\) prime? | \(O(\sqrt{n})\) |
| Sieve of Eratosthenes | All primes up to \(N\) | \(O(N \log \log N)\) |
What's Next
This page and Primes and Factorization cover the core number theory toolkit. The session mentioned that topics like the Chinese Remainder Theorem, Extended Euclidean Algorithm, and advanced factorization methods (\(O(N^{1/4})\) for very large numbers) will be covered in a future session.