새소식

Career/Coding Test

99클럽 코테 스터디 8/99일차 TIL #기능개발도움말(미들러)

  • -
반응형

문제:기능개발


출처: 프로그래머스

https://school.programmers.co.kr/learn/courses/30/lessons/42586

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

1. 문제 설명


프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.
또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.
먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.

제한 사항


- 작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.
- 작업 진도는 100 미만의 자연수입니다.
- 작업 속도는 100 이하의 자연수입니다.
- 배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다.
예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.

2. 문제 해석

진도가 100일때 서비스 반영 가능
progress: 먼저 배포되어야 하는 순서임
speeds: 개발 속도
각 배포마다 몇 개의 기능이 배포되는지
ex)
progresses: [93, 30, 55] | speeds: [1, 30, 5]
1-> 7일 후 배포 가능
2-> 3일 후 배포 가능 -> 1번 기다려야함
30->9일 후 배포 가능 -> 7일 후 1, 2 완료 후 배포

return: [2, 1]
3. 문제 풀이

풀이 (1) 100/100

1. 예상 완료 기간을 이용해서 데이터를 일단 만들기(list 형식)

93, 1  => 100-93 = 7. =? 1*n => 7
30, 30 => 100-30 = 70 -> 30*n => 3

(100 - progress) % speed 가 0인 경우는 (100 - progress) // speed : 몫으로

(100 - progress) % speed 가 0이 아닌 경우는 (100 - progress) // speed+1 : 몫 +1

2. 생성된 리스트를 순회하면서 앞의 값과 현재 값을 비교하여 return 값을 형성

- cnt: 몇개의 작업을 한번에 처리할 지

- 임시 값을 하나 설정하고(이전 값) 그 값과 현재 값을 비교:

이전값 >= 현재값 : 기다려야 하는 상황, cnt += 1

이전값 < 현재값: 안기다려도 되는 상황: 앞의 cnt를 answer에 append, cnt를 초기화

def solution(progresses, speeds):

    
    counts = len(progresses)
    left_days = []
    
    for i in range(counts):
        left_day = 100-int(progresses[i])
        speed = speeds[i]
        if left_day % speed != 0:
            day = left_day // speed +1
        else:
            day = left_day // speed
            
            
        left_days.append(day)
    print(left_days)
        
    answer = []
    
    cnt = 0
    tmp_day = left_days[0]
    for k, j in enumerate(left_days):
        
        if j <= tmp_day:
            cnt+=1
        else:
            answer.append(cnt)
            tmp_day = left_days[k]
            cnt = 1
    answer.append(cnt)
        
    return answer

테스트 1 통과 (0.01ms, 10.2MB)
테스트 2 통과 (0.04ms, 10.4MB)
테스트 3 통과 (0.04ms, 10.2MB)
테스트 4 통과 (0.02ms, 10.3MB)
테스트 5 통과 (0.01ms, 10.3MB)
테스트 6 통과 (0.02ms, 10.2MB)
테스트 7 통과 (0.06ms, 10.1MB)
테스트 8 통과 (0.02ms, 10.2MB)
테스트 9 통과 (0.03ms, 10.1MB)
테스트 10 통과 (0.03ms, 10.2MB)
테스트 11 통과 (0.01ms, 10.2MB)
정확성 테스트
테스트 1 통과 (0.01ms, 10.2MB)
테스트 2 통과 (0.04ms, 10.4MB)
테스트 3 통과 (0.04ms, 10.2MB)
테스트 4 통과 (0.02ms, 10.3MB)
테스트 5 통과 (0.01ms, 10.3MB)
테스트 6 통과 (0.02ms, 10.2MB)
테스트 7 통과 (0.06ms, 10.1MB)
테스트 8 통과 (0.02ms, 10.2MB)
테스트 9 통과 (0.03ms, 10.1MB)
테스트 10 통과 (0.03ms, 10.2MB)
테스트 11 통과 (0.01ms, 10.2MB)

2중 for문이 없어서 딱히 연산 속도의 문제는 없었다.

코드를 다 작성하고 for문이 각각 돌고 있어서 한 번에 합치는게 조금 더 좋다고 생각이 들었다.

문제 풀이 당시에는 시간 내 푸는게 목적이었어서 pass 했던 부분.

풀이 (2) 100/100

위 부분 말고도 다른 부분 줄일 만한게 있나 싶어 GPT 형님께 질문한 결과 수정된 코드는 다음과 같다.

def solution(progresses, speeds):
    from math import ceil
    
    # Calculate the remaining days for each task to complete
    left_days = [ceil((100 - p) / s) for p, s in zip(progresses, speeds)]
    
    # List to store the answer
    answer = []
    
    # Initialize the first task's completion day and a counter
    current_max_day = left_days[0]
    count = 1
    
    # Iterate over the remaining days to count batches
    for day in left_days[1:]:
        if day <= current_max_day:
            count += 1
        else:
            answer.append(count)
            current_max_day = day
            count = 1
    
    # Append the last count
    answer.append(count)
    
    return answer

 

기존에 left_days 라고 명명한 list를 list comprehension + ceil + zip으로 바로 끝냈다.

이런거 연습 많이 해야함..

left_days = [ceil((100 - p) / s) for p, s in zip(progresses, speeds)]

ceil 

ceil을 활용하게 되면 소숫점 올림을 해서 나머지 조건문을 한 번에 처리 가능하다.

그리고 for 문을 돌 때 progresses[i], speeds[i] 부분을 한번에 처리하기 위해 zip으로 리스트 자료형을 묶었다.

 

 

4. 주요 코드 및 정리

 

4.1 enumerate()

for i, j in enumerate(list): 리스트의 값을 가져오는 동시에 인덱스값도 같이 return 되는 반복문 형태.

앞의 값이 인덱스, 뒤의 값이 리스트 값. 

4.2 python 올림 내림 반올림 함수, ceil(), floor(), round()

각 함수 사용을 위해 import math 또는 from math import ceil, floor 사용 (round는 자체 내장 함수로 import 불필요)

ceil(): 올림

floor(): 버림

round(): 반올림

from math import ceil, floor
print(100/2.2)
>> 45.45454545454545
print(ceil(100/2.2))
>> 46
print(floor(100/2.2))
>> 45
print(round(100/2.2))
>> 45

이번에 공부하면서 알게 된게 round() 함수를 쓸 때 홀수, 짝수인 경우 반올림이 다르게 들어가는 것을 알게 되었다.(충격)

모든 소숫점에 대해서 그런것은 아니고 0.5에서 문제가 있던 것이다.

print(round(1.5))
>> 2
print(round(2.5))
>> 2
print(round(3.5))
>> 4
print(round(4.5))
>> 4
print(round(5.5))
>> 6

round()함수는 홀수의 경우 0.5를 올림을, 짝수의 경우 0.5를 버림을 하는 식의 반올림이 진행된다.(왜이랩)?

ChatGPT의 답변은 다음과 같다.


Python의 round 함수는 반올림할 때 "짝수 쪽으로" 반올림하는 규칙을 따릅니다. 이 방법은 "은행가의 반올림" 또는 "가우스 반올림"이라고도 불리며, 통계적으로 더 정확한 결과를 얻기 위해 사용됩니다.

이 방법에서는 .5 값을 가장 가까운 짝수로 반올림합니다. 따라서 다음과 같은 결과가 나옵니다:

  • round(1.5)는 2 (가장 가까운 짝수)
  • round(2.5)는 2 (가장 가까운 짝수)
  • round(3.5)는 4 (가장 가까운 짝수)
  • round(4.5)는 4 (가장 가까운 짝수)
  • round(5.5)는 6 (가장 가까운 짝수)

이 규칙은 데이터의 편향을 줄이기 위해 사용되며, 특히 많은 수의 데이터를 반올림할 때 유용합니다.


정확하게는 짝수쪽으로 반올림을 하는 규칙이고 이게 가우스 반올림이라고 한다.

참고로 한국어로 이걸 사사오입이라고 하는 사람들도 있던데 사사오입은 0부터 4까지 내림, 5부터 9까지 올림을 의미하는 말이다

 

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.