coding test

[파이썬, Java] 추석 트래픽

잔망루피 2021. 4. 1. 17:47

문제 설명

추석 트래픽

이번 추석에도 시스템 장애가 없는 명절을 보내고 싶은 어피치는 서버를 증설해야 할지 고민이다. 장애 대비용 서버 증설 여부를 결정하기 위해 작년 추석 기간인 9월 15일 로그 데이터를 분석한 후 초당 최대 처리량을 계산해보기로 했다. 초당 최대 처리량은 요청의 응답 완료 여부에 관계없이 임의 시간부터 1초(=1,000밀리초)간 처리하는 요청의 최대 개수를 의미한다.

입력 형식

  • solution 함수에 전달되는 lines 배열은 N(1 ≦ N ≦ 2,000)개의 로그 문자열로 되어 있으며, 각 로그 문자열마다 요청에 대한 응답완료시간 S와 처리시간 T가 공백으로 구분되어 있다.
  • 응답완료시간 S는 작년 추석인 2016년 9월 15일만 포함하여 고정 길이 2016-09-15 hh:mm:ss.sss 형식으로 되어 있다.
  • 처리시간 T 0.1s, 0.312s, 2s 와 같이 최대 소수점 셋째 자리까지 기록하며 뒤에는 초 단위를 의미하는 s로 끝난다.
  • 예를 들어, 로그 문자열 2016-09-15 03:10:33.020 0.011s은 "2016년 9월 15일 오전 3시 10분 33.010초"부터 "2016년 9월 15일 오전 3시 10분 33.020초"까지 "0.011초" 동안 처리된 요청을 의미한다. (처리시간은 시작시간과 끝시간을 포함)
  • 서버에는 타임아웃이 3초로 적용되어 있기 때문에 처리시간은 0.001 ≦ T ≦ 3.000이다.
  • lines 배열은 응답완료시간 S를 기준으로 오름차순 정렬되어 있다.

출력 형식

  • solution 함수에서는 로그 데이터 lines 배열에 대해 초당 최대 처리량을 리턴한다.

입출력 예제

예제1

  • 입력: [
    "2016-09-15 01:00:04.001 2.0s",
    "2016-09-15 01:00:07.000 2s"
    ]
  • 출력: 1

예제2

  • 입력: [
    "2016-09-15 01:00:04.002 2.0s",
    "2016-09-15 01:00:07.000 2s"
    ]
  • 출력: 2
  • 설명: 처리시간은 시작시간과 끝시간을 포함하므로
    첫 번째 로그는 01:00:02.003 ~ 01:00:04.002에서 2초 동안 처리되었으며,
    두 번째 로그는 01:00:05.001 ~ 01:00:07.000에서 2초 동안 처리된다.
    따라서, 첫 번째 로그가 끝나는 시점과 두 번째 로그가 시작하는 시점의 구간인 01:00:04.002 ~ 01:00:05.001 1초 동안 최대 2개가 된다.

예제3

  • 입력: [
    "2016-09-15 20:59:57.421 0.351s",
    "2016-09-15 20:59:58.233 1.181s",
    "2016-09-15 20:59:58.299 0.8s",
    "2016-09-15 20:59:58.688 1.041s",
    "2016-09-15 20:59:59.591 1.412s",
    "2016-09-15 21:00:00.464 1.466s",
    "2016-09-15 21:00:00.741 1.581s",
    "2016-09-15 21:00:00.748 2.31s",
    "2016-09-15 21:00:00.966 0.381s",
    "2016-09-15 21:00:02.066 2.62s"
    ]
  • 출력: 7
  • 설명: 아래 타임라인 그림에서 빨간색으로 표시된 1초 각 구간의 처리량을 구해보면 (1)은 4개, (2)는 7개, (3)는 2개임을 알 수 있다. 따라서 초당 최대 처리량은 7이 되며, 동일한 최대 처리량을 갖는 1초 구간은 여러 개 존재할 수 있으므로 이 문제에서는 구간이 아닌 개수만 출력한다.

해설 보러가기

 

 

💚 나의 풀이

 

from datetime import timedelta, datetime

def cal(start, results) :
    end=start+timedelta(seconds=1)
    cnt=0
    for result in results :
        if result[0] < end and result[1] >= start :
            cnt+=1
    return cnt
    
def solution(lines):
    answers=0
    results=[]
    
    for line in lines :
        log=line.split()
        end=log[0]+" "+log[1]
        log[2]=log[2][:-1]  # s 제거
        end=datetime.fromisoformat(end)
        start=end-timedelta(milliseconds=float(log[2])*1000-1)
        results.append([start, end])
    
    for result in results :
        answers=max(answers, max(cal(result[0], results), cal(result[1], results)))
    return answers

 

datetime.fromisoformat(date_string)

입력이 '2016-09-15 20:59:57.421'이면

출력은 '2016-09-15 20:59:57.421000'

 

timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

두 날짜 또는 시간의 차를 통해 기간을 나타냄.

 

start를 구할 때, -1을 하는 이유는 처리시간은 시작시간과 끝 시간을 포함해야한다고 문제 조건에 나와있어서다.

 

 

