Loading [MathJax]/jax/output/CommonHTML/jax.js
 

중국인의 나머지 정리(Chinese Remainder Theorem) 기본 이해하기

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

 

정수 m1,m2,...,mn이 임의의 i,j = 1,2,...,n에 대하여 ij일때, mimj가 서로소라면, 즉

 

gcd(mi,mj)=1,ij라고 하자.

 

일차연립합동식 xa1(modm1),  xa2(modm2),  xa3(modm3), ,  xan(modmn) 의 해는 mod m1,m2,...,mn에 대하여 유일하게 존재한다.

 

 

2. 보조정리 1

 

정수 a,b,k에 대하여 1ka×[(ka)1(modb)]=b×[b1(moda)]

 

여기서 [a1(modm)]은 m에 대한 a의 곱셈의 역원이다.

 

(증명)

 

곱셈 역원의 정의는, a×[a1(modm)]1이다.

 

즉, a와 a의 역원 [a1(modm)]의 곱을 m으로 나눈 나머지가 1이라는 뜻이다.

 

그러므로 ka×[(ka)1modb]1은 b에 대하여 적당한 정수 p가 존재하여...

 

ka×[(ka)1modb]=bp+1로 바꿔쓸수 있다.

 

그런데, ka×[(ka)1modb]은 a로 나눈 나머지가 0이다.

 

왜냐하면, ka×[(ka)1modb]는, 정수 k, 정수 a, 정수 [(ka)1modb]의 곱으로 이루어져 있기 때문에, a로 나누면 당연히 나누어 떨어진다.

 

그러므로 다음과 같이 a에 대한 합동식으로 바꿔쓸수 있다.

 

bp+10(moda)

 

합동식의 성질에 따라 양변에 -1을 빼도 성립하므로,

 

bp1(moda)

 

양변에 -1을 곱해도 성립하므로...

 

b(p)1(moda)

 

그러므로 -p는 b의 a에 대한 곱셈의 역원 [b1moda]이고 ka×[(ka)1modb]=bp+1으로부터 다음 식이 유도된다.

 

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

 

 

3. 보조정리 2

 

정수 a,b,m에 대하여...

 

[a1modm][b1modm]=[(ab)1modm]

 

(증명)

 

곱셈 역원의 정의로부터..

 

a×[a1modm]1(modm)

b×[b1modm]1(modm)

 

두 식을 곱하면...

 

ab×[a1modm][b1modm]1(modm)

 

그러므로, ab의 m에 대한 곱셈의 역원은... [a1modm][b1modm]

 

 

4. 중국인의 나머지 정리 해법

 

두 식 xa1(modm1), xa2(modm2)을 생각해보자.

 

먼저 첫번째 식으로부터.. x를 m1으로 나눈 나머지가 a1이므로..

 

x=m1k1+a1

 

두번째 식에 대입하면..

 

m1k1+a1a2(modm2)

 

양변에 a1을 빼면...

 

m1k1(a2a1)(modm2)

 

양변에 정수 m1m2에 대한 곱셈의 역원을 곱해주면

 

k1[m11modm2](a2a1)(modm2)

 

위 식은 나머지 식이므로, 등식으로 바꾸면 다음과 같다.

 

k1=m2k2+[m11modm2](a2a1)

 

이 식을 x=m1k1+a1에 대입하면 다음과 같다.

 

x=m1m2k2+m1[m11modm2](a2a1)+a1

 

식을 풀어써보면 다음과 같다.

 

x=m1m2k2+a1a1(m1[m11modm2])+a2m1[m11modm2]

 

보조정리 1 1ka×[(ka)1(modb)]=b×[b1(moda)]에 의하여

 

x=m1m2k2+a1(m2[m12modm1])+a2m1[m11modm2]

 

이것을 세번째 식인 xa3(modm3)에 대입하고 보조정리 2를 이용하면 다음을 얻는다고 한다.

 

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

 

n개의 합동식에 대해 위 과정을 반복하면 다음을 얻는다고 한다.

 

