중국인의 나머지 정리

2018년 8월 13일

예제

다음 일차연립합동식을 만족하는 모든 x를 구하시오.

x5(mod7)x13(mod18)x21(mod29)

x가 첫 번째 식을 만족해야 하므로 x=7n+5라고 쓸 수 있습니다. 이를 두 번째 식에 대입하면

7n+513(mod18)7n8(mod18)

법 18에 대한 7의 곱셈 역원 13을 양변에 곱합시다. 7과 18이 서로소이므로 존재합니다.

n14(mod18)

따라서 n=18m+14로 나타낼 수 있고, 이걸 다시 x=7n+5에 대입하면 x=126m+103, 또는

(1)x103(mod126)

(1)은 연립합동식의 첫 번째와 두 번째 식을 합친 것입니다. 같은 방법으로 세 번째 식까지 합치면 연립합동식의 해를 구할 수 있습니다. x=126m+103을 세 번째 식에 대입하면

126m+10321(mod29)126m5(mod29)

역시 법 29에 대한 126의 곱셈 역원 3을 곱하여 m을 구합니다.

m15(mod29)

고로 m=29k+15이고, x=126m+103에 대입하면 주어진 방정식의 해는 다음과 같습니다.

x1993(mod3654)

중국인의 나머지 정리

위 문제를 푸는 핵심 아이디어는, 법 둘을 어떻게 고르든 항상 서로소라는 걸 이용해서 곱셈 역원을 구하고 두 합동식을 하나로 합친다는 겁니다. 이를 일반화하여 m1, , mn

gcd(mi,mj)=1for  ij

를 만족할 때, 일차연립합동식

xa1(modm1)xa2(modm2)xan(modmn)

의 해를 구하는 일반적인 알고리즘을 생각할 수 있습니다. 이를 유도하기 전에 유도 과정에 필요한 보조정리 두 가지를 짚고 넘어가겠습니다.

정수 a, b, k에 대해

1ka[(ka)1modb]=b(b1moda)

단, a1modm은 법 m에 대한 a의 곱셈 역원을 의미한다.

곱셈 역원의 정의에 의해 적당한 정수 l이 존재하여,

ka[(ka)1modb]=bl+1

또는 법 a에 대한 합동식으로 쓰면

bl+10(moda)b(l)1(moda)

이므로 l은 법 a에 대한 b의 곱셈 역원입니다. 따라서

1ka[(ka)1modb]=b(l)=b(b1moda)

가 성립합니다.

정수 a, b, m에 대해

(a1modm)(b1modm)=(ab)1modm
a(a1modm)1(modm)b(b1modm)1(modm) ab(a1modm)(b1modm)1(modm)

따라서 보조정리가 성립합니다.

제일 처음 두 식부터 합쳐봅시다. x=m1k1+a1이라 하고 둘째 식에 대입하면

m1k1+a1a2(modm2) k1(m11modm2)(a2a1)(modm2) k1=m2k2+(m11modm2)(a2a1)
x=m1m2k2+m1(m11modm2)(a2a1)+a1=m1m2k2+[1m1(m11modm2)]a1+m1(m11modm2)a2=m1m2k2+m2(m21modm1)a1+m1(m11modm2)a2

그대로 셋째 식에 또 대입합시다. 중간 과정이 매우 긴데, 결과만 적겠습니다.

x=m1m2m3k3+m2m3[(m2m3)1modm1]a1+m3m1[(m3m1)1modm2]a2+m1m2[(m1m2)1modm3]a3

규칙이 보이시나요? M=m1m2m3, Mi=M/mi라 하면 연립합동식의 최종 정답은 아래와 같습니다.

xi=1nMi(Mi1modmi)ai(modM)

이를 중국인의 나머지 정리(chinese remainder theorem, CRT)라고 합니다. 예제 1을 중국인의 나머지 정리로 풀어봅시다.

M=3654M1=522,M2=203,M3=126M11modm1=2,M21modm2=11,M31modm3=3x522×2×5+203×11×13+126×3×211993(mod3654)

법이 서로소가 아닐 때

일차연립합동식에서 법이 서로소가 아니어도 두 식을 합칠 수 있습니다. m1m2가 서로소가 아니라고 가정하고 다음 합동식을 봅시다.

(2)xa1(modm1)xa2(modm2)

똑같은 방식으로 x=m1k1+a1이라 하고 두 번째 식에 대입하면

m1k1a2a1(modm2)m1k1+m2k2=a2a1

만약 a2a1m1m2의 최대공약수 g의 배수가 아니라면 식 (2)의 해는 존재하지 않습니다. 이제 확장 유클리드 호제법으로 적당한 k1의 값을 하나 구했다고 하면(k0라고 합시다) k1의 일반해는

k1=k0+lm2g

따라서

x=m1k0+lm1m2g+a1

또는 m1m2/gm1m2의 최소공배수이므로

xm1k0+a1(modlcm(m1,m2))

응용

BOJ 1476:: 날짜 계산

다음 합동식을 만족하는 양의 정수 x의 최솟값을 구하면 됩니다.

xE(mod15)xS(mod28)xM(mod19)

중국인의 나머지 정리를 쓰면

x6916E+4845S+4200M(mod7980)

BOJ 6064:: 카잉 달력

각 테스트 케이스에 대해 다음 합동식을 만족하는 양의 정수 k의 최솟값을 구하면 됩니다.

kx(modM)ky(modN)

위에서 설명한 대로 yxgcd(M,N)의 배수여야 합니다.

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

tuple<ll, ll, ll> extended_euclidean(ll a, ll b) {
    if (b == 0)
        return make_tuple(a, 1, 0);
    auto [g, x, y] = extended_euclidean(b, a%b);
    return make_tuple(g, y, x-(a/b)*y);
}

int main(void) {
    ll t, m, n, x, y, k;
    cin >> t;
    while (t--) {
        cin >> m >> n >> x >> y;
        auto [g, k0, _] = extended_euclidean(m, n);

        if ((y - x) % g != 0)
            cout << "-1" << endl;
        else {
            k0 *= (y - x) / g;
            k = (m * k0 + x) % (m * n / g);
            while (k <= 0)
                k += m * n / g;
            cout << k << endl;
        }
    }
    return 0;
}