欲速不達

일을 급히 하고자 서두르면 도리어 이루지 못한다.

Fantastic AI, Fantastic World

DS | Data Science/Alogithm & Coding Test - Python

[프로그래머스] 고득점 Kit - 힙 : 디스크 컨트롤러(파이썬)

_껀이_ 2022. 10. 1. 22:36
728x90
반응형

1. 문제설명

하드디스크는 한 번에 하나의 작업만 수행할 수 있습니다. 디스크 컨트롤러를 구현하는 방법은 여러 가지가 있습니다. 가장 일반적인 방법은 요청이 들어온 순서대로 처리하는 것입니다.

 

예를들어

- 0ms 시점에 3ms가 소요되는 A작업 요청
- 1ms 시점에 9ms가 소요되는 B작업 요청
- 2ms 시점에 6ms가 소요되는 C작업 요청

와 같은 요청이 들어왔습니다. 이를 그림으로 표현하면 아래와 같습니다.

한 번에 하나의 요청만을 수행할 수 있기 때문에 각각의 작업을 요청받은 순서대로 처리하면 다음과 같이 처리 됩니다.

- A: 3ms 시점에 작업 완료 (요청에서 종료까지 : 3ms)
- B: 1ms부터 대기하다가, 3ms 시점에 작업을 시작해서 12ms 시점에 작업 완료(요청에서 종료까지 : 11ms)
- C: 2ms부터 대기하다가, 12ms 시점에 작업을 시작해서 18ms 시점에 작업 완료(요청에서 종료까지 : 16ms)

이 때 각 작업의 요청부터 종료까지 걸린 시간의 평균은 10ms(= (3 + 11 + 16) / 3)가 됩니다.

하지만 A → C → B 순서대로 처리하면

- A: 3ms 시점에 작업 완료(요청에서 종료까지 : 3ms)
- C: 2ms부터 대기하다가, 3ms 시점에 작업을 시작해서 9ms 시점에 작업 완료(요청에서 종료까지 : 7ms)
- B: 1ms부터 대기하다가, 9ms 시점에 작업을 시작해서 18ms 시점에 작업 완료(요청에서 종료까지 : 17ms)

이렇게 A → C → B의 순서로 처리하면 각 작업의 요청부터 종료까지 걸린 시간의 평균은 9ms(= (3 + 7 + 17) / 3)가 됩니다.

각 작업에 대해 [작업이 요청되는 시점, 작업의 소요시간]을 담은 2차원 배열 jobs가 매개변수로 주어질 때, 작업의 요청부터 종료까지 걸린 시간의 평균을 가장 줄이는 방법으로 처리하면 평균이 얼마가 되는지 return 하도록 solution 함수를 작성해주세요. (단, 소수점 이하의 수는 버립니다)

 
 

2. 제한사항

  • jobs의 길이는 1 이상 500 이하입니다.
  • jobs의 각 행은 하나의 작업에 대한 [작업이 요청되는 시점, 작업의 소요시간] 입니다.
  • 각 작업에 대해 작업이 요청되는 시간은 0 이상 1,000 이하입니다.
  • 각 작업에 대해 작업의 소요시간은 1 이상 1,000 이하입니다.
  • 하드디스크가 작업을 수행하고 있지 않을 때에는 먼저 요청이 들어온 작업부터 처리합니다.

 

 

3. 입출력예시

jobs return
[[0, 3], [1, 9], [2, 6]] 9
  • 0ms 시점에 3ms 걸리는 작업 요청이 들어옵니다.
  • 1ms 시점에 9ms 걸리는 작업 요청이 들어옵니다.
  • 2ms 시점에 6ms 걸리는 작업 요청이 들어옵니다.

 

 

4. 문제풀이

  • 1차 시도