M=m1m2...mn이고 이것을 mi로 나눈 몫 Mi=M/mi라고 하자.

 

n개의 연립합동식은 다음과 같은 하나의 합동식으로 나타낼 수 있다.

 

xni=1Mi[M1imodmi]ai(modM)

 

 

5. 예시를 통해 이해해보기

 

x5(mod7), x13(mod18), x21(mod29)의 x를 구해보자.

 

먼저 m1=7, m2=18, m3=29이고.. 어떤 두 쌍을 잡아도 서로소이다.

 

(7,18)과 (18,29), (7,29)가 서로소이다.

 

세 수의 곱 M=7×18×29=3654

 

그리고 M1=3654/7=522, M2=3654/18=203이고, M3=3654/29=126

 

그러면 [M11modm1]을 구해야하는데 어떻게 할 수 있을까

 

페르마의 소정리로도 구할 수 있고 확장 유클리드 호제법으로도 구할 수 있다.

 

def extended_gcd(a,b):
    
    before_x = 1
    before_y = 0

    x = 0 
    y = 1

    while b != 0:
        
        q,r = a//b, a%b

        a,b = b,r

        before_x,x = x,before_x - x*q
        before_y,y = y,before_y - y*q
    
    return a,before_x,before_y

 

522x1(mod7)는 다음과 같이 바꿔쓸수 있다.

 

522x=7y+1

 

그러므로, 522x7y=1

 

522와 7에 대한 확장 유클리드 알고리즘을 사용하면 x=2를 얻는다.

 

[M11modm1]=2

 

비슷한 방식으로 나머지 M2, M3에 대한 역원은 다음과 같이 구해진다.

 

[M12modm2]=11,

 

[M13modm3]=3

 

그러므로 xni=1Mi[M1imodmi]ai(modM)에 대입하면 다음과 같은 하나의 합동식으로 나타난다.

 

x(52225+2031113+126321)=421871993(mod3654)

 

그래서 연립합동식의 해는 정수 k에 대하여..

 

x=3654k+1993으로 나타낼 수 있다.

 

 

6. 2개의 연립합동식의 해

 

공식을 외우지 못하더라도.. 합동식이 뭔지 알고 있다면 사실 충분히 풀 수 있다.

 

xa1(modm1),

xa2(modm2)이라고 하자.

 

첫번째 합동식은 m1에 대하여... x=m1y+a1인 정수 y가 존재한다.

 

이를 두번째 합동식에 대입하면... m1y+a1a2(modm2)

 

양변에 a1을 빼면... m1y(a2a1)(modm2)

 

역시 등식으로 바꾸면.. m1y=m2k+(a2a1)

 

그러므로 다음과 같은 선형 디오판토스 방정식으로 바꿀 수 있다.

 

m1ym2k=a2a1

 

이 선형 디오판토스 방정식의 해법은 다음과 같았다.

 

먼저 g=gcd(m1,m2)에 대하여, (a2a1)가 g의 배수가 아니면 해 (y,k)가 존재하지 않는다.

 

g의 배수라면 (y,k)가 존재하고... 양변을 g로 나눈다.

 

그러면 m1/gm2/g는 서로소이고... (m1/g)y+(m2/g)k=1의 해를 확장 유클리드 알고리즘으로 찾는다.

 

그러한 해 y', k'에 대하여... y = ((a_{2}-a_{1})/g)y', k = -((a_{2}-a_{1})/g)k'을 고르면...

 

m1ym2k=a2a1이 된다.

 

따라서, y = ((a_{2}-a_{1})/g)y', k = -((a_{2}-a_{1})/g)k'이 하나의 해가 된다.

 

 

7. mi의 쌍들이 서로소가 아닐때

 

위에서 구한 중국인의 나머지 정리의 공식

 

xni=1Mi[M1imodmi]ai(modM)의 전제조건은, m1,m2,...,mn의 모든 쌍들이 서로소여야 한다.

 

하지만, 모든 쌍이 서로소인 경우는 극히 드물것이다.

 

실제로 서로소가 아니더라도, 연립합동식을 하나의 합동식으로 나타낼 수 있다.

 

