유클리드 호제법이란?
유클리드 호제법은 알고리즘 기초 문제 중 하나인 최대공약수 구하기를 해결하기 위한 방법 중 하나로 적은 연산으로도 최대공약수를 구할 수 있다. 여기서 호제법이란 두 수가 서로를 나누어 원하는 수를 얻는 알고리즘을 나타낸다. 이때 2개의 자연수 a, b에 대해서 a를 b로 나눈 나머지를 r이라 하면(단, a>b), a와 b의 최대공약수는 b와 r의 최대공약수와 같다.
예를 들어, 99와 57의 최대공약수를 유클리드 호제법을 이용하여 구한다면, 다음과 같다.
99 % 57 = 42 → 나머지(42) != 0
57 % 42 = 15 → 나머지(15) != 0
42 % 15 = 12 → 나머지(12) != 0
15 % 12 = 3 → 나머지(3) != 0
12 % 3 = 0 → 나머지(0) == 0 → 작업 종료
최종적으로 99와 57의 최대공약수는 12와 3의 최대공약수인 3과 같게 된다.
소스코드 구현 및 성능 비교
유클리드 호제법을 이용하여 최대공약수를 구하는 로직을 자바로 구현하면 다음과 같다.
import java.util.*;
class Main {
public static int getGcd(int a, int b) {
int temp;
while (b != 0) {
temp = a % b;
a = b;
b = temp;
}
return a;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt(), b = sc.nextInt();
System.out.println(getGcd(a, b));
System.out.println((a * b) / getGcd(a, b)); // 참고 : a * b = LCM(최소공배수) * GCD(최대공약수)
}
}
이를 다음과 같이 간단하게 재귀로 나타낼 수도 있다.
private static int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
하지만 두 수의 최대공약수를 구하기 위해 반드시 유클리드 호제법을 이용할 필요는 없다. 좀 더 직관적인 방법으로는 두 수 중 적은 숫자를 기준으로(a > b → b) 각각의 수(a, b)를 나누었을 때 나머지가 0이 나올때까지 1씩 감소시키는 것이다. 자바 소스코드로 나타내면 다음과 같다.
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int a = sc.nextInt(), b = sc.nextInt();
for (int i = b; i >= 1; i--) { // a > b 가정
if (a % i == 0 && b % i == 0) {
System.out.print(i);
break;
}
}
}
}
하지만 위 알고리즘의 경우 시간 성능이 최선의 경우에는 운이 좋게 한번만에 연산이 끝날 수도 있지만 최악의 경우에는 1이 될때까지 연산이 계속될 수도 있는 단점이 있다.
아래 예시에서는 위 알고리즘을 통해 99와 57의 최대공약수를 구했더니 무려 55번의 연산 작업으로 정답을 구했다. 유클리드 호제법을 이용했을 경우 5번의 연산 작업으로 정답을 구한 것과 상당히 비교된다.
99 % 57 != 0 && 57 % 57 == 0 → false(불일치)
99 % 56 != 0 && 57 % 56 != 0 → false(불일치)
99 % 55 != 0 && 57 % 55 != 0 → false(불일치)
...
99 % 3 == 0 && 57 % 3 == 0 → true(일치)
물론 운이 좋게 30과 10의 최대공약수를 구할 때는 한 번의 연산으로 최대공약수를 구할 수 있겠지만,(물론 유클리드 호제법을 이용한 경우에도 한 번의 연산으로 가능) 30과 11이라는 숫자가 주어진 경우 11번의 연산 작업을 해야 정답을 구할 수 있게 된다.
결론, 왠만하면 최대공약수를 구하는 알고리즘을 구할 때는 유클리드 호제법을 이용해보자!
참고 자료
'Algorithm > Basic' 카테고리의 다른 글
이분 탐색, Lower Bound와 Upper Bound - Java (0) | 2022.08.18 |
---|---|
[배열 연습] 2차원 배열 뱀 채우기 - Java (0) | 2022.08.03 |
소수를 판별하는 방법들 - Java (0) | 2022.06.11 |
C언어로 연결리스트 자료 구조 구현해보기 (0) | 2022.01.09 |
알고리즘의 시간 복잡도와 빅오 표기법 (0) | 2021.12.01 |