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

중국인의 나머지 정리 (Chinese Remainder Theorem)

· 수정 · 📖 약 3분 · 841자/단어 #algorithm #math #crt #number-theory
CRT, 중국인의 나머지 정리, chinese remainder theorem, 중국인 나머지

정의

중국인의 나머지 정리 (CRT) 는 다음과 같은 연립 합동식의 해가 유일 하게 존재함을 보장:

x ≡ a_1 (mod m_1)
x ≡ a_2 (mod m_2)
...
x ≡ a_k (mod m_k)

단, m_1, m_2, …, m_k 는 쌍마다 서로소 (pairwise coprime).

해는 M = m_1 · m_2 · … · m_k 에 대해 modulo M 로 유일:

x = Σ a_i · M_i · inv(M_i)   (mod M)

M_i = M / m_i, inv(M_i) 는 M_i 의 modulo m_i 역원.

문제 상황과 동기

하나의 수 n 을 여러 mod 값으로 알고 있을 때 n 을 복원하라.

  • naive (n = 0..M-1 순회): O(M). M 이 크면 불가능.
  • CRT 공식 (closed form): O(k log M). 각 역원을 extgcd 로 O(log m_i).

핵심 통찰: 큰 mod 의 해는 각 작은 mod 의 해를 독립적으로 구하고 조합. M_i · inv(M_i) ≡ 1 (mod m_i) 가 “투영” 과 “복원” 을 담당.

시각화

핵심 아이디어

CRT 구성

M = Π m_i
M_i = M / m_i
t_i = M_i^(-1) (mod m_i)    (즉 M_i · t_i ≡ 1 mod m_i)
x = Σ a_i · M_i · t_i  (mod M)

검증: x ≡ a_i (mod m_i). 왜? M_i · t_i ≡ 1 (mod m_i) 이고, i ≠ j 에 대해 M_j 는 m_i 의 배수 (M_j = Π_{k≠j} m_k) 이므로 M_j ≡ 0 (mod m_i).

Garner’s Algorithm

CRT 를 반복문으로 구성: x_1 = a_1, x_{i+1} = x_i + M_i · ((a_{i+1} - x_i) · inv(M_i)) mod m_{i+1}. 큰 수 연산 없이 incremental.

비서로소 mod

m_i 가 pairwise coprime 이 아닐 때: 소인수분해 후 쪼개거나, extended CRT (exCRT) 사용.

알고리즘

crt(a[], m[], k):
    M = 1
    for i = 0..k-1: M *= m[i]
    x = 0
    for i = 0..k-1:
        M_i = M / m[i]
        t_i = extgcd(M_i, m[i]).x   // M_i * t_i ≡ 1 (mod m[i])
        x = (x + a[i] * M_i % M * t_i) % M
    return x

구현

// 중국인의 나머지 정리 (pairwise coprime mod)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

tuple<ll, ll, ll> extgcd(ll a, ll b) {
  if (b == 0) return {a, 1, 0};
  auto [d, x1, y1] = extgcd(b, a % b);
  return {d, y1, x1 - (a / b) * y1};
}

ll mod_inv(ll a, ll m) {
  auto [d, x, y] = extgcd(a, m);
  return (x % m + m) % m;
}

ll crt(vector<ll> a, vector<ll> m) {
  ll M = 1, x = 0;
  for (auto v : m) M *= v;
  for (size_t i = 0; i < a.size(); i++) {
      ll Mi = M / m[i];
      ll ti = mod_inv(Mi % m[i], m[i]);
      x = (x + a[i] * Mi % M * ti) % M;
  }
  return x;
}

int main() {
  int k; cin >> k;
  vector<ll> a(k), m(k);
  for (int i = 0; i < k; i++) cin >> a[i] >> m[i];
  cout << crt(a, m) << "\n";
  return 0;
}
stdin
3
2 3
3 5
2 7
결과
23

복잡도

항목
시간O(k log M) (k: 합동식 개수, M = Π m_i)
공간O(k) 또는 O(1) (incremental)
전제 조건m_i pairwise coprime

변형 / 활용

방법설명
Garner’s AlgorithmIncremental, 중간 큰 수 연산 없음
extended CRT (exCRT)m_i 가 서로소가 아닐 때도 해결
RSA-CRTdecryption 을 mod p, mod q 로 나눠 4x 속도 향상
NTT (Number Theoretic Transform)3-prime NTT 로 큰 convolution
보간 다항식 (Lagrange)CRT 와 구조적 유사 (점-값 → 다항식)

함정

1. mod 가 서로소가 아닌 경우

CRT 공식이 성립하지 않음. 해가 없거나, modulo lcm(m_i) 에서 여러 해 존재. exCRT 필요.

2. 64-bit 오버플로우

M = Π m_i 가 long long 범위 (≈ 9e18) 를 초과할 수 있음. Python 은 자동 bigint, C++ 은 __int128 또는 분할 연산.

3. 역원이 존재하지 않음

gcd(M_i, m_i) = 1 이어야 역원 존재 (pairwise coprime 가 보장). exCRT 는 이를 우회.

4. 음수 a_i 처리

a_i 를 modulo m_i 에서 양수로 정규화: a_i = (a_i % m_i + m_i) % m_i.

BOJ 연습 문제

번호제목정답률링크
BOJ 1476날짜 계산(수집 안 됨)kokoa-lab
BOJ 6064카잉 달력(수집 안 됨)kokoa-lab
BOJ 15717떡파이어(수집 안 됨)kokoa-lab
BOJ 11692시그마 함수(수집 안 됨)kokoa-lab

참고

이 글의 용어 (4개)
모듈러 역원 (Modular Multiplicative Inverse)algorithm
정의 정수 a 의 모듈러 역원 (modular multiplicative inverse) 은 을 만족하는 정수 x. 기호로 또는 . 존재 조건: gcd(a, m) = 1 일 때만…
오일러 피 함수 (Euler's Totient Function)algorithm
정의 오일러 피 함수 φ(n) 는 1 이상 n 이하의 자연수 중 n 과 서로소인 수의 개수. 1763년 레온하르트 오일러가 도입. , 소수 p 에 대해 . 문제 상황과 동기 n …
확장 유클리드 호제법 (Extended Euclidean Algorithm)algorithm
정의 확장 유클리드 호제법 (Extended Euclidean Algorithm) 은 두 정수 a, b 에 대해 를 구하면서, 동시에 를 만족하는 정수 x, y 를 찾는 알고리즘…
FFT, NTTalgorithm
정의 FFT (Fast Fourier Transform, 고속 푸리에 변환) 은 길이 인 수열의 이산 푸리에 변환 (DFT) 을 O(n log n) 에 계산하는 분할정복 알고리즘…

💬 댓글

사이트 검색 / 명령어

검색

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