바로 위 6번에 기술한대로...

 

xa1(modm1)

xa2(modm2)이라고 하자.

 

첫번째 식은 x를 m1으로 나눈 나머지가 a1이라는 뜻이므로, x=m1k1+a1을 만족하는 정수 k_{1}이 존재한다.

 

이 식을 두번째 합동식에 넣으면...

 

m1k1+a1a2(modm2)

 

양변에 a_{1}을 빼면..

 

m1k1a2a1(modm2)

 

이러한 합동식을 푸는 방법은 이미 배운적이 있다.

 

m1k1m2로 나눈 나머지가 a_{2} - a_{1}이므로...

 

m1k1=m2k2+a2a1

 

식을 바꿔쓰면..

 

m1k1m2k2=a2a1의 선형 디오판토스 방정식이 된다.

 

이미 알다시피 m1m2의 최대공약수를 g=gcd(m1,m2)라고 하자.

 

a2a1이 g의 배수가 아니면, 이 방정식의 해는 존재하지 않는다.

 

g의 배수라면, 방정식의 해가 존재하고..

 

구하는 방법은 m1k1m2k2=a2a1의 양변을 g로 나누면..

 

m1gm2g는 서로소이고.. 이들을 이용해서 확장 유클리드 알고리즘을 통해 k1,k2을 구할 수 있다.

 

이들은 m1gk1m2gk2=1 의 특수해이다.

 

그러므로.. m1/g,m2/g를 이용한 확장 유클리드 알고리즘으로 k1,k2을 구하자.

 

만약, k1=a2a1gk1, k2=a2a1gk2k1, k2를 선택하자.

 

그러면, k1=ga2a1k1, k2=ga2a1k2이므로,

 

m1gk1m2gk2=1에 대입하면 다음과 같다.

 

m1k1m2k2=a2a1

 

그러므로  x=m1k1+a1을 만족하는 x를 찾기 위한 특수해 k1=a2a1gk1과 같이 구할 수 있다.

 

이 식의 일반해는 다음과 같이 구할 수 있다.

 

m1k1m2k2=a2a1에서 m1m2/g를 더하고 빼면..

 

m1(k1+m2/g)m2(k2+m1/g)=a2a1

 

적당한 정수 p에 대하여... 위 과정을 p번 반복하면

 

m1(k1+pm2/g)m2(k2+pm1/g)=a2a1

 

그러므로, 일반해는 적당한 정수 p에 대하여.. k1=(a2a1)gk1+pm2g

 

이것을 x=m1k1+a1에 대입하면 다음과 같다.

 

x=m1(a2a1)gk1+pm1m2g+a1

 

여기서 g=gcd(m1,m2)이므로, m1m2g=lcm(m1,m2)이다.

 

그러므로 양변을 lcm(m1,m2)로 나누면...

 

xm1(a2a1)gk1+a1(mod(lcm(m1,m2)))

 

만약에 3개 이상의 연립합동식이 존재한다면..

 

2개씩 합치는 과정을 계속해서 반복하면 된다.

 

그런데, 합치다가 단 1번이라도 해가 존재하지 않는다면..

 

a2a1이 g의 배수가 아니라면... 전체 연립합동식의 해도 존재하지 않는다.

 

 

8. 연습문제

 

23062번: 백남이의 여행 준비의 준비 (acmicpc.net)

 

23062번: 백남이의 여행 준비의 준비

