[BOJ] 2108: 통계학

이슬비·2022년 3월 9일
0

Algorithm

목록 보기
8/110

오늘 푼 문제까지 해서 이제 남은 정렬 문제는 총 6개! 앞으로도 잘해보자!

2108: 통계학

먼저 코드를 공유하겠다! 틀린 코드부터...

1. 첫 번째 코드: 실패

# N: 홀수

N = int(input())

N_list = [int(input()) for _ in range(N)]

# 평균값
avg = sum(N_list)/N
print(int(avg))

# 중앙값: 정렬
ordered_list = sorted(N_list)
print(ordered_list[N//2])

# 최빈값
count = []
for i in range(N):
    count.append(N_list.count(i))
max = count.index(max(count))
print(N_list[max])

# 범위
print(ordered_list[-1]-ordered_list[0])

처음에 이 코드를 제출했었다. 정말 코린이가 제출했다고 해도 믿을만한 그런 코드... 시간 초과/메모리 초과는 하나도 고려하지 않은 그런 코드...^^ 문제가 있는 부분을 고르라면 범위 빼고 다인 것 같다 ^^...

2. 두 번째 코드: 실패

시간 초과가 떠서 그 다음으로 제출한 코드!

# N: 홀수

import sys

N = int(sys.stdin.readline())

N_list = [int(sys.stdin.readline()) for _ in range(N)]

# 평균값
avg = sum(N_list)/N
print(int(avg))

# 중앙값: 정렬
ordered_list = sorted(N_list)
print(ordered_list[N//2])

# 최빈값
count = []
for i in range(N):
    count.append(N_list.count(i))
max = count.index(max(count))
print(N_list[max])

# 범위
print(ordered_list[-1]-ordered_list[0])

배운 걸 써먹자며, 이번에는 sys를 import하여 야무지게 코드를 짜봤다. 하지만 여전히 시간초과가 났고, 인터넷을 뒤져본 결과 count가 메모리와 시간을 엄청 잡아먹는다는 것...! 역시 세상사 호락호락하지 않다.
그런데 count를 쓰는 것말고는 도무지 방법이 생각나지 않아 이번에도 인터넷의 힘을 조금 빌렸다.

3. 세 번째 코드: 성공

이 코드에서 조금 독특한 것은 바로 Collection 라이브러리의 Counter을 사용한다는 것이다.

# N: 홀수

import sys
from collections import Counter

N = int(sys.stdin.readline())
N_list = [int(sys.stdin.readline()) for _ in range(N)]

# 평균값
avg = round(sum(N_list)/N)
print(avg)

# 중앙값: 정렬
N_list.sort()
print(N_list[N//2])

# 최빈값
cnt = Counter(N_list).most_common(2)

if len(N_list) > 1:
    if cnt[0][1] == cnt[1][1]:
        print(cnt[1][0])
    else:
        print(cnt[0][0])
else:
    print(cnt[0][0])
    
# 범위
print(N_list[-1]-N_list[0])

하나하나씩 코드를 뜯어보자.

1. 평균값

# 평균값
avg = round(sum(N_list)/N)
print(avg)

이전 코드를 보면 알 수 있지만, 나는 그 전에는 모두 int()를 써서 소수점 첫째자리에서 반올림 해주었다. 정확히는 버림... 해주었다. 왜냐하면 int()가 버림을 수행하는지 몰랐기 때문... 그래서 찾아보니 round() 함수라는 것을 발견했다.

round 함수란, 반올림을 할 수 있는 함수다. 우리가 컴퓨터에서 쓰는 소수점은 부동소수점이기 때문에 정확하지 않다. (우리가 쓰는 10진수와 비교했을 때) 그래서 반올림 과정에서 이상한 점을 발견할 수 있는데, 이것과 관련해서는 아래 블로그를 참고하길 바란다.
(https://blockdmask.tistory.com/418)

2. 중앙값

# 중앙값: 정렬
N_list.sort()
print(N_list[N//2])

중앙값은 처음에 sorted()로 했다가, sort()로 바꿨다. sorted()로 하면 또 새로운 메모리에 리스트를 할당해야하니 공간복잡도가 더 커지지 않을까 생각했다.

3. 최빈값

# 최빈값
cnt = Counter(N_list).most_common(2)

if len(N_list) > 1:
    if cnt[0][1] == cnt[1][1]:
        print(cnt[1][0])
    else:
        print(cnt[0][0])
else:
    print(cnt[0][0])

대망의 최빈값...! 인터넷의 다른 분들도 그렇고, 이 부분이 제일 어렵다고 한다. 최빈값과 관련해서는 아래의 블로그를 참고하였다.
(https://somjang.tistory.com/entry/BaekJoon-2108%EB%B2%88-%ED%86%B5%EA%B3%84%ED%95%99-Python)

최빈값에서 collection의 Counter을 활용한다. 최빈값이 여러 개일 수도 있으니 most_common을 활용하여 빈도수가 높은 숫자 2개를 가져온다. 이때 만약 N_list의 길이가 2이 상이면 두 전째로 작은 값을 출력하고 그렇지 않은 경우에는 가장 빈도수가 높은 값을 그대로 출력한다.

사실 이 말이 무슨 말인지 이해가 안되었다 ^^... 지금부터 차근히 이해해보도록 하자.
Counter 클래스는 파이썬의 기본 자료구조 중 하나, dictionary를 확장하고 있다. counter의 most_common이라는 메서드는 데이터의 개수가 많은 순으로 정렬된 배열을 return 해준다. 이때의 shape은

[('l', 3), ('o',2), ('h',1)]

과 같이 (value, count) 이다. 그래서 cnt[0][1]처럼 쓸 수 있는 것이다...!

오늘도 신기한 알고리즘의 세계 끝 ~~!~!

profile
정말 알아?

0개의 댓글