유클리드 호제법 (Euclidean Algorithm) 은 두 정수 a, b 의 최대공약수 (GCD, Greatest Common Divisor) 를 O(log min(a, b)) 시간에 구하는 알고리즘. 기원전 300년경 유클리드 원론 7권에 등장. 재귀식 gcd(a, b) = gcd(b, a mod b) 가 핵심.
핵심 통찰: gcd(a, b) = gcd(b, a mod b). 나머지 연산으로 숫자가 빠르게 줄어듦.
시각화
핵심 아이디어
나머지 연산과 GCD
a = b·q + r (0 ≤ r < b)gcd(a, b) = gcd(b, r)증명: d = gcd(a, b) 이면, d | a, d | b. a = b·q + r 이므로 r = a - b·q, 따라서 d | r. 즉 d 는 b, r 의 공약수. 역으로 d' = gcd(b, r) 이면, d' | b, d' | r. a = b·q + r 이므로 d' | a. 즉 d' 는 a, b 의 공약수. 따라서 gcd(a, b) = gcd(b, r).
재귀 종료
gcd(a, 0) = agcd(a, b) = gcd(b, a mod b)
b 가 0 이 될 때까지 반복. 최악 O(log min(a, b)) 단계.
확장 유클리드 (Extended Euclidean)
gcd(a, b) = d 뿐 아니라, 베주 항등식 (Bézout’s identity)a·x + b·y = d 를 만족하는 정수 x, y 도 구함.
extgcd(a, b): if b == 0: return (a, 1, 0) // gcd, x, y d, x', y' = extgcd(b, a mod b) x = y' y = x' - (a // b) * y' return (d, x, y)
mod 역원 계산 (a·x ≡ 1 (mod m) 의 x) 에 필수.
알고리즘
재귀 (간결)
gcd(a, b): if b == 0: return a return gcd(b, a mod b)
반복 (스택 X)
gcd(a, b): while b != 0: a, b = b, a mod b return a
확장 유클리드
extgcd(a, b): if b == 0: return (a, 1, 0) d, x1, y1 = extgcd(b, a % b) x = y1 y = x1 - (a // b) * y1 return (d, x, y)
구현
// 재귀 + 반복 gcd, extgcd#include <bits/stdc++.h>using namespace std;using ll = long long;ll gcd_recursive(ll a, ll b) { return b ? gcd_recursive(b, a % b) : a;}ll gcd_iterative(ll a, ll b) { while (b) { ll t = a % b; a = b; b = t; } return a;}// Extended Euclidean: ax + by = gcd(a, b)tuple<ll, ll, ll> extgcd(ll a, ll b) { if (b == 0) return {a, 1, 0}; auto [d, x1, y1] = extgcd(b, a % b); return {d, y1, x1 - (a / b) * y1};}int main() { ll a, b; cin >> a >> b; cout << "gcd(rec): " << gcd_recursive(a, b) << "\n"; cout << "gcd(iter): " << gcd_iterative(a, b) << "\n"; auto [d, x, y] = extgcd(a, b); cout << "extgcd: " << d << ", x=" << x << ", y=" << y << "\n"; cout << "check: " << a << "*" << x << " + " << b << "*" << y << " = " << a*x + b*y << "\n";}
# math.gcd 내장 또는 직접 구현import mathdef gcd_recursive(a, b): return gcd_recursive(b, a % b) if b else adef gcd_iterative(a, b): while b: a, b = b, a % b return adef extgcd(a, b): if b == 0: return a, 1, 0 d, x1, y1 = extgcd(b, a % b) x = y1 y = x1 - (a // b) * y1 return d, x, yimport sysinput = sys.stdin.readlinea, b = map(int, input().split())print(f"gcd(rec): {gcd_recursive(a, b)}")print(f"gcd(iter): {gcd_iterative(a, b)}")print(f"math.gcd: {math.gcd(a, b)}")d, x, y = extgcd(a, b)print(f"extgcd: {d}, x={x}, y={y}")print(f"check: {a}*{x} + {b}*{y} = {a*x + b*y}")
// 재귀 + 반복, BigInteger.gcd() 도 사용 가능import java.util.*;import java.io.*;public class Main { static long gcdRecursive(long a, long b) { return b == 0 ? a : gcdRecursive(b, a % b); } static long gcdIterative(long a, long b) { while (b != 0) { long t = a % b; a = b; b = t; } return a; } static class ExtGcdResult { long d, x, y; ExtGcdResult(long d, long x, long y) { this.d = d; this.x = x; this.y = y; } } static ExtGcdResult extgcd(long a, long b) { if (b == 0) return new ExtGcdResult(a, 1, 0); ExtGcdResult r = extgcd(b, a % b); long x = r.y; long y = r.x - (a / b) * r.y; return new ExtGcdResult(r.d, x, y); } public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); long a = Long.parseLong(st.nextToken()); long b = Long.parseLong(st.nextToken()); System.out.println("gcd(rec): " + gcdRecursive(a, b)); System.out.println("gcd(iter): " + gcdIterative(a, b)); ExtGcdResult r = extgcd(a, b); System.out.println("extgcd: " + r.d + ", x=" + r.x + ", y=" + r.y); System.out.println("check: " + a + "*" + r.x + " + " + b + "*" + r.y + " = " + (a*r.x + b*r.y)); }}
💬 댓글