Homeβ€Ί DSAβ€Ί Modular Arithmetic β€” ModExp, ModInverse, Fermat's Little Theorem

Modular Arithmetic β€” ModExp, ModInverse, Fermat's Little Theorem

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: Number Theory β†’ Topic 4 of 5
Master modular arithmetic for competitive programming and cryptography.
βš™οΈ Intermediate β€” basic DSA knowledge assumed
In this tutorial, you'll learn:
  • Modular arithmetic distributes over +, -, Γ— but NOT / β€” use modular inverse for division.
  • Fast modexp: O(log exp) using repeated squaring β€” use Python's pow(a,b,mod).
  • Modular inverse when mod is prime: a^(-1) ≑ a^(mod-2) mod p via Fermat's little theorem.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑ Quick Answer
Clock arithmetic is modular arithmetic β€” 10 hours after 5pm is 3am, not 15:00. Everything wraps around at the modulus. In computer science, modular arithmetic enables working with huge numbers (like in cryptography) by keeping results in a manageable range, while preserving mathematical properties.

Every competitive programming problem involving large numbers ends with 'output the answer modulo 10^9+7'. Every RSA operation involves modular exponentiation. Every Diffie-Hellman key exchange involves modular arithmetic. Understanding modular arithmetic is not optional for systems engineers or competitive programmers β€” it is the foundation layer that makes everything else possible.

The key insight that unlocks modular arithmetic: (a Γ— b) mod m = ((a mod m) Γ— (b mod m)) mod m. This distributivity means you can reduce intermediate values at every step, keeping numbers in range, without affecting the final result. Without this, even simple operations on 'big' numbers would overflow. With it, you can compute 2^(10^18) mod (10^9+7) with Python's built-in pow(2, 1018, 109+7) β€” under a microsecond.

Modular Arithmetic Properties

The key property: modular operations distribute over addition, subtraction, and multiplication: (a + b) mod m = ((a mod m) + (b mod m)) mod m (a Γ— b) mod m = ((a mod m) Γ— (b mod m)) mod m (a - b) mod m = ((a mod m) - (b mod m) + m) mod m

Division requires modular inverse β€” you cannot simply divide.

Fast Modular Exponentiation

Compute a^b mod m in O(log b) using repeated squaring. Essential for cryptography and competitive programming.

modexp.py Β· PYTHON
12345678910111213141516
def mod_exp(base: int, exp: int, mod: int) -> int:
    """Compute base^exp mod m in O(log exp)."""
    result = 1
    base %= mod
    while exp > 0:
        if exp & 1:  # if exp is odd
            result = result * base % mod
        base = base * base % mod
        exp >>= 1
    return result

print(mod_exp(2, 10, 1000))   # 1024 mod 1000 = 24
print(mod_exp(3, 200, 13))    # 3^200 mod 13 = 9
print(mod_exp(2, 10**18, 10**9+7))  # handles huge exponents
# Python built-in: pow(base, exp, mod)
print(pow(2, 10, 1000))  # 24 β€” uses same algorithm
β–Ά Output
24
9
246026182
24

Modular Inverse β€” Fermat's Little Theorem

The modular inverse of a mod m is x such that a*x ≑ 1 (mod m). When m is prime, Fermat's Little Theorem gives: a^(m-1) ≑ 1 (mod m), so a^(-1) ≑ a^(m-2) (mod m). Compute in O(log m).

mod_inverse.py Β· PYTHON
12345678910111213141516
MOD = 10**9 + 7  # common prime modulus in competitive programming

def mod_inv_fermat(a: int, mod: int = MOD) -> int:
    """Modular inverse using Fermat's little theorem.
    Only works when mod is prime."""
    return pow(a, mod - 2, mod)

print(mod_inv_fermat(3))    # 333333336 (3 * 333333336 mod MOD = 1)
print(3 * mod_inv_fermat(3) % MOD)  # 1 βœ“

# Modular division: a/b mod m = a * inv(b) mod m
def mod_div(a: int, b: int, mod: int = MOD) -> int:
    return a * mod_inv_fermat(b, mod) % mod

print(mod_div(10, 2))  # 5 (10/2 mod MOD = 5)
print(mod_div(7, 3))   # (7 * inv(3)) mod MOD
β–Ά Output
333333336
1
5
333333340

Precomputing Factorials and Inverse Factorials

For combinatorics problems (nCr mod p), precompute factorial and inverse factorial arrays.

factorial_mod.py Β· PYTHON
12345678910111213141516171819
MOD = 10**9 + 7

def precompute_factorials(n: int, mod: int = MOD):
    fact = [1] * (n + 1)
    for i in range(1, n + 1):
        fact[i] = fact[i-1] * i % mod
    inv_fact = [1] * (n + 1)
    inv_fact[n] = pow(fact[n], mod - 2, mod)
    for i in range(n - 1, -1, -1):
        inv_fact[i] = inv_fact[i+1] * (i+1) % mod
    return fact, inv_fact

def n_choose_r(n, r, fact, inv_fact, mod=MOD):
    if r < 0 or r > n: return 0
    return fact[n] * inv_fact[r] % mod * inv_fact[n-r] % mod

fact, inv_fact = precompute_factorials(10**6)
print(n_choose_r(10, 3, fact, inv_fact))   # 120
print(n_choose_r(20, 10, fact, inv_fact))  # 184756
β–Ά Output
120
184756

Wilson's Theorem and Fermat's Little Theorem

Fermat's Little Theorem: If p is prime and gcd(a,p)=1, then a^(p-1) ≑ 1 (mod p).

Wilson's Theorem: p is prime iff (p-1)! ≑ -1 (mod p). Theoretically elegant but computationally impractical for primality testing.

Euler's Theorem (generalisation): a^Ο†(n) ≑ 1 (mod n) when gcd(a,n)=1. Fermat's is the special case when n is prime (Ο†(p)=p-1).

πŸ”₯
Competitive ProgrammingThe modulus 10^9+7 is prime and fits in 32 bits. Always use it unless the problem specifies otherwise. Precompute factorials and inverse factorials up to the maximum n for O(1) binomial coefficients.

🎯 Key Takeaways

  • Modular arithmetic distributes over +, -, Γ— but NOT / β€” use modular inverse for division.
  • Fast modexp: O(log exp) using repeated squaring β€” use Python's pow(a,b,mod).
  • Modular inverse when mod is prime: a^(-1) ≑ a^(mod-2) mod p via Fermat's little theorem.
  • Precompute factorials and inverse factorials for O(1) nCr mod p queries.
  • 10^9+7 is the standard competitive programming prime modulus.

Interview Questions on This Topic

  • QHow does fast modular exponentiation work and why is it O(log exp)?
  • QWhat is Fermat's Little Theorem and how does it give us modular inverses?
  • QWhen does a modular inverse not exist?
  • QHow would you compute C(n,r) mod 10^9+7 efficiently?

Frequently Asked Questions

Why is 10^9+7 so commonly used as the modulus?

It is prime (enabling Fermat's inverse), fits in 32 bits, and the product of two numbers mod 10^9+7 fits in 64 bits (10^18 < 2^63). These three properties make it ideal for competitive programming.

πŸ”₯
Naren Founder & Author

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.

← PreviousPrime Factorization and DivisorsNext β†’Chinese Remainder Theorem
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged