그리디 알고리즘 (탐욕법, Greedy)

2025. 5. 8. 13:59·PS/알고리즘
728x90

그리디 알고리즘 (Greedy Algorithm)

눈앞의 이익만 우선 추구하는 알고리즘을 총칭하여 `그리디 알고리즘`이라 한다.

그리디 알고리즘은 대부분의 경우 뛰어난 결과를 도출하지 못하지만, 드물게 최적해를 보장하는 경우도 있다.

📌 그리디 알고리즘은 최적화 문제를 대상으로 한다.
최적해를 찾을 수 있으면 그것을 목표로 삼고, 찾기 어려운 경우에는 주어진 시간 내에 그런대로 괜찮은 해를 찾는 것을 목표로 한다.
do{
  가장 좋아보이는 선택을 한다; (매번 최선의 선택)
} until(해 구성 완료)

그리디 알고리즘은 하나의 온전한 해가 만들어질 때까지 눈앞에 가장 좋아 보이는 선택을 반복한다.


그리디 알고리즘으로 최적해가 보장되는 예

1) 최소 신장 트리 (MST)

최소 신장 트리를 위한 프림 알고리즘, 크루스칼 알고리즘은 그리디 알고리즘으로 최적해가 보장된다.


2) 회의실 배정 문제

  • 회사에 회의실이 1개 있다. 여러 부서에서 회의실을 사용하므로 미리 신청을 받아 스케줄링을 한다.
  • 회의실을 사용하고자 하는 부서는 원하는 회의 시작 시간과 종료 시간을 명시해 신청서를 제출한다.
  • 이렇게 받은 N개의 회의 신청에 대해 회의실 사용 스케줄을 정하려고 한다.

목표: 겹치는 회의가 없게 하면서 가장 많은 수의 회의를 소화할 수 있도록 하는 것

Greedy_Schedule(S, n)
S = {(s, t), s=시작 시간, t=종료 시간}
{
    t에 대한 오름차순으로 정렬하고, 이 순서대로의 번호를 다시 매김
    T ← {1};
    last ← 1;
    
    for(i ← 2 ~ i≤n, i++){
    	if(t_last ≤ s_i){
        	T ← T ∪ {i};
            last ← i;
        }
    }
    return T;
}

예시

다음의 8개의 회의가 신청된 상황, 각 시작 시간과 종료 시간을 종료 시간이 빠른 순서로 정렬한 결과가 다음과 같음

 (3, 5), (1, 6), (6, 7), (5, 9), (8, 13), (7, 14), (12, 18), (16, 20)

알고리즘은 이 순서로 회의들을 포함시킬지 체크

  1. 먼저 1번 회의 `(3, 5)`는 무조건 포함
  2. 1번 회의가 끝나는 시간은 5
    1. (1, 6)은 앞선 회의와 겹치므로 제외
    2. (6, 7)은 겹치지 않으므로 포함
  3. 배정된 회의가 끝나는 시간은 7
    1. (5, 9) 제외
    2. (8, 13) 포함
  4. 배정된 회의가 끝나는 시간은 13
    1. (7, 14) 제외
    2. (12,18) 제외
    3. (16, 20) 포함

즉, 차례가 왔을 때 자신의 시작 시간이 이미 배정된 회의 중 맨 마지막에 끝나는 것보다 뒤면 포함 & 반복

배정 결과: (3, 5), (6, 7),  (8, 13), (16, 20)

 얼핏 직관과 어긋나 보이는 접근
∵ 가장 많은 회의를 소화하려면 회의 시간이 짧은 것을 유리하다고 생각해 소요 시간이 짧은 회의부터 고려하는 경향이 있음

📌 종료 시간을 기준으로 삼는 이유
더 많은 회의를 배정하기 위해 "앞으로 가능한 시간대를 최대한 많이 확보" 하려는 전략이기 때문

3) 그 밖의 예

  • 그래프에서 최단 경로를 구하는 다익스트라 알고리즘
  • 문서에서 각 원소들의 출현 빈도가 주어질 때 해당 문서를 효율적으로 압축하는 허프만 코딩 알고리즘

