MathIntermediate

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 basicsUnderstanding congruences, residues, and modulus operations is essential to apply FLT correctly.
  • Prime numbersFLT 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 exponentiationEfficiently 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 algorithmsInterpreting Fermat's primality test results requires understanding 'probable prime' and error risk.

Detailed Explanation

Tap terms for definitions

01Overview

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

Let p be a prime and let a with p a. Then Fermat’s Little Theorem states: 1 . Equivalently, the multiplicative order of a in the group (/p)^{} divides p−1. A direct corollary follows by multiplying both sides by : . This provides a constructive formula for modular inverses when the modulus is prime. The statement arises from group theory: the nonzero residues modulo p form a cyclic group of size p−1 under multiplication; by Lagrange’s theorem, equals the identity element for any a in that group. Euler’s theorem generalizes FLT: for n 1 and a with (a, n) = 1, we have 1 , where is Euler’s totient function. For prime p, (p) = p−1, recovering FLT. In algorithmic terms, we exploit these congruences using fast exponentiation to compute mod p in O( p) time, enabling modular division and exponent reduction.

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

The core operation enabled by Fermat’s Little Theorem is modular exponentiation. Using binary exponentiation, computing p runs in O( e) time and O(1) extra space, since each step squares the base and conditionally multiplies when the current bit of e is 1. Therefore, a Fermat-based modular inverse p costs O( p) time and O(1) space. When precomputing factorials and inverse factorials modulo a prime p up to N, we do O(N) multiplications to build fact[i] and another O(N) to build invfact[i] (commonly by computing fact[N]^{p-2} once, then walking down), for a total of O(N) time and O(N) space. Each nCk query then answers in O(1) time using three table lookups and two or three modular multiplications. For primality filtering via Fermat’s test with k bases, each test performs a modular exponentiation n, costing O( n) time; overall O(k n) with O(1) space, plus gcd checks. Note that safe multiplication under the modulus may require 128-bit temporaries to avoid overflow, which does not change asymptotic cost but is essential for correctness. Reducing huge exponents b given as a decimal string requires processing the string once to compute b (p−1), which is O(L) for L digits, followed by one O( p) exponentiation. In summary, FLT-based operations are highly efficient: per-operation costs are logarithmic or constant after linear precomputation, making them ideal for constraints typical of competitive programming.

Code Examples

Modular inverse via Fermat and modular division a/b mod p
1#include <bits/stdc++.h>
2using namespace std;
3
4using int64 = long long;
5using i128 = __int128_t; // for safe multiplication when needed
6
7// Fast modular exponentiation: computes a^e mod p in O(log e)
8int64 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
21int64 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
26int 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.

Time: O(q \log p)Space: O(1)
nCk modulo a prime using factorials and Fermat inverses
1#include <bits/stdc++.h>
2using namespace std;
3using int64 = long long;
4using i128 = __int128_t;
5
6int64 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
17struct 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
34int 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).

Time: Precomputation O(N), each query O(1)Space: O(N)
Fermat probable-prime test (with caveats)
1#include <bits/stdc++.h>
2using namespace std;
3using int64 = long long;
4using i128 = __int128_t;
5
6int64 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
17bool 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
34int 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.

Time: O(k \log n) for k iterationsSpace: O(1)
Reducing huge exponents using FLT: compute a^b mod p when b is a big decimal string
1#include <bits/stdc++.h>
2using namespace std;
3using int64 = long long;
4using i128 = __int128_t;
5
6int64 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
17int 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).

Time: O(L + \log p), where L is the number of digits of bSpace: O(1)