Notice
Recent Posts
Recent Comments
Today
Total
05-07 20:02
Archives
관리 메뉴

Jeongchul Kim

programmers lv1 모의고사 python 본문

Algorithm

programmers lv1 모의고사 python

김 정출 2019. 9. 18. 18:31

programmers lv1 모의고사 python


문제 설명

수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.


1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...

2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...

3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...


1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성해주세요.


제한 조건

시험은 최대 10,000 문제로 구성되어있습니다.

문제의 정답은 1, 2, 3, 4, 5중 하나입니다.

가장 높은 점수를 받은 사람이 여럿일 경우, return하는 값을 오름차순 정렬해주세요.

출처 : https://programmers.co.kr/learn/courses/30/lessons/42862


첫 번째 풀이(시간초과)

제가 생각한 첫 번째 풀이 방법

[1] 학생들이 찍는 답안과 정답을 뺀다. 문제를 맞혔다면 뺀 값은 0이 된다. 배열과 배열을 빼야하므로 numpy를 써보자

[2] 계산한 배열에서 0을 카운트 count()하자

[3] 문제의 개수(N)와 학생들이 찍는 답안의 개수(S)를 생각해야 한다. N == S가 아니라 N > S, N < S라면
- N > S 라면 S 배열을 *로 복사해서 늘려야한다.

 - N < S 라면 S 배열을 slice 로 짜르자.


예제 테스트는 통과했지만, 실제 제출시 절반만 통과하게 됩니다.


import numpy as np

import collections


def count_collect(student, answers):

    answer_length = len(answers)

    student_length = len(student)

    # 만약 정답의 길이가 학생들의 찍는 길이보다 작다면 인덱싱으로 자른다.

    if student_length >= answer_length:

        student = student[0:answer_length]

        d = np.array(student) - np.array(answers)

        collect = collections.Counter(d)

        return collect[0]

    else: # 만약 정답의 길이가 학생들이 찍는 길이보다 크다면?

        ratio = int(answer_length / student_length)

        student = student * (ratio+1)

        student = student[0:answer_length]

        d = np.array(student) - np.array(answers)

        collect = collections.Counter(d)

        return collect[0]    


def solution(answers):

    # 1. 학생들이 찍는 정답 방식과 정답을 뺀다. 정답이라면 0이다.

    # 2. 0을 카운트한다.

    # 3. 이때 발생하는 문제가 정답과 학생들이 찍는 것 길이를 맞춰야한다.

    student1 = [1, 2, 3, 4, 5]

    student2 = [2, 1, 2, 3, 2, 4, 2, 5]

    student3= [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]

    answer = []

    student1_collect = count_collect(student1, answers)

    student2_collect = count_collect(student2, answers)

    student3_collect = count_collect(student3, answers)

    

    max_collect = max(student1_collect, student2_collect, student3_collect)

    if student1_collect == max_collect:

        answer.append(1)

    if student2_collect == max_collect:

        answer.append(2)

    if student2_collect == max_collect:

        answer.append(3)

        

    return answer



두 번째 풀이(정답)

배열만큼을 복사해서 메모리를 낭비하지 말고, 맞은 횟수를 카운트하는 방식으로 사용하기로 했습니다.

그리고 학생들이 찍는 답안 방식은 패턴으로 반복되기 때문에 인덱스를 이용하면 쉽게 해결되는 문제입니다.


enumerate : list, tuple, string에 대해 원하는 인덱스와 값을 돌려줍니다. 현재 어느 위치에 값이 있는지 알려줄 때 편합니다.

list = [100, 90, 80]

for idx, value in enumerate(list):

    print(idx, value)

(0, 100)

(1, 90)

(2, 80)



[1] 학생들 패턴을 인덱스와 %(mode)를 활용해 공간 복잡도를 줄여보자. 정답의 인덱스를 알기 위해 enumerate를 쓰자.

[2] 인덱스를 패턴의 전체길이에서 %로 나눠 계산하고 일치하다면 학생 3명 점수 배열의 해당 학생 값을 증가한다.

[3] 최종적으로 학생 3명 점수 배열에서 max값을 확인하고 배열에 append 한다.


def solution(answers):

    student1 = [1, 2, 3, 4, 5]

    student2 = [2, 1, 2, 3, 2, 4, 2, 5]

    student3= [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]

    score = [0, 0, 0]

    result = []

    

    # 1. enumerate를 활용해서 idx, value를 확인한다.

    for idx, answer in enumerate(answers):

        # 2. itertools.cycle()을 사용해서 순환 객체를 만들어도 되지만, 인덱싱으로 순환을 돌아보자

        if answer == student1[idx % len(student1)] :

            score[0] += 1

        if answer == student2[idx % len(student2)] :

            score[1] += 1

        if answer == student3[idx % len(student3)] :

            score[2] += 1

            

    # 3. 가장 높은 점수를 받은 사람을 구해야 한다. max() 오름차순 정렬도 해야 한다. 

    for idx, student in enumerate(score):

        if max(score) == student:

            result.append(idx+1)

            

    return result



다른 사람 풀이 1

itertools에 cycle을 이용하였습니다. 저의 첫 번째 풀이 방법인 배열을 * 복사로 늘리거나, 슬라이싱으로 자를 필요가 없게되고 간결하게 사용이 가능해집니다. zip으로 같이 사용하게 되면 cycle이 아닌 정답 배열 기준으로 반복을 돌게 됩니다.


itertools.cycle() : 순환 가능한 객체에서 요소를 반복적으로 계산합니다. 

zip() : 동일한 개수로 이루어진 자료형을 묶어줍니다.


from itertools import cycle

  

def solution(answers):

    winner = []

    pattern_supo_1 = [1 ,2, 3, 4, 5]

    pattern_supo_2 = [2, 1, 2, 3, 2, 4, 2, 5]

    pattern_supo_3 = [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]

    score_supo = [0, 0, 0]

    for supo_1, supo_2, supo_3, answer in zip(cycle(pattern_supo_1), cycle(pattern_supo_2), cycle(pattern_supo_3), answers):

        if supo_1 == answer: score_supo[0] += 1

        if supo_2 == answer: score_supo[1] += 1

        if supo_3 == answer: score_supo[2] += 1


    for i, score in enumerate(score_supo):

        if score == max(score_supo):

            winner.append(i+1)

            

    return winner


다른 사람 풀이 2

제가 풀은 정답 코드에서 엄청 간결하게 코드를 줄인 축약 버전입니다.


def solution(answers):

    p = [[1, 2, 3, 4, 5],

         [2, 1, 2, 3, 2, 4, 2, 5],

         [3, 3, 1, 1, 2, 2, 4, 4, 5, 5]]

    s = [0] * len(p)


    for q, a in enumerate(answers):

        for i, v in enumerate(p):

            if a == v[q % len(v)]:

                s[i] += 1

    return [i + 1 for i, v in enumerate(s) if v == max(s)]



Comments