그리디 알고리즘으로 최적해가 보장되지 않는 예

1) 이진 트리의 최적합 경로 찾기

각 노드가 양의 가중치를 갖는 이진 트리, 트리의 내용은 사전에 알 수 없음

극단적인 예로, 가보지 않은 어떤 노드가 다른 모든 경로의 합보다 큰 가중치를 갖고 있을 수 있으므로,
모든 노드를 보기 전에는 최적해를 보장할 수 없음

만일 각 단계를 내려갈 때마다 눈앞의 이익만 좇는다면 아래와 같은 그림이 됨

이진 트리 그리디 알고리즘 적용

  • 루트에서 두 자식의 가중치를 보면 15와 60임
  • 그리디 알고리즘은 60을 선택하지만, 이 경로는 한 단계 더 내려가면 리프 노드를 만나 끝남.
  • 이 경로의 점수는 62점

최적 경로는 점선을 따라 내려가는 경로로 점수가 157점임.

이 문제는 DFS, BFS 방식으로 모든 노드를 확인하기 전까지 최적해 보장X

2) 보따리 문제

  • 부피가 M인 보따리 (냅색, Knapsack)와 이 보따리에 넣으려 하는 n개의 물건이 있다.
  • 물건 i의 부피는 `Wi`이고 이것을 보따리에 넣을 경우 `Pi`의 가치가 있다.
  • 물건들의 전체 부피 합이 M 이하이면서 가치가 최대가 되도록 보따리에 물건을 넣도록 하라
Greedy_Knapsack(P[], W[], M)
// P: 가치 배열, W: 부피 배열, M: 보따리 부피
{
    //P와 W를 P[i]/W[i]에 따라 내림차순(단위 부피당 가치가 큰 순서)로 정렬
    beadRoom ← M; i ← 1;
    while(W[i] ≤ beadRoom and i ≤ n){
    	X ← X ∪ {i};
        beadRoom ← beadRoom - W[i];
        i++;
    }
    return X;
}

 

이 코드는 보따리 용량을 초과하지 않는, 한 단위 부피당 가치가 가장 큰 물건 순서로 각 물건을 보따리에 추가

그리디 알고리즘은 조합 전체를 고려한 최적 선택을 하지 않기 때문에 항상 최적해를 보장할 수 없음

예시👇🏻

더보기

문제 설정

  • 배낭 용량: 50
  • 물건 A: W:10, P:60 → 가치 밀도 = 6
  • 물건 B: W:20, P:100 → 가치 밀도 = 5
  • 물건 C: W:30, P:120 → 가치 밀도 = 4

그리디 알고리즘

  1. A 선택, 남은 용량 40
  2. B 선택, 남은 용량 20

총 가치 = 40(A) + 20(B) = 60

최적해 = 100(B) + 120(C) = 220

이 문제는 *NP-Hard에 속하는 난제로, `0/1 보따리 문제`라고 칭함

❓ NP-Hard (Non-determinstic Polynomial-time Hard)
NP-Hard는 모든 NP 문제보다 최소한 어려운 문제들로, 다항 시간 내에 풀 수 있다는 보장이 없음
정답이 맞는지 검증은 빠를 수 있지만, 스스로 푸는 건 매우 어려움
0/1 냅색 문제처럼 겉보기엔 빨리 풀려도, 입력 크기 기준으론 지수 시간이면 NP-Hard에 속함

보따리 문제에서 물건을 자를 수 있다면, 그리디 방식으로 최적해를 보장할 수 있음

즉, 단위 부피당 가치가 가장 큰 물건 순서로 추가하다가 어떤 물건을 넣으려는 순간 보따리 용량이 초과하면,
남은 용량에 들어갈 만큼 잘라서 넣으면 됨

이 유형의 보따리 문제를 `자를 수 있는 보따리 문제 (Fractional Knapsack Problem)` 라 칭함


3) 동전 바꾸기

  • 우리 나라 동전(1,5,10,50,100,500)이 있다.
  • 3,256원을 가장 적은 개수의 동전을 사용해 만드려면 500원을 사용가능한 데까지 쓰면 6개가 됨
  • 다음 100원 2개, 50원 1개, 5원 1개, 1원 1개로 하면 총 11개의 동전으로 만들 수 있음