def solution(jobs):
    answer = 0
    # [작업이 요청되는 시점, 작업의 소요시간]
    jobs = sorted(jobs, key=lambda x:x[1])
    print(jobs)
    
    # 전체 소요시간
    sum_time = 0
    for i in jobs:
        sum_time+=i[1]
    print(sum_time)
    
    # 소요시간, 시작시간 체크
    l_time = [0]*len(jobs)
    start_time = [0]*len(jobs)
    
    # 각 프로세스가 끝나는 시점의 시간합
    tt, t = [], 0
    for i in jobs:
        t+=i[1]
        tt.append(t)
    print(tt)
    
            
    for j in range(sum_time):
        for k in range(len(jobs)):
            if j == jobs[k][0]:
                start_time[k]+=j
    
    for k in range(len(jobs)):
        if k==0:
            l_time[k]+=jobs[k][1]
        if k!=0:
            l_time[k]+=tt[k]-min(jobs[k-1][1],jobs[k][0])
    
    # 각각의 소요시간
    print(l_time)
    
    # 평균계산
    mean_time = int(sum(l_time)/len(l_time))
    print(mean_time)
    
    answer = mean_time
        
    return answer

힙 문제인데 힙으로 안풀었다.

당연히 오답이다.

 

힙 문제 탭으로 들어가놓고도 문제를 읽고 어.. 이거 힙 아니어도 풀수있지 않나.. 하면서 호기롭게 했다가 오답이다. 사실 힙 어떻게 쓰는지 까먹어서 못쓴 것도 있다.

그래서 다시 힙을 어떻게 구현하는지 복습하고 구현한 코드가 아래와 같다.

 

  • 2차 시도
import heapq

def solution(jobs):
    answer, i = 0, 0
    start, end = -1, 0    # 첫 시작이 0인 경우를 뽑기 위해서
    heap = []
    while len(jobs)>i: # 종료 시점 = 모든 태스크 종료 후
        for job in jobs:
            # job[0] = 각 태스크의 요청시간
            if start<job[0]<=end: # 하나씩
                heapq.heappush(heap, (job[1], job[0]))
        
        if len(heap)>0:
            now = heapq.heappop(heap)
            # 파이썬에서 heapq는 최소힙으로 구성되어 있으므로 가장 앞에 위치한 요소가 반환됨
            start = end
            end += now[0]
            answer += (end-now[1])
            # 모든 태스크 종료 후 작업중지하기 위한 조치
            i+=1
        else:
            # 요청시간과 시작시간이 연속되거나 겹쳐지지 않는 경우
            # 위의 heappush가 수행되지 않기 때문에 end부분을 늘려주는 것
            end+=1
        
    answer = int(answer/len(jobs))
    
    return answer

파이썬에서 heapq는 최소힙으로 구성되어있기 때문에 넣었다 빼기만해도 오름차순으로 정렬된다. 위의 문제와 같이 요소값이 튜플이나 리스트로 구성이 되어있을 때 다른 자료구조와 마찬가지로 인덱스 0을 기준으로 정렬되는 것으로 보인다.

 

파이썬에서 heapq는 import heapq로 불러올 수 있으며, 삽입연산 .heappush()와 추출연산 .heappop() 등이 있다. 

자세한건 아래 문서를 참고하면 된다.

 

https://docs.python.org/ko/3/library/heapq.html

 

heapq — 힙 큐 알고리즘 — Python 3.10.7 문서

heapq — 힙 큐 알고리즘 소스 코드: Lib/heapq.py 이 모듈은 우선순위 큐 알고리즘이라고도 하는 힙(heap) 큐 알고리즘의 구현을 제공합니다. 힙은 모든 부모 노드가 자식보다 작거나 같은 값을 갖는

docs.python.org

 

참고로 파이썬에서 최대힙을 구성해야할때는 임시적으로 원래 자료의 기호를 바꾸어(+ -> - 등) 사용하는 경우도 있다.

 

 

5. 회고

프로그래머스 레벨 3 문제이지만 힙에 대한 이해와 heapq 라이브러리의 사용법을 알고 있으면 코드는 쉽게 해결될 문제였다. 하지만 실제로 코딩테스트 환경에서 이 문제를 봤을때 힙을 떠올릴 수 있을지는 모르겠다. 정렬과 관련된 문제는 힙을 떠올려보도록 훈련하는 것도 좋을 것 같다.

 

 

728x90
반응형