모듈러 연산(나머지 연산)
Modular Arithmetic
정의
나눗셈 알고리즘
THE DIVISION ALGORITHM
Let aa be an integer and dd a positive integer. Then there are unique integers qq and rr, with 0≤r<d0≤r<d, such that a=dq+ra=dq+r.
- 정수 aa와 양의 정수 dd 에 대하여
- 0≤r<d0≤r<d 이고, a=dq+ra=dq+r 을 만족하는 qq와 rr 이 유일하게 존재한다.
- rr이 나머지. 즉 나머지는 0 보다 크다.
div 와 mod
In the equality given in the division algorithm, d is called the divisor, a is called the dividend, q is called the quotient, and r is called the remainder. This notation is used to express the quotient and remainder:
q=a div dr=amoddq=a div dr=amodd
mod
는 Java, Python 과 같은 프로그래밍 언어에서의%
와 같다.- 7=2×3+17=2×3+1 인 경우
- 2=7 div 32=7 div 3.
- 7mod2=17mod2=1.
음수의 나눗셈은 어떻게 될까?
- −7=2×(−4)+1−7=2×(−4)+1 로 생각한다.
- 나머지 rr은 0≤r<20≤r<2 여야 한다.
- −7mod2=1−7mod2=1.
모듈로 합동
congruent modulo
If aa and bb are integers and mm is a positive integer, then aa is congruent to bb modulo mm if mm divides a−ba−b. We use the notation a≡b (modm)a≡b (modm) to indicate that aa is congruent to bb modulo mm. We say that a≡b (modm)a≡b (modm) is a congruence and that mm is its modulus(plural moduli). If aa and bb are not congruent modulo mm, we write a≢b (modm)a≢b (modm).
- 정수 a,ba,b와 양의 정수 mm에 대하여
- a−ba−b 가 mm 으로 나누어 떨어진다면,
- aa와 bb는 모듈로 mm 합동(a is congruent to b modulo m)이다.
- a−ba−b 가 mm 으로 나누어 떨어진다면,
- 모듈로 mm 합동의 표기. a≡b (modm)a≡b (modm).
- 모듈로 mm 합동이 아님의 표기. a≢b (modm)a≢b (modm).
모듈로 합동의 필요충분조건
Let aa and bb be integers, and let mm be aa positive integer. Then a≡b (modm)a≡b (modm) if and only if amodm=bmodmamodm=bmodm.
- a≡b (modm)a≡b (modm) 의 필요충분조건은
- amodm=bmodmamodm=bmodm.
Let mm be a positive integer. The integers aa and bb are congruent modulo mm if and only if there is an integer kk such that a=b+kma=b+km.
- 정수 a,ba,b 가 모듈로 mm 합동이기 위한 필요충분조건은
- a=b+kma=b+km 을 만족하는 kk 의 존재이다.
- a−b=kma−b=km 이란 뜻이므로, 당연한 말이다.
모듈로 m 합동 클래스
The set of all integers congruent to an integer aa modulo mm is called the congruence class of aa modulo mm.
- 정수 aa와 모듈로 mm합동인 모든 정수의 집합을
- "aa와 모듈로 mm 합동 클래스"라 부른다.
- 예를 들어 {...−19,−9,1,11,21,31,...}{...−19,−9,1,11,21,31,...} 은 1과 모듈로 10 합동 클래스다.
- (10으로 나누면 나머지가 1 나오는 모든 정수의 집합).
모듈로 연산
Let mm be a positive integer.
If a≡b (modm)a≡b (modm) and c≡d (modm)c≡d (modm),
then a+c≡b+d (modm)a+c≡b+d (modm) and ac≡bd (modm)ac≡bd (modm).
- 양의 정수 m에 대하여
- a≡b (modm) 이고 c≡d (modm) 이면,
- a+c≡b+d (modm) 이고 ac≡bd (modm) 이다.
- 양의 정수 m에 대하여
- a와 b 가 m 으로 나눴을 때의 나머지가 같고, c와 d가 m으로 나눴을 때의 나머지가 같다면
- a+c≡b+d (modm) 이고 ac≡bd (modm) 이다.
Let m be a positive integer and let a and b be integers. Then (a+b)modm=((amodm)+(bmodm))modm
and
a×bmodm=((amodm)×(bmodm))modm.
산술 모듈로 m
Arithmetic Modulo m
- m 보다 작고 음이 아닌 정수의 집합, Zm={0,1,...,m−1}.
연산자 정의
- +m: Zm 의 두 원소에 대해 덧셈을 하고 modm 한다.
- a+mb=(a+b)modm.
- ⋅m: Zm 의 두 원소에 대해 곱셈을 하고 modm 한다.
- a⋅mb=(a×b)modm.
위의 두 연산자를 사용할 때 산술 모듈로 m 한다(we are said to be doing arithmetic modulo m)고 말한다.
위의 두 연산자는 다음을 만족한다.
- a∈Zm,b∈Zm,c∈Zm이면,
- 폐쇄(closure): a+mb∈Zm이고 a⋅mb∈Zm이다.
- 결합성(associativity): (a+mb)+mc=a+m(b+mc) 이고 (a⋅mb)⋅mc=a⋅m(b⋅mc) 이다.
- 교환성(commutativity): a+mb∈Zm이고 a⋅mb∈Zm이다.
- 항등원(identity elements): 0과 1은 각각 모듈로 m 합과 곱의 항등원이다.
- 덧셈법 역원(additive inverses): a≠0 이고 a∈Zm 이면, m−a 는 a 모듈로 m 의 덧셈법 역원이다.
- a+m(m−a)=0 이고, 0+m0=0 이다.
- 분배성(distributivity): a⋅m(b+mc)=(a⋅mb)+m(a⋅mc) 이고, (a+mb)⋅mc=(a⋅mc)+m(b⋅mc) 이다.
모듈로 m 합을 갖는 Zm 은 교환 그룹(commutative group) 이라 불린다.
두 연산을 모두 갖는 Zm 은 교환 고리(commutative ring) 이라 불린다.
알고리즘
기본적인 몫과 나머지 구하기
- 다음 함수는 몫과 나머지를 계산하는 기본적인 알고리즘을 js로 구현한 것이다.
- 이 알고리즘은 O(qloga) 의 비트 동작을 사용한다.
// a: integer
// b: positive integer
function div(a, d) {
let q = 0
let r = Math.abs(a)
// 나머지가 d 보다 작아질 때까지 a 에서 d 를 계속 뺀다
while (r >= d) {
r -= d // 빼기가 끝난 다음 남은 것이 나머지
q += 1 // 빼기를 한 횟수가 몫
}
if (a < 0 && r > 0) {
r = d - r
q = -(q + 1)
}
return {
div: q, // 몫
mod: r, // 나머지
}
}
실행 결과는 다음과 같다.
div(10, 3) // { div: 3, mod: 1 }
div(17, 3) // { div: 5, mod: 2 }
div(-17, 3) // { div: -6, mod: 1 }
거듭제곱 수의 나머지 구하기
bnmodm 의 나머지를 구할 때 bn 이 꽤 큰 수라면 계산하기 곤란할 수 있다.
가령 730mod661 을 구한다고 하자.
- bn=730=22,539,340,290,692,258,087,863,249 이다.
- JavaScript의 경우
Number.MAX_SAFE_INTEGER
가 9,007,199,254,740,991 이다. - Java의 경우
Long.MAX_VALUE
가 9,223,372,036,854,775,807 이다.
- JavaScript의 경우
이런 경우라면 730 을 계산한 다음 나머지를 구하는 방법을 사용하면 오버플로가 발생할 것이다.
따라서 위의 모듈로 연산에서 배운 공식을 사용하여 문제를 분할해 해결하도록 하자.
a×bmodm=((amodm)×(bmodm))modm.730mod661=(715×715)mod661=((715mod661)×(715mod661))mod661=(21×21)mod661=441
- 답은 441 이다.
- 중간에 715mod661을 계산하는 과정은 생략했다.
이 방법을 사용하면 730mod661 은 715mod661 을 구하면 풀리는 문제가 된다.
한편, 715mod661=((710mod661)×(75mod661))mod661 이므로…
730mod661 은 710mod661,710mod661 을 구하면 풀리는 문제인 것이다.
이를 반복하면 아무리 거듭제곱의 수가 많아도 오버플로하지 않도록 관리하면서 계산하는 것이 가능해진다.
이진법을 사용해 거듭제곱 수의 나머지 구하기
다음 알고리즘은 바로 위에서 문제를 분할해 푸는 방법을 응용한 것이다.
이 방법에서는 bnmodm의 n을 이진법으로 보고, 2의 거듭제곱으로 쪼개서 순차적으로 계산한다.
// b: integer
// n: list (n의 이진수 표현)
// m: integer
function mod_exponentiation(b, n, m) {
let x = 1
let power = mod(b, m)
for (let i = 0; i < n.length; i++) {
if (n[i] == 1) {
x = mod(x * power, m)
}
power = mod(power * power, m)
}
return x
}
가령 3644mod645 를 구한다고 하자.
645=10100001002 이므로 n = [0,0,1,0,0,0,0,1,0,1]
이 된다.
루프를 돌면서 각 값의 테이블을 만들어 보면 다음과 같다.
i | n[i] | x | power |
---|---|---|---|
1 | 3mod645=3 | ||
0 | 0 | 1 | (32)mod645=9 |
1 | 0 | 1 | (92)mod645=81 |
2 | 1 | (1×81)mod645=81 | (812)mod645=111 |
3 | 0 | 81 | (1112)mod645=66 |
4 | 0 | 81 | (662)mod645=486 |
5 | 0 | 81 | (4862)mod645=126 |
6 | 0 | 81 | (1262)mod645=396 |
7 | 1 | (81×396)mod645=471 | (3962)mod645=81 |
8 | 0 | 471 | (812)mod645=111 |
9 | 1 | (471×111)mod645=36 |
재귀 알고리즘으로 거듭제곱 수의 나머지 구하기
a×bmodm=((amodm)×(bmodm))modm.
위의 식을 응용하면 다음과 같은 방법으로 거듭제곱 수의 나머지를 구할 수 있다.
- n 이 짝수인 경우.
- bnmodm=(bn2modm)2modm.
- n 이 홀수인 경우.
- bnmodm=((b⌊n2⌋modm)2modm×bmodm)modm.
다음은 이 알고리즘을 자바스크립트 코드로 작성한 것이다.
// b^n mod m
function mpower(b, n, m) {
if (n == 0) {
return 1;
}
if (n % 2 == 0) {
const next = mpower(b, half(n), m);
return square(next) % m;
}
const next = mpower(b, half(n), m);
return ((square(next) % m) * (b % m)) % m;
}
function half(n) {
return parseInt(n / 2, 10);
}
function square(n) {
return n * n;
}
참고문헌
- Rosen의 이산수학 / Kenneth H. Rosen 저 / 공은배 등저 / 한국맥그로힐(McGraw-Hill KOREA) / 2017년 01월 06일