예제
다음 일차연립합동식을 만족하는 모든
법 18에 대한 7의 곱셈 역원 13을 양변에 곱합시다. 7과 18이 서로소이므로 존재합니다.
따라서
식
역시 법 29에 대한 126의 곱셈 역원 3을 곱하여
고로
중국인의 나머지 정리
위 문제를 푸는 핵심 아이디어는, 법 둘을 어떻게 고르든 항상 서로소라는 걸 이용해서 곱셈 역원을 구하고 두 합동식을 하나로 합친다는 겁니다. 이를 일반화하여
를 만족할 때, 일차연립합동식
의 해를 구하는 일반적인 알고리즘을 생각할 수 있습니다. 이를 유도하기 전에 유도 과정에 필요한 보조정리 두 가지를 짚고 넘어가겠습니다.
정수
단,
곱셈 역원의 정의에 의해 적당한 정수
또는 법
이므로
가 성립합니다.
정수
따라서 보조정리가 성립합니다.
제일 처음 두 식부터 합쳐봅시다.
그대로 셋째 식에 또 대입합시다. 중간 과정이 매우 긴데, 결과만 적겠습니다.
규칙이 보이시나요?
이를 중국인의 나머지 정리(chinese remainder theorem, CRT)라고 합니다. 예제 1을 중국인의 나머지 정리로 풀어봅시다.
법이 서로소가 아닐 때
일차연립합동식에서 법이 서로소가 아니어도 두 식을 합칠 수 있습니다.
똑같은 방식으로
만약
따라서
또는
응용
BOJ 1476:: 날짜 계산
다음 합동식을 만족하는 양의 정수
중국인의 나머지 정리를 쓰면
BOJ 6064:: 카잉 달력
각 테스트 케이스에 대해 다음 합동식을 만족하는 양의 정수
위에서 설명한 대로
#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;
}