[Algorithm] 유클리드 호제법; 최대공약수 찾기

알고리즘을 풀다보면 최대공약수를 구할 수 있는지 요구하는 문제가 많다. 딱히 알고리즘이 아니더라도, 코딩을 하다보면 최대공약수가 필요한 경우가 많다.

이럴때 쓰이는 알고리즘이 유클리드 호제법이다.

 

\(a, b ∈ Z\)이고 \(a≥b\)라면 \(a = bq + r\)를 만족하는 유일한 정수 \(q, r\)이 존재한다.\((0≤r<b)\)

\(GCD(a, b) = d\)일 때, \(a\)와 \(b\)는 \(a = αd, b = βd\)로 표현이 가능하며 이 때 \(α\)와 \(β\)는 서로소이다.

위의 식 \(a = bq + r\)은 \(αd = βdq + r\)로 표현할 수 있으며, \(r = (α – βq)d\)가 된다.

\(b = βd, r = (α – βq)d\)에 의해 \(d\)는 \(r\)과 \(b\)의 공약수 중 하나라는 것을 알 수 있다.  이 때 \(b\)와 \(r\)의 최대공약수를 \(c\)라고 하면 \(c≥d\)임을 알 수 있다.

\(b = mc, r = nc\) (\(m, n\)은 서로소)라 하면 위의 식 \(a = bq + r\)은 \(a = mcq + nc = (mq + n)c\)이다.

위에서 구한 두 식 \(b = mc\)와 \(a = (mq + n)c\)에 의해 \(c\)는 \(a, b\)의 공약수 중 하나라는 것을 알 수 있다.

따라서 \(c\)는 \(a, b\)의 최대공약수 \(d\)보다 같거나 작아야 한다. \((d≥c)\)

위에서 구한 두 식 \(c≥d, d≥c\)에 의해 \(c=b\)가 성립하고, 따라서 \(GCD(a, b) = GCD(b, r)\)이 성립한다.

 

예시로 1071과 1029의 최대공약수를 구해보자.

위의 증명에 따라 GCD(1071, 1029) = GCD(1029, 42) = GCD(42, 21) = GCD(21, 0)이 성립하고 GCD(X, 0) = X라는 성질에 의해 GCD(21, 0) = 21이다.

따라서 1071과 1029의 최대공약수는 21이다.

 

이 식을 코드로 표현하면 아래와 같다. gcd함수의 매개변수 x, y의 대소관계는 중요치 않은데, x < y라면 첫번째 루프를 돌면서 swap이 되므로, x > y가 되기 때문이다.

#include <stdio.h>

int gcd(int a, int b) {
	int temp;

	while (b) {
		temp = a % b;
		a = b;
		b = temp;
	}
	
	return a;
}

int main() {
	int a = 1071, b = 1029;

	printf("gcd(%d, %d) = %d\n", a, b, gcd(a, b));

	return 0;
}
gcd(1071, 1029) = 21

 

유클리드 호제법의 나머지 연산은 최악의 경우라도 b의 자릿수의 5배를 넘지 않는다는 것이 라메의 정리에 의해 증명되었다.

 

따라서 재귀적으로 프로그래밍을 해도 크게 문제가 되지 않는다.

int gcd(int a, int b){
	return b ? gcd(b, a%b) : a;
}