import java.util.*;

class Solution {
    public int cal(double start, ArrayList<ArrayList<Double>> result){
        double end=start+1000;
        int cnt=0;
        for(ArrayList<Double> i : result){
            if(i.get(0) < end && i.get(1) >= start) cnt++;
        }
        return cnt;
    }
    
    
    public int solution(String[] lines) {
        int answer = 0;
        ArrayList<ArrayList<Double>> list=new ArrayList<ArrayList<Double>>();
        
        for(String line : lines){
            String[] date=line.split(" ");
            date[2]=date[2].replace("s","");    // s제거
            String[] end=date[1].split(":");
            Double second=(Integer.parseInt(end[0])*3600 + Integer.parseInt(end[1])*60 + Double.parseDouble(end[2]))*1000;
            Double start=second-(Double.parseDouble(date[2])*1000)+1;
            ArrayList<Double> result=new ArrayList<>();
            result.add(start);
            result.add(second);
            list.add(result);
        }
        
        for(ArrayList<Double> i : list){
            answer=Math.max(answer, Math.max(cal(i.get(0), list), cal(i.get(1), list)));
        }
        
        return answer;
    }
}

 

처리시간 date[2]에 *1000을 빼먹어서 테케 예제에서 5/7, 제출하면 68점으로 실패했었다.

응답 시작과 완료 시간은 millisecond 단위로 변환해서 계산했다.

 

 

👱‍♀️ 다른사람 풀이

 

# https://velog.io/@someh/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%B6%94%EC%84%9D-%ED%8A%B8%EB%9E%98%ED%94%BD
from datetime import datetime, timedelta

def compare(time, moment):
    start = time
    end = time + timedelta(milliseconds=999) # 시작시간과 끝시간을 포함하므로
    if start >= moment[0] and start <= moment[1]:
        return True
    elif end >= moment[0] and end <= moment[1]:
        return True
    elif start <= moment[0] and end >= moment[1]:
        return True
    return False

def solution(lines):  # lines : 응답완료시간 s와 처리시간 t
    result = []
    for line in lines:
        temp = line.split(' ')
        date = str(temp[0]) + " " + str(temp[1])    # 응답 완료 시간

        if '.' in temp[2]:
            delay = temp[2].split('.')
            delay[1] = delay[1][0:-1]       # s 제외하고 숫자만
        else:
            delay = list(temp[2][0:-1])
            delay += ["0"]

        end = datetime.fromisoformat(date)
        start = end - timedelta(seconds=int(delay[0]), milliseconds=int(delay[1]) - 1)
        result.append([start, end])

    answers = []
    for timelist in result:     # 로그
        for time in timelist:	# 로그의 시작 시간 또는 종료 시간
            answer = 0
            for moment in result:
                if compare(time, moment) == True:
                    answer += 1
            answers.append(answer)
    return max(answers)

 

요청량이 변하는 순간은 각 로그의 시작과 끝이다.

작업의 시작 시간과 끝나는 시간을 구해서 result에 담는다.

각 작업의 start와 end 각각에 대해서 1초간 요청이 들어왔는지 본다.

임의 시간부터 1초간 처리하는 요청의 최대 개수를 센다. 각각의 로그를 모든 로그의 start와 end 타임라인 안에 들어가는지 본다.

 

1. moment[0]이 start보다 작거나 같고, 끝나는 시간 moment[1]이 start보다 크거나 같을 때

2. moment[0]이 end보다 작거나 같고, 끝나는 시간 moment[1]이 end보다 크거나 같을 때

3. 시작 시간 moment[0]이 start보다 작거나 같고, 끝나는 시간 moment[1]이 end보다 크거나 같을 때

위의 세가지 경우에 초당 요청이 처리됨.

 

 

# 출처 : https://it-garden.tistory.com/m/408?category=857251
def checktr(time,li):
    c=0
    start=time
    end=time+1000
    for i in li:
        if i[1] >= start and i[0] < end:
            c += 1
    return c

def solution(lines):
    li=[]
    r=1
    for line in lines:
        y,a,b=line.split()	# 날짜, 시간, 처리시간
        a=a.split(':')  # 시간
        b=float(b.replace('s',''))*1000     # millisecond로 변환
        end=(int(a[0])*3600 + int(a[1])*60 + float(a[2]))*1000  # millisecond로 변환
        start=end-b+1
        li.append([start,end])
    for i in li:
        r=max(r,checktr(i[0],li),checktr(i[1],li))
    return r

 

위 풀이와 알고리즘은 같다.

 

 

 

 

🙇‍♀️ 출처 : 프로그래머스

 

반응형

'coding test' 카테고리의 다른 글

[파이썬] 단속카메라  (0) 2021.04.05
[파이썬] 자물쇠와 열쇠  (0) 2021.04.02
[파이썬, Java] 단어 변환  (0) 2021.03.26
[파이썬] 디스크 컨트롤러  (0) 2021.03.25
[파이썬, Java] 네트워크  (0) 2021.03.24