Fermat's Little Theorem
Key Points
- •Fermat's Little Theorem says that for a prime p and integer a not divisible by p, ≡ 1 (mod p).
- •A powerful corollary is that the modular inverse of a modulo p is mod p when p is prime and gcd(a, p) = 1.
- •This enables O(log p) modular inverses and fast division in modular arithmetic using binary exponentiation.
- •Exponents can be reduced modulo (p−1) when the base is coprime with p, which simplifies huge exponent computations.
- •FLT underlies fast computations of nCk mod p using factorials and inverse factorials.
- •Fermat’s primality test checks if ≡ 1 (mod p) for random a, but can be fooled by Carmichael numbers.
- •Always verify the modulus is prime and the base is not divisible by p before applying FLT directly.
- •In practice, combine FLT with safe modular multiplication and binary exponentiation to avoid overflow and achieve speed.
Prerequisites
- →Modular arithmetic basics — Understanding congruences, residues, and modulus operations is essential to apply FLT correctly.
- →Prime numbers — FLT specifically requires a prime modulus; recognizing primes and their properties is necessary.
- →Greatest common divisor (gcd) — You must ensure gcd(a, p) = 1 to use FLT for inverses and exponent reduction.
- →Binary exponentiation — Efficiently computing a^{e} mod p in O(log e) is the practical engine behind FLT-based algorithms.
- →Safe integer arithmetic in C++ — Avoiding overflow in modular multiplication often requires 128-bit intermediates or careful casting.
- →Combinatorics (binomial coefficients) — Common FLT applications include computing nCk mod p using factorials and inverse factorials.
- →Probabilistic algorithms — Interpreting Fermat's primality test results requires understanding 'probable prime' and error risk.
Detailed Explanation
Tap terms for definitions01Overview
Fermat's Little Theorem (FLT) is a cornerstone of modular arithmetic that makes computations with primes fast and reliable. It states that if p is a prime and a is any integer not divisible by p, then a^{p-1} ≡ 1 (mod p). From this, we immediately get that the modular inverse of a modulo p is a^{p-2} mod p. These facts turn tough problems like modular division and huge exponentiation into efficient algorithms that run in logarithmic time using binary exponentiation. In competitive programming and cryptography, FLT is used to compute modular inverses, reduce exponents, build factorial tables for binomial coefficients modulo a prime, and form the basis of simple (though imperfect) primality tests. While its statement is simple, applying it correctly requires checking conditions: the modulus must be prime, and the base must be coprime with p. FLT also connects to broader number theory: Euler’s theorem generalizes it to composite moduli using the totient function, and the Carmichael function refines exponent cycles. For programmers, mastering FLT means writing a robust fast power function, handling edge cases (like a ≡ 0 (mod p)), and knowing when exponent reduction by p−1 is valid.
02Intuition & Analogies
Imagine clock arithmetic with p hours on the clock, where p is prime. If you take steps of size a around this clock, and a shares no common divisor with p, then repeatedly stepping wraps you through all positions exactly once before returning to the start. After p−1 nonzero steps you land back at 1, which is the intuition behind a^{p-1} ≡ 1 (mod p). In other words, multiplying by a permutes the nonzero residues modulo a prime, so the product of all residues stays the same even after multiplying by a, forcing a^{p-1} to be 1 modulo p. For modular inverses, think of undoing a multiplication: if a^{p-1} ≡ 1, then multiplying both sides by a^{-1} shows a^{p-2} acts as the “undo” of a mod p. This is like saying, on the p-hour clock, walking a steps forward p−2 times is the same as taking one step backward. For huge exponents, FLT says the cycle length of powers of a divides p−1, so you only need to know where you are within the cycle. That’s why we can reduce exponents modulo p−1 when the base is not a multiple of p. When computing combinations like nCk mod p, inverses let you divide by k! and (n−k)! even though division isn’t naturally defined in modular arithmetic—FLT gives us a safe and fast stand-in for division. Finally, for primality testing, if numbers fail the FLT property for some base, they’re composite; if they pass many random checks, they’re probably prime, though certain trap numbers (Carmichael numbers) can still fool this simple test.
03Formal Definition
04When to Use
Use FLT whenever your modulus is a prime and you need a modular inverse or to simplify a large power. Typical applications include: computing a/b mod p by turning it into a \cdot b^{p-2} mod p; evaluating polynomial expressions modulo a prime; precomputing factorials and inverse factorials to answer many nCk mod p queries quickly; and reducing massive exponents b in expressions like a^{b} mod p by replacing b with b \bmod (p−1) when \gcd(a, p) = 1. FLT also provides a quick, probabilistic filter for primality: pick random bases a and check whether a^{p-1} \equiv 1 \pmod{p}; failure implies composite. Use it as a fast sieve to eliminate obvious composites before running a stronger test (like Miller–Rabin). In problems with constraints around 10^9+7 or 998244353 (both primes), FLT is the standard tool to support modular division and combinations. Avoid FLT if the modulus is not prime (unless you switch to Euler’s theorem or use extended GCD for inverses modulo non-prime when the inverse exists).
⚠️Common Mistakes
Common pitfalls include: (1) Using FLT when the modulus is not prime. The identity a^{p-2} \equiv a^{-1} \pmod{p} only holds for prime p; for composite moduli, you must use extended Euclid to find inverses (and they may not exist if \gcd(a, n) \ne 1). (2) Forgetting the requirement p \nmid a. If a \equiv 0 \pmod{p}, then a^{p-1} \equiv 0, not 1; attempting to compute a^{p-2} as an inverse will be invalid and may lead to division-by-zero analogs. (3) Overflow in modular multiplication. In C++, multiplying two 64-bit values before taking mod can overflow signed long long; use 128-bit intermediates (__int128) or ensure the modulus is small enough. (4) Misusing exponent reduction when a is divisible by p. The reduction b \bmod (p−1) relies on \gcd(a, p) = 1; if a \equiv 0 (mod p) and b > 0, the result is 0 regardless of b. (5) Treating the Fermat primality test as definitive. Carmichael numbers pass a^{n-1} \equiv 1 (mod n) for many bases; rely on stronger tests for certainty. (6) Neglecting normalization of negative residues: in C++, (-x) % p is negative; always convert with (x % p + p) % p. (7) Using pow from <cmath> for modular powers, which is floating-point and imprecise; always implement integer binary exponentiation.
Key Formulas
Fermat's Little Theorem
Explanation: For a prime p and integer a not divisible by p, raising a to the power p−1 yields 1 modulo p. This underpins modular inverses and exponent reduction.
Fermat Inverse
Explanation: When p is prime and gcd(a, p) = 1, the modular inverse of a modulo p can be computed by exponentiating a to p−2. This enables O(log p) inverses via fast power.
Exponent Reduction Mod p−1
Explanation: If gcd(a, p) = 1, then powers of a repeat with period dividing p−1, so you can reduce the exponent modulo p−1 before exponentiating. This is crucial for handling huge exponents.
nCk via Fermat Inverses
Explanation: For prime p and 0 k n, compute combinations by multiplying factorials with modular inverses computed as mod p. With precomputation, queries become O(1).
Binary Exponentiation Time
Explanation: Fast power doubles the exponent bits each step, so the number of multiplications grows with the number of bits of e. This is why exponentiation and Fermat inverses are efficient.
Euler's Theorem
Explanation: Generalizes FLT to any modulus n using Euler’s totient function (n). For prime p, (p)=p−1, recovering FLT.
Modular Division under Prime Modulus
Explanation: Division modulo a prime converts to multiplication by the inverse, computed via Fermat’s exponent. This allows safe, fast modular division.
Complexity Analysis
Code Examples
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 using int64 = long long; 5 using i128 = __int128_t; // for safe multiplication when needed 6 7 // Fast modular exponentiation: computes a^e mod p in O(log e) 8 int64 mod_pow(int64 a, long long e, int64 p) { 9 a %= p; 10 if (a < 0) a += p; 11 int64 res = 1 % p; 12 while (e > 0) { 13 if (e & 1LL) res = (int64)((i128)res * a % p); 14 a = (int64)((i128)a * a % p); 15 e >>= 1LL; 16 } 17 return res; 18 } 19 20 // Fermat inverse: valid only when p is prime and gcd(a, p) = 1 21 int64 mod_inv_fermat(int64 a, int64 p) { 22 // Precondition checks are caller's responsibility in performance-critical code 23 return mod_pow(a, p - 2, p); 24 } 25 26 int main() { 27 ios::sync_with_stdio(false); 28 cin.tie(nullptr); 29 30 // Example: compute (a / b) mod p for multiple queries 31 // Input format: 32 // p q 33 // then q lines of a b 34 // Assumes p is prime and b not divisible by p 35 long long p; int q; 36 if (!(cin >> p >> q)) return 0; 37 38 while (q--) { 39 long long a, b; cin >> a >> b; 40 a %= p; if (a < 0) a += p; 41 b %= p; if (b < 0) b += p; 42 if (b == 0) { 43 cout << "Division undefined (b ≡ 0 mod p)\n"; 44 continue; 45 } 46 long long invb = mod_inv_fermat(b, p); 47 long long ans = (long long)((i128)a * invb % p); 48 cout << ans << '\n'; 49 } 50 return 0; 51 } 52
This program implements binary exponentiation and uses Fermat's Little Theorem to compute modular inverses as b^{p-2} mod p. It then answers queries of modular division a/b mod p in O(log p) per query. It safely multiplies using 128-bit intermediates to avoid overflow.
1 #include <bits/stdc++.h> 2 using namespace std; 3 using int64 = long long; 4 using i128 = __int128_t; 5 6 int64 mod_pow(int64 a, long long e, int64 p) { 7 a %= p; if (a < 0) a += p; 8 int64 res = 1 % p; 9 while (e > 0) { 10 if (e & 1LL) res = (int64)((i128)res * a % p); 11 a = (int64)((i128)a * a % p); 12 e >>= 1LL; 13 } 14 return res; 15 } 16 17 struct Comb { 18 int n; 19 int64 p; 20 vector<int64> fact, invfact; 21 Comb(int n_, int64 p_) : n(n_), p(p_), fact(n_+1), invfact(n_+1) { 22 fact[0] = 1; 23 for (int i = 1; i <= n; ++i) fact[i] = (int64)((i128)fact[i-1] * i % p); 24 // Compute invfact[n] = (fact[n])^{p-2} using Fermat 25 invfact[n] = mod_pow(fact[n], p - 2, p); 26 for (int i = n; i >= 1; --i) invfact[i-1] = (int64)((i128)invfact[i] * i % p); 27 } 28 int64 nCk(int n_, int k_) const { 29 if (k_ < 0 || k_ > n_) return 0; 30 return (int64)((i128)fact[n_] * invfact[k_] % p * invfact[n_-k_] % p); 31 } 32 }; 33 34 int main() { 35 ios::sync_with_stdio(false); 36 cin.tie(nullptr); 37 38 // Example usage: precompute up to N and answer Q queries 39 int N, Q; long long P; 40 cin >> N >> P >> Q; // e.g., N=1000000, P=1000000007 (prime) 41 Comb C(N, P); 42 43 while (Q--) { 44 int n, k; cin >> n >> k; 45 cout << C.nCk(n, k) << '\n'; 46 } 47 return 0; 48 } 49
Precomputes factorials and inverse factorials modulo a prime p. The inverse factorial array is built using a single Fermat inverse fact[n]^{p-2} and then linear-time downward multiplication. Each binomial coefficient query is answered in O(1).
1 #include <bits/stdc++.h> 2 using namespace std; 3 using int64 = long long; 4 using i128 = __int128_t; 5 6 int64 mod_pow(int64 a, long long e, int64 m) { 7 a %= m; if (a < 0) a += m; 8 int64 res = 1 % m; 9 while (e > 0) { 10 if (e & 1LL) res = (int64)((i128)res * a % m); 11 a = (int64)((i128)a * a % m); 12 e >>= 1LL; 13 } 14 return res; 15 } 16 17 bool is_fermat_probable_prime(long long n, int iterations = 10) { 18 if (n < 2) return false; 19 // Small primes check 20 for (long long p : {2LL, 3LL, 5LL, 7LL, 11LL, 13LL, 17LL, 19LL, 23LL, 29LL, 31LL, 37LL}) { 21 if (n == p) return true; 22 if (n % p == 0) return false; 23 } 24 std::mt19937_64 rng(1234567); 25 std::uniform_int_distribution<long long> dist(2, n - 2); 26 for (int i = 0; i < iterations; ++i) { 27 long long a = dist(rng); 28 if (std::gcd(a, n) != 1) return false; // found a nontrivial gcd => composite 29 if (mod_pow(a, n - 1, n) != 1) return false; // violates FLT => composite 30 } 31 return true; // probable prime (can be wrong for Carmichael numbers) 32 } 33 34 int main() { 35 ios::sync_with_stdio(false); 36 cin.tie(nullptr); 37 38 long long n; cin >> n; 39 bool probable = is_fermat_probable_prime(n, 20); 40 cout << (probable ? "probable prime (Fermat)" : "composite") << "\n"; 41 return 0; 42 } 43
Implements a Fermat primality check: for random bases a, verify a^{n-1} ≡ 1 (mod n). If any base fails, n is composite. Passing all checks only means 'probable prime' because Carmichael numbers can pass. For robust primality, use Miller–Rabin.
1 #include <bits/stdc++.h> 2 using namespace std; 3 using int64 = long long; 4 using i128 = __int128_t; 5 6 int64 mod_pow(int64 a, long long e, int64 p) { 7 a %= p; if (a < 0) a += p; 8 int64 res = 1 % p; 9 while (e > 0) { 10 if (e & 1LL) res = (int64)((i128)res * a % p); 11 a = (int64)((i128)a * a % p); 12 e >>= 1LL; 13 } 14 return res; 15 } 16 17 int main() { 18 ios::sync_with_stdio(false); 19 cin.tie(nullptr); 20 21 long long a, p; string b; 22 // Input: a p b (with p prime, b is nonnegative integer as decimal string) 23 if (!(cin >> a >> p >> b)) return 0; 24 25 a %= p; if (a < 0) a += p; 26 27 if (b == "0") { 28 // a^0 mod p = 1 for any a != 0; 0^0 is undefined but often treated as 1 in programming contests 29 cout << 1 % p << "\n"; 30 return 0; 31 } 32 33 if (a == 0) { 34 // 0^b mod p is 0 for b > 0 35 cout << 0 << "\n"; 36 return 0; 37 } 38 39 // Reduce exponent modulo (p-1) using FLT because gcd(a, p) = 1 40 long long phi = p - 1; // since p is prime 41 long long e_mod = 0; 42 for (char c : b) { 43 int d = c - '0'; 44 e_mod = ( ( (__int128)e_mod * 10 ) + d ) % phi; 45 } 46 47 cout << mod_pow(a, e_mod, p) << "\n"; 48 return 0; 49 } 50
Uses FLT to reduce the exponent modulo p−1 when gcd(a, p) = 1, allowing computation of a^{b} mod p even when b has thousands of digits. Handles edge cases: b = 0 and a ≡ 0 (mod p).