a·a^(p-2) ≡ a^(p-1) ≡ 1 (mod p)∴ inv(a) = a^(p-2) mod p
알고리즘
// 분할 정복 거듭제곱 O(log p)mod_pow(base, exp, mod): result = 1 while exp > 0: if exp & 1: result = (result * base) % mod base = (base * base) % mod exp >>= 1 return result// FLT 로 모듈러 역원mod_inverse(a, p): // p 는 소수 return mod_pow(a, p - 2, p)
구현
// Fermat 소정리 + 분할 정복 거듭제곱#include <bits/stdc++.h>using namespace std;using ll = long long;ll mod_pow(ll base, ll exp, ll mod) { ll result = 1; while (exp > 0) { if (exp & 1) result = (result * base) % mod; base = (base * base) % mod; exp >>= 1; } return result;}ll mod_inv(ll a, ll p) { return mod_pow(a, p - 2, p); // FLT}int main() { ll a, p; cin >> a >> p; cout << a << "^(" << p << "-2) mod " << p << " = " << mod_inv(a, p) << "\n"; cout << "verify: " << a << " * " << mod_inv(a, p) << " mod " << p << " = " << (a * mod_inv(a, p)) % p << "\n"; return 0;}
# pow(base, exp, mod) 내장 함수로 O(log exp)import sysinput = sys.stdin.readlinedef mod_inv(a: int, p: int) -> int: return pow(a, p - 2, p) # FLT, p 는 소수a, p = map(int, input().split())inv = mod_inv(a, p)print(f"inv({a}) mod {p} = {inv}")print(f"verify: {a} * {inv} mod {p} = {a * inv % p}")
// 분할 정복 거듭제곱 + FLTimport java.util.*;import java.io.*;public class Main { static long modPow(long base, long exp, long mod) { long result = 1; while (exp > 0) { if ((exp & 1) == 1) result = (result * base) % mod; base = (base * base) % mod; exp >>= 1; } return result; } static long modInv(long a, long p) { return modPow(a, p - 2, p); // FLT } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); long a = Long.parseLong(st.nextToken()); long p = Long.parseLong(st.nextToken()); long inv = modInv(a, p); System.out.println("inv(" + a + ") mod " + p + " = " + inv); System.out.println("verify: " + a + " * " + inv + " mod " + p + " = " + (a * inv % p)); }}
stdin
3 1000000007
결과
inv(3) mod 1000000007 = 333333336verify: 3 * 333333336 mod 1000000007 = 1
복잡도
항목
값
시간
O(log p) (분할 정복 거듭제곱)
공간
O(1)
전제 조건
p 는 소수, gcd(a, p) = 1
변형 / 활용
응용
설명
모듈러 역원
inv(a) = a^(p-2) mod p. 조합 nCr 에서 나누기 대체
이항 계수 (Lucas 정리)
큰 nCr 을 작은 소수 mod 에서 FLT 로 factorial inverse
Miller-Rabin 소수 판별
a^(d·2^s) ≡ 1 (mod n) 검사. FLT 의 역 활용
RSA 암호
ed ≡ 1 (mod φ(n)). FLT(오일러 정리) 가 기반
Fermat 의 인수분해
a^2 ≡ b^2 (mod n) → gcd(a-b, n)
함정
1. p 가 소수가 아닌 경우
FLT 가 성립하지 않음. 예: 2^(10-1) = 512 ≡ 2 (mod 10) ≠ 1. 오일러 정리 a^(φ(n)) ≡ 1 사용.
2. a 가 p 의 배수인 경우
a mod p = 0 이면 a^(p-1) ≡ 0 (mod p). 동치 형태 a^p ≡ a (mod p) 는 항상 성립 (0^p = 0).
3. p = 2
a^(1) ≡ 1 (mod 2) 는 홀수 a 에서 성립. 짝수면 a^(1) ≡ 0. 동치 형 a^2 ≡ a 는 항상 참.
4. 조합 inverse 전처리
nCr 을 많이 구할 때: factorial[n], inv_factorial[n] = factorial[n]^(p-2) 로 전처리. O(N + log p).
💬 댓글