본문으로 건너뛰기
김신건의 로그

이산 제곱근 (Discrete Square Root / Tonelli-Shanks)

· 수정 · 📖 약 3분 · 807자/단어 #algorithm #math #discrete-sqrt #number-theory #modular-arithmetic
quadratic-residue, discrete sqrt, discrete square root, Tonelli-Shanks, 이산 제곱근, 모듈러 제곱근, modular square root

정의

이산 제곱근 (Discrete Square Root) 은 소수 p 에 대해 x^2 ≡ a (mod p) 를 만족하는 x 를 찾는 문제. a이차잉여 (quadratic residue) 이면 해가 존재하며, Tonelli-Shanks 알고리즘 (1970, Daniel Shanks) 이 일반적인 소수 p 에 대해 O(log^2 p) 시간에 해를 구한다. p ≡ 3 (mod 4) 일 때는 x = ±a^{(p+1)/4} 로 단순화.

문제 상황과 동기

x^2 ≡ a (mod p) 에서 x 를 구하라. p 는 홀수 소수.

  • naive: x = 0..p-1 을 순회. O(p). p=10^9 면 불가능.
  • p ≡ 3 (mod 4): x = ±a^{(p+1)/4}. 거듭제곱 O(log p).
  • 일반 p (p ≡ 1 mod 4): Tonelli-Shanks. O(log^2 p).

핵심 통찰: 합성곱(quadratic residue) 구조를 이용해 p-1 = s·2^e 로 분해, 반복적으로 위수를 줄임.

시각화

핵심 아이디어

p-1 = s·2^e (s: 홀수). Tonelli-Shanks 는 다음 invariant 를 유지한다.

찾고자 하는 값: n (a = n)
p-1 = s·2^e

초기:
  x = n^{(s+1)/2}   # 첫 추정값
  b = n^s
  g = z^s           # z: 임의의 비제곱수
  r = e

반복:
  1. b^(2^m) ≡ 1 (mod p) 인 최소 m (0 < m ≤ r) 찾기
  2. m=0 이면 종료, x 가 답
  3. 아니면 x, b, g, r 갱신

특수 경우: p ≡ 3 (mod 4) 이면 x = ±n^{(p+1)/4} mod p.

알고리즘

tonelli_shanks(n, p):
    if n % p == 0: return 0
    if p % 4 == 3: return n^((p+1)/4) % p

    s, e = factorization of p-1 as s·2^e
    z = 2
    while z^((p-1)/2) != p-1 mod p: z += 1  # 비제곱수 찾기

    x = n^((s+1)/2) % p
    b = n^s % p
    g = z^s % p
    r = e

    while true:
        m = find_min_m(b, p, r)
        if m == 0: return x
        x = x · g^(2^(r-m-1)) % p
        b = b · g^(2^(r-m)) % p
        g = g^(2^(r-m)) % p
        r = m

구현

// Tonelli-Shanks: sqrt(n) mod p, O(log^2 p)
#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 tonelli_shanks(ll n, ll p) {
  if (n == 0) return 0;
  if (p % 4 == 3) return modpow(n, (p + 1) / 4, p);
  ll s = p - 1, e = 0;
  while (s % 2 == 0) s /= 2, e++;
  ll z = 2;
  while (modpow(z, (p - 1) / 2, p) != p - 1) z++;
  ll x = modpow(n, (s + 1) / 2, p);
  ll b = modpow(n, s, p);
  ll g = modpow(z, s, p);
  ll r = e;
  while (true) {
      ll m = 0, tmp = b;
      while (m < r && tmp != 1) tmp = tmp * tmp % p, m++;
      if (m == 0) return x;
      ll g_pow = modpow(g, 1LL << (r - m - 1), p);
      x = x * g_pow % p;
      g = g_pow * g_pow % p;
      b = b * g % p;
      r = m;
  }
}

int main() {
  ll n, p; cin >> n >> p;
  ll x = tonelli_shanks(n, p);
  ll other = p - x;
  if (x == 0 || x == other) cout << x;
  else cout << min(x, other) << " " << max(x, other);
  return 0;
}
stdin
8 19
결과
6 13

복잡도

항목
시간 (p≡3 mod 4)O(log p) (거듭제곱 1회)
시간 (일반)O(log^2 p) (Tonelli-Shanks 반복)
공간O(1)

변형 / 활용

Cipolla 알고리즘

Tonelli-Shanks 와 달리 비제곱수의 제곱근을 F_p[x]/(x^2 - a) 에서 직접 계산. O(log p). 구현은 더 짧지만, F_p^2 체 연산 필요.

합성수 mod

합성수 mod m 에서 제곱근은 인수분해와 동등한 난이도. CRT 로 각 소인수에 대한 해 결합.

응용

타원곡선 좌표 찾기, Rabin 암호 복호화, Quadratic sieve 체 단계.

함정

1. p 가 소수가 아닌 경우

Tonelli-Shanks 는 소수 p 에 대해서만 동작. 합성수 mod 는 인수분해 필요. 소수 판별 없이 들어오면 잘못된 결과.

2. 비제곱수 z 탐색

확률적 (무작위 시도). 기대 2회 시도. 최악 O(p) 가능하나 실용적.

3. 해가 없는 경우

a^((p-1)/2) ≠ 1 mod p 이면 이차잉여가 아님. Euler criterion 으로 미리 확인.

BOJ 연습 문제

번호제목정답률링크
BOJ 17646제곱수의 합 2 (More Huge)11.5%kokoa-lab
BOJ 17603Factorization25.0%kokoa-lab
BOJ 18609Square Root Partitioning24.6%kokoa-lab
BOJ 28811Монетки20.4%kokoa-lab

참고

이 글의 용어 (5개)
모듈러 역원 (Modular Multiplicative Inverse)algorithm
정의 정수 a 의 모듈러 역원 (modular multiplicative inverse) 은 을 만족하는 정수 x. 기호로 또는 . 존재 조건: gcd(a, m) = 1 일 때만…
분할 정복을 이용한 거듭제곱 (Exponentiation by Squaring)algorithm
정의 분할 정복을 이용한 거듭제곱 (Exponentiation by Squaring) 은 을 O(log n) 시간에 계산하는 분할 정복 알고리즘. 같은 모듈러 연산에서 특히 필수…
이산 로그 (Discrete Logarithm)algorithm
정의 이산 로그 (Discrete Logarithm) 는 군 G 에서 를 만족하는 최소 음이 아닌 정수 x 를 찾는 문제. 실수 로그와 달리, mod 연산 아래에서의 역연산은 어…
중국인의 나머지 정리 (Chinese Remainder Theorem)algorithm
정의 중국인의 나머지 정리 (CRT) 는 다음과 같은 연립 합동식의 해가 유일 하게 존재함을 보장: 단, m1, m2, ..., mk 는 쌍마다 서로소 (pairwise copr…
Euler's Criterion: 이차잉여 판정algorithm
정의 홀소수 p 에 대해 a 가 mod p 에서 이차잉여 (quadratic residue) 인지 판정: $$ a^{(p-1)/2} \equiv \begin{cases} 1 & …

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기