포스트

그리디 — 눈앞의 최선만 집었는데 전체가 최적일 때

그리디 알고리즘은 언제 통하고 언제 실패할까? 교환 논법과 활동 선택, 그리고 허프만 코딩이 빈도로 최적 압축을 만드는 과정을 애니메이션으로 정리합니다.

그리디 — 눈앞의 최선만 집었는데 전체가 최적일 때

가장 단순한 전략, 가장 위험한 전략

그리디(greedy)는 알고리즘 중 가장 직관적입니다. 매 순간 그 자리에서 가장 좋아 보이는 선택을 하고 절대 되돌아보지 않습니다. 거스름돈을 줄 때 큰 동전부터 집는 그 본능 그대로입니다. 빠르고(보통 정렬 + 한 번 훑기) 코드도 짧습니다.

문제는 위험하다는 것. 매 순간의 최선이 전체의 최선을 보장하지 않습니다. 그래서 그리디의 진짜 어려움은 “어떻게 푸느냐”가 아니라 “이 문제에서 그리디가 정답을 주는가“를 증명하는 데 있습니다.

그리디가 깨지는 순간 — 거스름돈 반례

동전이 {1, 4, 5}이고 8원을 거슬러 준다고 합시다. 큰 것부터 집는 그리디는 5 + 1 + 1 + 1 = 4개. 하지만 정답은 4 + 4 = 2개입니다. 그리디가 첫 선택(5)에 갇혀 더 나은 전체 해를 놓쳤습니다.

flowchart TD
    S["8원 거슬러주기 · 동전 {1,4,5}"]
    S --> G["그리디: 5 → 1 → 1 → 1<br/>동전 4개 ✗"]
    S --> O["최적(DP): 4 → 4<br/>동전 2개 ✓"]

이처럼 그리디가 통할지 아닐지는 동전 체계에 달려 있습니다(원화·달러처럼 잘 설계된 체계는 그리디가 맞음). 그래서 그리디를 쓰려면 반드시 정당성을 따져야 하고, 안 되면 동적계획법으로 모든 경우를 봐야 합니다.

언제 그리디가 통하는가 — 두 개의 증명 도구

그리디의 정당성은 보통 두 가지로 증명합니다.

  • 탐욕적 선택 속성(greedy-choice property): 첫 그리디 선택을 포함하는 최적해가 반드시 존재한다.
  • 교환 논법(exchange argument): 임의의 최적해를 가져와, 그리디 선택과 다른 부분을 그리디 쪽으로 교환해도 해가 나빠지지 않음을 보인다 → 그리디 해도 최적.

수학적으로 이 구조가 보장되는 대상이 매트로이드(matroid) 입니다. 문제가 매트로이드로 모델링되면 “가중치 큰 것부터 그리디”가 항상 최적임이 증명돼 있습니다 — 최소 신장 트리의 크루스칼이 그 대표 사례입니다.

활동 선택 — 끝나는 시간이 빠른 것부터

회의실 하나에 겹치지 않게 회의를 최대한 많이 넣는 문제. 정답 그리디는 “가장 일찍 끝나는 회의부터” 집는 것입니다. 직관: 빨리 끝낼수록 뒤에 남는 시간이 많아 더 많은 회의를 담을 여지가 생깁니다. 아래는 끝나는 시간 순으로 정렬한 회의들 중, 겹치지 않는 것을 차례로 채택(초록)하고 겹치는 것은 버리는(회색) 과정입니다.

끝나는 시간 순 정렬 → 겹치지 않으면 채택(초록), 겹치면 버림(회색) 시간 → A 채택 B 겹침 C 채택 D 겹침 E 채택

정렬에 $O(n\log n)$, 한 번 훑기에 $O(n)$. 교환 논법으로 “가장 일찍 끝나는 회의를 포함하는 최적해가 존재함”을 보이면 정당성이 끝납니다.

허프만 코딩 — 자주 나오는 글자에 짧은 비트를

그리디의 가장 우아한 승리는 허프만 코딩(데이터 압축)입니다. 모든 글자에 같은 비트 수를 쓰는 대신, 자주 나오는 글자엔 짧은 코드, 드문 글자엔 긴 코드를 줍니다. 핵심 제약은 접두부호(prefix code) — 어떤 코드도 다른 코드의 접두사가 되면 안 됩니다(그래야 구분자 없이 디코딩 가능).

그리디 전략은 명쾌합니다. 빈도가 가장 작은 두 노드를 골라 합치기를 반복합니다. 합친 노드의 빈도는 두 자식의 합이고, 이를 다시 후보에 넣습니다(우선순위 큐가 딱 맞는 자리). 아래는 빈도 {A:5, B:2, C:1, D:1}에서 트리가 아래에서 위로 자라는 모습입니다.

C:1 D:1 B:2 A:5 2 4 9 합칠 때마다 최소 빈도 두 개 선택 → 드문 글자가 깊이↑

트리에서 왼쪽 간선을 0, 오른쪽을 1로 읽으면 각 글자의 코드가 나옵니다. A는 얕아서 짧고(예: 1), C·D는 깊어서 길죠(예: 000, 001). 평균 코드 길이 $\sum p_i \cdot \text{len}_i$가 최소가 되는 것이 허프만의 최적성이고, 이는 교환 논법으로 증명됩니다.

