최대공약수와 최소공배수 (GCD, LCM)
한눈에 보기
유클리드 호제법으로 최대공약수를 O(log n)에 구하는 원리를 도형과 나머지 연산으로 풀어 설명한다. 최소공배수 공식과 오버플로 방지, 기약분수와 주기 문제 같은 활용 사례를 정리한다.
최종 업데이트 2026.09.17
최대공약수(GCD)는 두 수를 모두 나누어떨어지게 하는 가장 큰 수, 최소공배수(LCM)는 두 수의 공통 배수 중 가장 작은 수다. 둘 다 유클리드 호제법 하나로 해결되며, 그 구현은 세 줄이면 끝난다.
코딩테스트에서는 단독 문제보다 다른 문제의 부품으로 등장한다. 분수를 기약분수로 줄이기, 주기가 다른 사건이 다시 겹치는 시점 구하기, 격자에서 두 점을 잇는 선분 위의 격자점 개수 세기 같은 문제가 전부 GCD로 풀린다.
유클리드 호제법의 직관
호제법의 핵심 아이디어는 이 한 줄이다.
gcd(a, b) = gcd(b, a % b)
왜 성립할까. a와 b의 공약수는 a − b도 나눈다. 뺄셈을 여러 번 반복한 것이 나머지 연산이므로, a와 b의 공약수 집합은 b와 a % b의 공약수 집합과 정확히 같다. 집합이 같으니 그중 최대값도 같다.
이것을 도형으로 보면 더 선명하다. 가로 a, 세로 b인 직사각형을 정사각형 타일로 빈틈없이 채울 때 가능한 가장 큰 타일의 한 변이 곧 gcd다.
나머지 연산으로 따라가기
실제 계산은 나머지를 반복해서 구하는 것이 전부다. b가 0이 되는 순간의 a가 답이다.
최소공배수는 덤이다
GCD를 구하면 LCM은 공식 하나로 나온다.
lcm(a, b) = a × b ÷ gcd(a, b)
두 수를 곱하면 공통 인수가 두 번 들어가므로, 한 번 나눠주면 최소공배수가 된다. 예를 들어 24 × 18 = 432이고 gcd가 6이므로 lcm은 72다.
구현할 때는 a * b // gcd(a, b)보다 a // gcd(a, b) * b가 안전하다. 곱셈을 먼저 하면 중간값이 커져서 C++이나 자바에서 오버플로가 날 수 있기 때문이다. 나눗셈을 먼저 해도 a는 gcd로 반드시 나누어떨어지므로 결과는 같다.
시간 복잡도
| 구분 | 복잡도 | 설명 |
|---|---|---|
| 유클리드 호제법 | O(log(min(a, b))) | 두 단계마다 값이 최소 절반으로 준다 |
| 공간 (반복) | O(1) | 변수 두 개 |
| 공간 (재귀) | O(log n) | 호출 스택 |
| 소인수분해 방식 | O(√n) | 훨씬 느리다. 쓸 이유가 없다 |
왜 로그인가? 두 번의 나머지 연산을 거치면 큰 쪽 값이 반드시 절반 이하로 줄어들기 때문이다. 값이 10억이어도 60번이면 끝난다. 최악의 경우는 연속한 피보나치 수를 넣었을 때로, 이때 단계 수가 가장 많아진다.
구현 코드 예시
def gcd(a, b):
"""유클리드 호제법 — 반복문 버전."""
while b:
a, b = b, a % b
return a
def gcd_recursive(a, b):
"""재귀 버전. 한 줄이면 끝난다."""
return a if b == 0 else gcd_recursive(b, a % b)
def lcm(a, b):
return a // gcd(a, b) * b # 나눗셈 먼저 — 오버플로 방지
# 여러 수의 GCD와 LCM은 두 개씩 접어 나가면 된다
from functools import reduce
def gcd_all(nums):
return reduce(gcd, nums)
def lcm_all(nums):
return reduce(lcm, nums)
파이썬은 표준 라이브러리가 이미 제공한다. 실전에서는 직접 구현하지 말고 이쪽을 쓰는 편이 안전하고 빠르다.
import math
math.gcd(48, 18) # 6
math.lcm(24, 18) # 72 (Python 3.9+)
math.gcd(12, 18, 24) # 6 — 인자 여러 개 가능 (Python 3.9+)
어디에 쓰이는가
- 기약분수 만들기: 분자와 분모를 gcd로 나눈다. 분수 연산 문제의 마무리에 항상 등장한다.
- 주기가 겹치는 시점: 3일마다, 5일마다 오는 두 사건이 다시 같은 날 겹치는 것은 lcm(3, 5) = 15일 후다.
- 격자점 개수: 원점에서 (a, b)로 가는 선분 위의 격자점은 gcd(a, b) + 1개다.
- 비율 비교: 두 비율이 같은지 판단할 때 각각 기약분수로 만들어 비교한다.
- 여러 수를 같은 크기로 나누기: "가능한 큰 같은 크기로 나눠 담기" 유형이 곧 gcd다.
자주 하는 실수
- LCM에서 곱셈을 먼저 한다.
a * b가 자료형 범위를 넘을 수 있다. 파이썬은 안전하지만 C++·자바로 옮길 때 바로 문제가 된다. 습관적으로 나눗셈을 먼저 쓴다. - a와 b의 순서를 신경 쓴다.
gcd(18, 48)도 정상 동작한다. 첫 단계에서18 % 48 = 18이 되어 자동으로 자리가 바뀐다. 미리 정렬할 필요가 없다. - 0을 처리하지 않는다.
gcd(a, 0) = a가 정의상 맞고 위 코드도 그렇게 동작한다. 하지만lcm(a, 0)은 0으로 나누기가 되어 터진다. 0이 들어올 수 있는 문제라면 따로 막아야 한다. - 음수를 그대로 넣는다. 파이썬의
%는 음수에서 부호가 달라 결과가 이상해진다.abs()를 씌우고 시작한다. - 소인수분해로 구현한다. 동작은 하지만 O(√n)이라 훨씬 느리고 코드도 길다. 호제법을 두고 돌아갈 이유가 없다.
면접에서 이렇게 물어본다
- 유클리드 호제법의 원리, 즉
gcd(a, b) = gcd(b, a % b)가 성립하는 이유를 설명해 보라. - 호제법의 시간 복잡도가 로그인 이유는 무엇인가.
- 최소공배수를 최대공약수로 구하는 공식을 유도해 보라.
- LCM 계산에서 오버플로를 피하는 방법은 무엇인가.
- 세 개 이상의 수에 대한 GCD는 어떻게 구하는가.
- 소인수분해로 구하는 방법과 비교했을 때 호제법의 장점은 무엇인가.
알고리즘에서 같이 읽기
- 이분 탐색 (Binary Search)정렬된 배열에서 탐색 범위를 절반씩 줄여 O(log n)에 값을 찾는 이분 탐색의 원리와 동작 과정을 그림으로 정리했다. lower bound와 upper bound, 코딩테스트 단골인 파라메트릭 서치와 회전 정렬 배열, 구현 코드와 자주 하는 실수까지 다룬다.
- 비트마스크 (Bitmask)정수의 각 비트로 집합의 포함 여부를 표현하는 비트마스크 기법을 다룬다. 기본 비트 연산 정리, 부분집합 열거, 외판원 순회 동적 계획법 구현과 주의점을 정리한다.
- DFS와 BFS (Depth First Search, Breadth First Search)그래프의 모든 정점을 방문하는 DFS와 BFS의 원리와 방문 순서 차이를 스택과 큐로 설명하고, O(V + E) 복잡도, 구현 코드, 문제 유형별 선택 기준을 정리한다.
- 다익스트라 알고리즘 (Dijkstra's Algorithm)음수 간선이 없는 그래프에서 한 정점으로부터의 최단 거리를 구하는 다익스트라 알고리즘의 그리디 원리, 우선순위 큐 구현, 복잡도 비교와 한계를 정리한다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기