= 최적해

이처럼 동전의 액면이 커지면서 바로 아래 액면의 배수가 되는 경우에는 그리디한 방식으로 최적해를 구할 수 있음

동전의 액면이 증가하면서 앞 액면의 배수가 되지 않는 경우에는 그리디한 방식으로 최적해가 보장되지 않음.

예시)

500원, 400원, 100원, 75원, 50원 동전이 있다고 가정

목표: 최소한의 동전 개수로 1,300원 만들기

  • 그리디 접근: 500원 2개, 100원 3개 선택 👉 5개 사용
  • 최적해: 500원 1개, 400원 2개 선택  👉 3개 사용

이 문제는 동적 프로그래밍으로 최적해를 구할 수 있음

동전 바꾸기 DP 코드 

public static int coinChange(int[] coins, int amount) {
        // 최소 동전 수를 저장할 배열 (무한대로 초기화)
        int[] dp = new int[amount + 1];
        for (int i = 0; i <= amount; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        dp[0] = 0; // 0원을 만들기 위한 동전 수는 0개

        // 각 금액에 대해 최소 동전 수 계산
        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                if (dp[i - coin] != Integer.MAX_VALUE) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }

        // 최소 동전 수가 구해지지 않으면 -1 반환
        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
  }

DP vs 그리디

  동적 계획법 (DP)  그리디 알고리즘
원리 문제를 작은 부분 문제로 나누고, 그 해결을 바탕으로 큰 문제를 해결 (최적 부분 구조, 중복되는 부분 문제) 각 단계에서 최적의 선택을 하여 문제를 해결
(전역 최적해를 구하기 위해 지역 최적해 선택)
해결 방식 여러 가능한 해결 방법을 계산하여 가장 최적의 해를 선택 (보통 메모이제이션이나 테이블을 사용) 각 단계에서 가능한 선택 중 가장 좋은 것을 선택
문제 유형 최적화 문제로 최소화, 최대화 문제에서 효과적.
중복되는 부분 문제 해결이 필요할 때.
그리디 선택 속성을 만족할 때 사용. 각 단계에서 최적의 선택이 전체 최적을 보장하는 경우에만 유효
시간 복잡도 O(n^2), O(nW) (예: 배낭 문제) 등 문제에 따라 다르며,
최악의 경우에도 보통 다항 시간 내 해결
O(n log n), O(n) 등 보통 더 효율적일 수 있음
특징 중간 결과들을 저장하여 중복 계산을 피함 각 선택에서의 최적값만 고려, 결과적으로 빠름

 

728x90
'PS/알고리즘' 카테고리의 다른 글
  • 유니온 파인드 (Union Find)
  • 암시적 그래프
  • 너비 우선 탐색 & 깊이 우선 탐색, 이진 탐색
  • 그래프
0woy
0woy
Algorithm, CS, Web 등 배운 내용을 기록합니다.
  • 0woy
    0woy dev
    0woy
  • 전체
    오늘
    어제
  • 🌐 LANGUAGE
    • 분류 전체보기 (80)
      • Backend (21)
        • JAVA (7)
        • DB (11)
        • Spring (1)
        • Spring Security (2)
      • Computer Science (22)
        • 네트워크 (9)
        • 운영체제 (5)
        • 보안 (7)
      • Frontend (15)
        • HTML5 (1)
        • CSS (1)
        • JS (4)
        • Vue 3 (9)
      • PS (16)
        • LeetCode (2)
        • Baekjoon (1)
        • Programmers (1)
        • 알고리즘 (12)
      • Dev Trivia (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    java
    dfs
    leetcode
    Filter
    CA
    DP
    Spring
    JDBC
    대칭키
    트리
    비밀키
    BFS
    Vue3
    그래프
    select
    가용성
    javascript
    https
    JS
    tcp
    공개키
    security
    Props
    RDB
    shortestpath
    Graph
    속성
    function
    set
    PreparedStatement
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
0woy
그리디 알고리즘 (탐욕법, Greedy)
상단으로

티스토리툴바