그리디는 도처에 있다

알고리즘그리디 선택 기준정당성
다익스트라미확정 중 최단 거리 정점 확정음수 간선 없을 때
크루스칼/프림가장 가벼운 안전한 간선매트로이드/컷 정리
허프만최소 빈도 두 노드 병합교환 논법
활동 선택가장 일찍 끝나는 일정교환 논법

다익스트라가 그리디인데 음수 간선에서 깨지는 것이 좋은 교훈입니다 — 그리디는 “되돌아보지 않음”이 전제라, 나중에 더 싼 경로가 나타날 수 있으면(음수 간선) 무너집니다.

프로덕션에서 마주치는 함정

함정증상해법
정당성 미검증일부 입력에서 최적 아님교환 논법으로 증명 or DP/완전탐색
정렬 기준 오류“가장 좋아 보이는”의 정의가 틀림무엇을 최소/최대화하는지 명확히
동점 처리같은 우선순위에서 임의 선택이 결과 좌우2차 정렬 키 명시
그리디≠근사 혼동최적인 줄 알았는데 근사일 뿐근사비 보장 따로 확인

면접/리뷰 단골 질문

  • Q. 그리디가 통하는 조건? → 탐욕적 선택 속성 + 최적 부분구조. 매트로이드로 모델링되면 보장.
  • Q. 그리디 정당성 증명법? → 교환 논법: 최적해를 그리디 선택 쪽으로 교환해도 나빠지지 않음을 보임.
  • Q. 활동 선택에서 왜 ‘끝 시간’으로 정렬? → 빨리 끝낼수록 뒤 자원이 많이 남아 더 많이 담을 수 있음. 시작/길이 기준은 반례 존재.
  • Q. 허프만이 최적인 이유? → 최소 빈도 두 글자가 가장 깊은 형제가 되는 최적해가 존재(교환 논법). 접두부호라 디코딩 모호성 없음.
  • Q. 그리디 vs DP 선택 기준? → 그리디 정당성이 증명되면 그리디(빠름). 안 되면 DP로 모든 경우.

정리

  • 그리디는 매 순간 국소 최선을 집고 되돌아보지 않는다 — 빠르지만 항상 옳진 않다.
  • 정당성은 탐욕적 선택 속성 + 교환 논법(또는 매트로이드 구조)으로 증명해야 한다.
  • 허프만 코딩은 최소 빈도 두 노드를 반복 병합해 최적 접두부호를 만든다 — 그리디의 우아한 승리.
  • 다익스트라·MST도 그리디. 단 다익스트라는 음수 간선에서 깨진다(되돌아보지 않음의 대가).

다음 글은 문자열 매칭(KMP·라빈-카프)입니다. 이전 글은 동적계획법 — 그리디로 안 되는 문제를 어떻게 표로 정복하는지 다뤘습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

시리즈 · 알고리즘 A-Z (20편)

  1. 복잡도 분석 — Big-O, 점근 표기, 그리고 분할상환이라는 거짓말 같은 진실
  2. 배열과 연결 리스트 — 같은 O(n)인데 왜 한쪽이 수십 배 느린가
  3. 스택·큐·힙 — 접근 규칙 하나가 만드는 강력함
  4. 해시 테이블 — 평균 O(1)의 비밀과 그 평균이 깨지는 순간
  5. 비교 정렬 — 퀵·머지·힙, 그리고 Ω(n log n)이라는 넘을 수 없는 벽
  6. 선형 시간 정렬 — 비교를 버리면 Ω(n log n) 벽이 사라진다
  7. 이진 탐색 — 반씩 버리는 마법, 그리고 "답을 이분하라"
  8. 재귀와 분할정복 — 콜스택에 쌓이는 신뢰, 마스터 정리로 푸는 비용
  9. 이진 탐색 트리와 균형 — 한쪽으로 쏠리는 트리를 회전으로 바로 세우기
  10. 그래프 순회 — BFS의 물결, DFS의 깊이, 그리고 위상정렬
  11. 최단 경로 — 다익스트라의 확신, A*의 직감, 벨만-포드의 인내
  12. 최소 신장 트리와 Union-Find — 가장 싸게 잇기, 그리고 거의 O(1)의 마법
  13. 동적계획법 — 같은 계산을 두 번 하지 않는다는 단 하나의 원칙
  14. 그리디 — 눈앞의 최선만 집었는데 전체가 최적일 때 지금 보는 글
  15. 문자열 매칭 — 어긋났을 때 얼마나 똑똑하게 다시 시작하는가
  16. 확률적 자료구조 — 정확함을 메모리와 맞바꾸는 기술 (Bloom·HyperLogLog·Count-Min)
  17. 일관된 해싱 — 노드가 들고날 때 데이터를 통째로 옮기지 않는 법
  18. 합의 알고리즘 — 죽는 서버들 사이에서 하나의 진실을 정하는 법 (Raft·Paxos)
  19. 레이트 리미팅과 스트리밍 알고리즘 — 폭주를 막고, 끝없는 흐름에서 표본을 뽑다
  20. 의미로 검색하다 — 벡터 검색(ANN·HNSW)과 PageRank, 그리고 시리즈의 끝