이산 로그 (Discrete Logarithm)
정의
이산 로그 (Discrete Logarithm) 는 군 G 에서 a^x ≡ b (mod m) 를 만족하는 최소 음이 아닌 정수 x 를 찾는 문제. 실수 로그와 달리, mod 연산 아래에서의 역연산은 어렵다 (trapdoor function). Baby-step giant-step (BSGS) 알고리즘은 x 를 O(√m) 시간에 찾는 meet-in-the-middle 기법으로, Daniel Shanks 가 1971년 고안.
문제 상황과 동기
a^x ≡ b (mod m) 에서 x 를 구하라. m ≤ 10^9, 다중 쿼리.
- naive: x = 0..m-1 을 순회. O(m). m=10^9 면 불가능.
- BSGS: O(√m) 시간 + O(√m) 공간. m=10^9 면 약 31623 단계.
핵심 통찰: x 를 i·m + j 로 분할, baby step 으로 j 를 테이블에 저장, giant step 으로 i 를 찾는다.
시각화
핵심 아이디어
x = i·m + j (0 ≤ i, j < m, m = ⌈√φ(m)⌉ 또는 ⌈√m⌉).
a^(i·m + j) ≡ b (mod m)
→ a^j ≡ b · (a^(-m))^i (mod m)
- Baby step: j = 0..m-1 에 대해
a^j mod m을 hash map 에 저장. - Giant step: i = 0..m-1 에 대해
b · (a^(-m))^i mod m이 hash map 에 있는지 확인. - 일치하면
x = i·m + j반환.
m 이 1이 아닌 경우 a 와 m 이 서로소여야 a^(-1) 존재. 서로소가 아니면 일반화된 알고리즘 필요.
알고리즘
discrete_log(a, b, m):
a = a mod m, b = b mod m
if b == 1: return 0
m_sqrt = ceil(sqrt(m))
cur = 1
table = {}
for j in 0..m_sqrt-1: # baby step
if cur not in table:
table[cur] = j
cur = cur * a mod m
factor = a^(-m_sqrt) mod m # modular inverse
cur = b
for i in 0..m_sqrt-1: # giant step
if cur in table:
return i * m_sqrt + table[cur]
cur = cur * factor mod m
return -1 # not found
구현
// Baby-step giant-step: a^x = b (mod m), O(sqrt(m))
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll modpow(ll a, ll b, ll m) {
ll r = 1;
while (b) { if (b & 1) r = r * a % m; a = a * a % m; b >>= 1; }
return r;
}
ll discrete_log(ll a, ll b, ll m) {
a %= m; b %= m;
if (b == 1) return 0;
ll n = (ll)sqrt(m) + 1;
unordered_map<ll, ll> baby;
ll cur = 1;
for (ll j = 0; j < n; j++) {
if (!baby.count(cur)) baby[cur] = j;
cur = cur * a % m;
}
ll factor = modpow(a, m - 1 - (n - 1), m);
if (factor < 0) factor += m;
cur = b;
for (ll i = 0; i < n; i++) {
if (baby.count(cur)) return i * n + baby[cur];
cur = cur * factor % m;
}
return -1;
}
int main() {
ll a, b, m; cin >> a >> b >> m;
ll x = discrete_log(a, b, m);
if (x == -1) cout << "Not found\n";
else cout << a << "^" << x << " mod " << m << " = " << b << "\n";
return 0;
}2 3 52^3 mod 5 = 3복잡도
| 항목 | 값 |
|---|---|
| 시간 | O(√m) (group operation) |
| 공간 | O(√m) (hash table) |
| 전처리 | O(√m) (baby step) |
변형
| 방법 | 특징 | 용도 |
|---|---|---|
| Pollard rho (log) | O(√m) 시간, O(1) 공간 | 메모리 제한 환경 |
| Pohlig-Hellman | O(Σ e_i (log p + √p_i)) | m 의 소인수분해 가능할 때 |
| Index Calculus | subexponential | 특수 이산 로그 |
| Elliptic Curve BSGS | EC point group | ECDLP |
함정
1. a 와 m 이 서로소가 아닌 경우
a 와 m 이 gcd > 1 이면 modular inverse 가 존재하지 않아 BSGS 를 바로 쓸 수 없음. 이 경우 일반화된 이산 로그 (Pohlig-Hellman, 또는 확장 BSGS) 필요.
2. mod 가 1인 경우
m = 1 이면 모든 수가 0 ≡ 0. 아무 x 나 답. 특수 처리.
3. 해가 없는 경우
BSGS 가 -1 을 반환하면 해가 없음. 단, 순환군이 아닌 mod 에서는 실제 해가 있어도 BSGS 가 찾지 못할 수 있음.
4. Hash table 충돌
map/unordered_map 충돌로 O(√m) 보다 느려질 수 있음. C++ unordered_map 은 최악 O(n). 충돌 방지를 위해 map (RB-tree, O(log √m)) 사용도 고려.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 4357 | 이산 로그 (Discrete Logging) | 49.4% | kokoa-lab |
| BOJ 21864 | 이산로그가 장난이냐? | 77.1% | kokoa-lab |
| BOJ 7936 | N의 존재 | - | kokoa-lab |
참고
- 분할 정복을 이용한 거듭제곱
- 확장 유클리드 호제법 (modular inverse)
- 모듈러 역원
- 중국인의 나머지 정리 (Pohlig-Hellman)
이 글의 용어 (4개)
- 모듈러 역원 (Modular Multiplicative Inverse)algorithm
- 정의 정수 a 의 모듈러 역원 (modular multiplicative inverse) 은 을 만족하는 정수 x. 기호로 또는 . 존재 조건: gcd(a, m) = 1 일 때만…
- 분할 정복을 이용한 거듭제곱 (Exponentiation by Squaring)algorithm
- 정의 분할 정복을 이용한 거듭제곱 (Exponentiation by Squaring) 은 을 O(log n) 시간에 계산하는 분할 정복 알고리즘. 같은 모듈러 연산에서 특히 필수…
- 중국인의 나머지 정리 (Chinese Remainder Theorem)algorithm
- 정의 중국인의 나머지 정리 (CRT) 는 다음과 같은 연립 합동식의 해가 유일 하게 존재함을 보장: 단, m1, m2, ..., mk 는 쌍마다 서로소 (pairwise copr…
- 확장 유클리드 호제법 (Extended Euclidean Algorithm)algorithm
- 정의 확장 유클리드 호제법 (Extended Euclidean Algorithm) 은 두 정수 a, b 에 대해 를 구하면서, 동시에 를 만족하는 정수 x, y 를 찾는 알고리즘…
💬 댓글