첫째 줄에 테스트 케이스의 개수 T 가 주어진다. (1T1,000,000) 각 테스트 데이터는 한 줄로 구성된다. 각 줄에는 여섯 개의 정수 A, B, C, a, b, c 가 주어진다. ($1 \le A,\;B,\;C\le1,000,000

www.acmicpc.net

 

중국인의 나머지 정리를 사용하는 문제

 

 

9. 풀이

 

결국 다음 연립합동식의 해를 구하면 된다.

 

xa(modA)  xb(modB)  xc(modC)

 

당연하지만 A,B,C가 서로소라는 보장은 없다.

 

그러므로 2개씩 합쳐서 반복적으로 합쳐나가면 되겠다.

 

위에서 구한 공식에 맞게 구해

 

from sys import stdin

#확장 유클리드 알고리즘
def extended_gcd(a,b):
    
    before_x = 1
    before_y = 0

    x = 0 
    y = 1

    while b != 0:
        
        q,r = a//b, a%b

        a,b = b,r

        before_x,x = x,before_x-x*q
        before_y,y = y,before_y-y*q
    
    return a,before_x,before_y

#유클리드 알고리즘
def gcd(a,b):
    
    while b != 0:
        
        a,b = b,a%b
    
    return a

T = int(stdin.readline())

for _ in range(T):
    
    A,B,C,a,b,c = map(int,stdin.readline().split())

    m = [0,A,B,C]
    s = [0,a,b,c]

    answer = 0

    for i in range(1,3):

        m_gcd = gcd(m[i],m[i+1])

        if (s[i+1] - s[i]) % m_gcd != 0:

            answer = -1
            break 

        M_gcd,k1,k2 = extended_gcd(m[i]//m_gcd,m[i+1]//m_gcd)
        
        #서로소가 아닐때, 중국인의 나머지 정리 점화식
        s[i+1] = m[i]*(s[i+1]-s[i])*k1//m_gcd + s[i]
        m[i+1] = m[i]*m[i+1]//m_gcd
    
    #s[-1]이 음수일수 있어서 최소의 양수로 바꾸는 과정
    #나머지연산 1번이면 가능
    if answer == 0:            
        
        answer = s[-1] % m[-1]
    
    print(answer)

 

공식에 맞게 반복적으로 합쳐나가는데,

 

합동식 xa(modA)에서 합치고 나서 얻은 a와 A를 저장해나가면 되겠다.

 

그런데 최종적으로 합치면, 반복문이 끝난건데..

 

그리고 나서 얻은 합동식은 xs[1](modm[1])

 

최소인 자연수 x를 찾아야하는데 그런데, s[-1]이 음수일수 있다.

 

이걸 양수로 바꾸는 방법은? 먼저 m[-1]을 양수가 될때까지 계속 더해준다.

 

    if answer == 0:
        
        if s[-1] < 0:
            
            k = 1

            answer = s[-1]

            while answer <= 0:

                answer += m[-1] 
        
        else:
            
            answer = s[-1]

    print(answer)

 

 

근데 s[-1]이 우연히 매우 작은 음수 -10000000000000000000000000000000이고 m[-1]이 매우 작은 양수 1이라면..

 

10000000000000000000000000000000번 더해야 겨우 0이 되는데

 

여기서 시간이 너무 오래걸린다

 

그것을 바꾸는 방법은 그냥 나머지 연산 1번만 하면 바로 양수로 바뀐다.

 

이 나머지 연산 테크닉은 많이 사용한 적이 있지

 

    if answer == 0:            
        
        answer = s[-1] % m[-1]
    
    print(answer)

 

참조

 

3. 중국인의 나머지 정리 :: rkm0959 (tistory.com)

 

3. 중국인의 나머지 정리

관련 코드는 github에서 찾아볼 수 있다. 이 주제도 이미 좋은 자료가 있습니다. https://casterian.net/archives/609 사실 3단원까지만 잘 이해해도 CP/PS 하는데는 문제가 없습니다. 레드까지는 문제 없을듯

rkm0959.tistory.com

 

 

중국인의 나머지 정리 (casterian.net)

 

The Casterian

Home

casterian.net

 

 

중국인의 나머지 정리 - 나무위키 (namu.wiki)

 

중국인의 나머지 정리 - 나무위키

나눗셈 정리에 의하여 m=q⋅lcm(a1,a2,⋯ ,an)+rm=q\cdot\text{lcm}\left(a_1,a_2,\cdots,a_n\right)+rm=q⋅lcm(a1​,a2​,⋯,an​)+r 을 만족하는 정수 q,rq,rq,r이 유일하게 존재한다 (0≤r

namu.wiki

 

728x90