-
탐욕 알고리즘자료구조와 알고리즘 2020. 11. 3. 12:25
탐욕 알고리즘의 이해
1. 탐욕 알고리즘 이란?
- Greedy algorithm 또는 탐욕 알고리즘 이라고 불리움
- 최적의 해에 가까운 값을 구하기 위해 사용됨
- 여러 경우 중 하나를 결정해야할 때마다, 매순간 최적이라고 생각되는 경우를 선택하는 방식으로 진행해서, 최종적인 값을 구하는 방식
2. 탐욕 알고리즘 예
문제1: 동전 문제
- 지불해야 하는 값이 4720원 일 때 1원 50원 100원, 500원 동전으로 동전의 수가 가장 적게 지불하시오.
- 가장 큰 동전부터 최대한 지불해야 하는 값을 채우는 방식으로 구현 가능
- 탐욕 알고리즘으로 매순간 최적이라고 생각되는 경우를 선택하면 됨
coin_list = [1, 100, 50, 500] print (coin_list) coin_list.sort(reverse=True) print (coin_list)
[1, 100, 50, 500]
[500, 100, 50, 1]
coin_list = [500, 100, 50, 1] def min_coin_count(value, coin_list): total_coin_count = 0 details = list() coin_list.sort(reverse=True) for coin in coin_list: coin_num = value // coin total_coin_count += coin_num value -= coin_num * coin details.append([coin, coin_num]) return total_coin_count, details
min_coin_count(4720, coin_list)
(31, [[500, 9], [100, 2], [50, 0], [1, 20]])
문제2: 부분 배낭 문제 (Fractional Knapsack Problem)
- 무게 제한이 k인 배낭에 최대 가치를 가지도록 물건을 넣는 문제
- 각 물건은 무게(w)와 가치(v)로 표현될 수 있음
- 물건은 쪼갤 수 있으므로 물건의 일부분이 배낭에 넣어질 수 있음, 그래서 Fractional Knapsack Problem 으로 부름
- Fractional Knapsack Problem 의 반대로 물건을 쪼개서 넣을 수 없는 배낭 문제도 존재함 (0/1 Knapsack Problem 으로 부름)
- Fractional Knapsack Problem 의 반대로 물건을 쪼개서 넣을 수 없는 배낭 문제도 존재함 (0/1 Knapsack Problem 으로 부름)
위의 표를 (무게, 가치) 순으로 튜플을 원소로 하는 리스트를 만든다
data_list = [(10, 10), (15, 12), (20, 10), (25, 8), (30, 5)]
가치/무게로 가치가 높은 순으로 데이터를 정렬한다.
data_list = sorted(data_list, key=lambda x: x[1] / x[0], reverse=True)
sorted 함수에 첫번째 매개값으로 데이터 리스트를 주고, 두번째 매개값으로 데이터를 정렬할 조건을 준다.
lambda x에 서 x는 리스트의 원소인 튜플을 말하고 x[1]은 가치 x[0]은 무게이다. 세번째 매개값으로 내림차순(가치가 큰것부터 정렬)을 주었다.
data_list에 이미 정렬이 된 상태로 들어가 결과값은 똑같다.
def get_max_value(data_list, capacity): data_list = sorted(data_list, key=lambda x: x[1] / x[0], reverse=True) #sorted에 (리스트, 정렬하고 싶은 기준(가치를 무게로 나뭄), 큰수정렬) total_value = 0 #얼만큼의 가치 되는지 알기위해 details = list() #어떤 가치의 물건이 얼만큼 들어갔는지 확인하기 위해 for data in data_list: if capacity - data[0] >= 0: capacity -= data[0] total_value += data[1] details.append([data[0], data[1], 1]) else: fraction = capacity / data[0] total_value += data[1] * fraction details.append([data[0], data[1], fraction]) break return total_value, details
전체 가치를 확인할 수 있는 total_value 변수를 만들고, 어떤 물건이 얼만큼 들어가있는지 확인하기 위해 details 리스트를 만든다.
for data in data_list 에서 이미 리스트는 가치가 큰 순으로 정렬이 되어있기 때문에 젤 첫 data는 가치가 제일 크다.
if capacity - data[0] >= 0: ▶만약에 가방의 용량에서 첫번째 물건의 무게를 빼고도 값이 남는다면
capacity -= data[0] ▶가방의 용량은 첫번째 물건의 무게만큼 빼준다.( 가방에 물건을 넣었기 떄문)
total_value += data[1] ▶가방에 들어간 물건의 가치만큼 가치를 카운트 한다.
details.append([data[0], data[1], 1]) ▶ detaill리스트에 물건의 무게와 가치를 넣고 온전히 들어갔다는 의미에서 1을표시
만약 가방의 용량이 물건 하나를 다 담기에 모자라다면 물건을 가방에 들어갈 수 있는 무게로 쪼개야 한다.
fraction = capacity / data[0] ▶ 가방의 여유 용량을 물건의 무게로 나눈다.
(남은 용량이 5이고 물건 무게가 20이라면 물건의1/4을넣을수있다.)
total_value += data[1] * fraction ▶무게가 줄었기 때문에 가치도 그에 비례해 줄어야한다.
details.append([data[0], data[1], fraction]) ▶디테일에 추가한다.
break ▶더이상 물건을 넣을수 없으므로 반복을 멈춘다.
만약 배낭에 무게를 30까지 넣을수 있다면 .
get_max_value(data_list, 30)
Out[20]:
(24.5, [[10, 10, 1], [15, 12, 1], [20, 10, 0.25]])
총 가방에 들어간 물건의 가치는 24.5이고 3번째 물건의 1/4이 가방에 담긴 것을 확인할수 있다.
3. 탐욕 알고리즘의 한계
- 탐욕 알고리즘은 근사치 추정에 활용
- 반드시 최적의 해를 구할 수 있는 것은 아니기 때문
- 최적의 해에 가까운 값을 구하는 방법 중의 하나임
예
- '시작' 노드에서 시작해서 가장 작은 값을 찾아 leaf node 까지 가는 경로를 찾을 시에
- Greedy 알고리즘 적용시 시작 -> 7 -> 12 를 선택하게 되므로 7 + 12 = 19 가 됨
- 하지만 실제 가장 작은 값은 시작 -> 10 -> 5 이며, 10 + 5 = 15 가 답
매 순간 최적의 선택을 하기때문에 탐욕 알고리즘은 한계가 있다. 매순간의 최적의 값이 전체 최적의 값과 다른 경우가 있을 수 있기 때문/ 근사치 추정에 활용
'자료구조와 알고리즘' 카테고리의 다른 글
최단 경로 알고리즘 이해 (0) 2020.11.04 깊이 우선 탐색 (0) 2020.11.02 너비 우선 탐색 (0) 2020.11.02 그래프 이해 (0) 2020.11.02 순차탐색 (0) 2020.11.01