모듈러 역원 (Modular Multiplicative Inverse)
정의
정수 a 의 모듈러 역원 (modular multiplicative inverse) 은 a·x ≡ 1 (mod m) 을 만족하는 정수 x. 기호로 a⁻¹ (mod m) 또는 inv(a, m).
존재 조건: gcd(a, m) = 1 일 때만 유일한 역원 존재 (mod m 에서).
주요 계산법:
- 확장 유클리드 호제법 (일반 mod, O(log m))
- Fermat 의 소정리 (m 이 소수, O(log m) 거듭제곱)
- Euler 정리 (gcd(a, m)=1, O(log m) 거듭제곱 + φ(m) 전처리)
문제 상황과 동기
나눗셈을 mod 에서 할 수 없을 때:
(a / b) mod m (X, 정의되지 않음)
→ a · b⁻¹ mod m (O, b 의 역원으로 곱셈 변환)
조합론 mod 계산 (nCr mod p):
nCr = n! / (r! · (n-r)!)→n! · inv(r!) · inv((n-r)!) mod p
분수 계수 DP, 확률 DP 에서 mod 1e9+7 로 정규화할 때 필수.
RSA 암호: 공개키 e 의 역원 d 를 φ(n) 에서 구함.
시각화
핵심 아이디어
-
확장 유클리드:
ax + my = gcd(a, m)의 정수해 (x, y) 구하기.- gcd(a, m) = 1 이면
ax + my = 1→ax ≡ 1 (mod m)→ x 가 역원. - x 를 0 ≤ x < m 범위로 정규화:
(x % m + m) % m.
- gcd(a, m) = 1 이면
-
Fermat 의 소정리 (m = p 소수):
aᵖ⁻¹ ≡ 1 (mod p)(gcd(a, p)=1).a · aᵖ⁻² ≡ 1 (mod p)→inv(a, p) = aᵖ⁻² mod p.- 분할 정복 거듭제곱 O(log p).
-
Euler 정리 (일반 m):
a^φ(m) ≡ 1 (mod m)(gcd(a, m)=1).inv(a, m) = a^(φ(m)-1) mod m.
invariant: a · inv(a, m) ≡ 1 (mod m) 항상 성립.
알고리즘
확장 유클리드 방식
inv_exgcd(a, m):
(g, x, y) = exgcd(a, m)
if g != 1:
return None # 역원 없음
return (x % m + m) % m
Fermat 의 소정리 (m 소수)
inv_fermat(a, p):
return pow(a, p - 2, p) # a^(p-2) mod p
Euler 정리 (일반 m)
inv_euler(a, m):
phi = euler_phi(m)
return pow(a, phi - 1, m)
구현
// 모듈러 역원: 확장 유클리드 + Fermat 소정리
#include <bits/stdc++.h>
using namespace std;
// 확장 유클리드
tuple<long long, long long, long long> exgcd(long long a, long long b) {
if (b == 0) return {a, 1, 0};
auto [g, x1, y1] = exgcd(b, a % b);
return {g, y1, x1 - (a / b) * y1};
}
// 역원 (일반 mod, 확장 유클리드)
long long inv_exgcd(long long a, long long m) {
auto [g, x, y] = exgcd(a, m);
if (g != 1) return -1; // 역원 없음
return (x % m + m) % m;
}
// 역원 (소수 mod, Fermat)
long long pow_mod(long long a, long long n, long long m) {
long long r = 1;
a %= m;
while (n > 0) {
if (n & 1) r = r * a % m;
a = a * a % m;
n >>= 1;
}
return r;
}
long long inv_fermat(long long a, long long p) {
return pow_mod(a, p - 2, p);
}
int main() {
long long a, m;
cin >> a >> m;
long long inv1 = inv_exgcd(a, m);
cout << "exgcd: " << inv1 << "\n";
// m 이 소수라 가정
long long inv2 = inv_fermat(a, m);
cout << "fermat: " << inv2 << "\n";
// 검증
if (inv1 != -1)
cout << "verify: " << (a * inv1) % m << " == 1\n";
return 0;
}5 1000000007exgcd: 400000003
fermat: 400000003
verify: 1 == 1복잡도
| 방법 | 시간 | 공간 | 조건 |
|---|---|---|---|
| 확장 유클리드 | O(log m) | O(log m) (재귀) | gcd(a, m)=1 |
| Fermat | O(log m) | O(1) | m 소수, gcd(a, m)=1 |
| Euler | O(log m + φ(m) 계산) | O(1) | gcd(a, m)=1 |
PS 에서는 mod 1e9+7 (소수) 이 대부분 → Fermat 가 간단. 일반 mod 는 확장 유클리드.
응용
1. 조합론 mod p (nCr)
// nCr mod p (p 소수)
long long fact[MAXN], inv_fact[MAXN];
void precompute(long long p) {
fact[0] = 1;
for (int i = 1; i < MAXN; i++)
fact[i] = fact[i-1] * i % p;
inv_fact[MAXN-1] = inv_fermat(fact[MAXN-1], p);
for (int i = MAXN-2; i >= 0; i--)
inv_fact[i] = inv_fact[i+1] * (i+1) % p;
}
long long nCr(int n, int r, long long p) {
if (r < 0 || r > n) return 0;
return fact[n] * inv_fact[r] % p * inv_fact[n-r] % p;
}
O(N) 전처리 + O(1) 쿼리.
2. 분수 계수 DP
# DP[i] = (분자) / (분모) mod p
MOD = 10**9 + 7
dp = [0] * N
dp[0] = 1
for i in range(N-1):
# dp[i+1] = dp[i] · 2 / 3
dp[i+1] = dp[i] * 2 % MOD * pow(3, -1, MOD) % MOD
3. 확률 mod 계산
기댓값 E = Σ p_i · x_i, p_i = a/b 일 때:
p_i mod m = a · inv(b, m) mod m
함정
1. gcd(a, m) ≠ 1
역원 존재하지 않음. 확인 없이 계산하면 WA 또는 런타임 에러.
if (gcd(a, m) != 1) {
// 역원 없음 처리
}
2. Fermat 를 비소수 mod 에
pow(a, m-2, m) 을 m 이 합성수일 때 쓰면 틀림. m 소수 여부 체크 필수.
3. 음수 역원
확장 유클리드에서 나온 x 가 음수일 수 있음. (x % m + m) % m 로 정규화.
4. 오버플로우
C++ 에서 a * inv % m 계산 시 중간 값이 long long 넘을 수 있음. (__int128)a * inv % m 또는 곱셈 mod 함수 사용.
5. 0 의 역원
0 은 역원 없음 (gcd(0, m) = m ≠ 1). 예외 처리.
변형
| 변형 | 설명 |
|---|---|
| 역원 배열 전처리 | inv[i] = inv(i, p) 를 O(N) 에 한번에. inv[i] = (p - (p/i)) * inv[p%i] % p 점화식 |
| 팩토리얼 역원 | inv_fact[i] = inv(i!, p) → nCr mod p |
| 다중 역원 (batch) | N 개 수의 역원 O(N + log p) |
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 14565 | 역원(Inverse) 구하기 | - | kokoa-lab |
| BOJ 11402 | 이항 계수 4 | - | kokoa-lab |
| BOJ 13977 | 이항 계수와 쿼리 | - | kokoa-lab |
| BOJ 15712 | 등비수열 | - | kokoa-lab |
참고
이 글의 용어 (4개)
- 조합론 (Combinatorics)algorithm
- 정의 조합론 (Combinatorics) 은 유한 집합의 원소를 세는 수학 분야. PS 에서는 주로 순열 (Permutation), 조합 (Combination), 이항계수 (B…
- 페르마 소정리 (Fermat's Little Theorem)algorithm
- 정의 페르마 소정리 (Fermat's Little Theorem, FLT) 는 소수 p 와 gcd(a, p) = 1 인 정수 a 에 대해 다음이 성립: 1640년 피에르 드 페르…
- 확장 유클리드 호제법 (Extended Euclidean Algorithm)algorithm
- 정의 확장 유클리드 호제법 (Extended Euclidean Algorithm) 은 두 정수 a, b 에 대해 를 구하면서, 동시에 를 만족하는 정수 x, y 를 찾는 알고리즘…
- Euler Phi Function: φ(n)algorithm
- 정의 Euler Phi Function φ(n) 은 1 이상 n 이하 정수 중 n 과 서로소인 정수의 개수. 기존 Euler Phi 위키 참조. 이 페이지는 alias 확장용 축…
이 개념을 다룬 위키 페이지 (13)
- wiki수학 (Mathematics)
- wiki조합론 (Combinatorics)
- wiki중국인의 나머지 정리 (Chinese Remainder Theorem)
- wiki이산 로그 (Discrete Logarithm)
- wiki이산 제곱근 (Discrete Square Root / Tonelli-Shanks)
- wiki유클리드 호제법 (Euclidean Algorithm)
- wiki오일러 피 함수 (Euler's Totient Function)
- wiki확장 유클리드 호제법 (Extended Euclidean Algorithm)
- wiki페르마 소정리 (Fermat's Little Theorem)
- wiki가우스 소거법 (Gaussian Elimination)
- wikiLifting The Exponent (LTE) Lemma
- wiki뤼카 정리 (Lucas Theorem)
- wikiMiller-Rabin 소수 판정 (Miller-Rabin Primality Test)
💬 댓글