백준 알고리즘 2108번

수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.

  • 산술평균 : N개의 수들의 합을 N으로 나눈 값
  • 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
  • 최빈값 : N개의 수들 중 가장 많이 나타나는 값
  • 범위 : N개의 수들 중 최댓값과 최솟값의 차이

N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.


입력

첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.


출력

  • 첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.
  • 둘째 줄에는 중앙값을 출력한다.
  • 셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.
  • 넷째 줄에는 범위를 출력한다.




풀이

배운 것

  • input()이 안 먹힐 때가 있는데 sys.stdin.readline()으로 읽는 방법이 있었다.
  • collections 패키지의 Counter 모듈을 알게 됨. dict(zip())을 이용해서 빈도 수를 셌었는데, 좋은 바퀴를 발견했다.

collections의 Counter를 쓰지 않을 땐 아래처럼 했다.


1
2
3
4
5
6
7
8
9
10
11

def mode(nums):
nums = sorted(nums)
mode_dict = dict(zip(nums, [0]*len(nums)))

for n in nums :
mode_dict[n] += 1

modes = [ k for k, v in mode_dict.items() if v == max(mode_dict.values())]

...(생략)...

성공 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import sys 
from collections import Counter

#main
t = int(sys.stdin.readline())

numbers = []
for _ in range(t):
numbers.append(int(sys.stdin.readline()))

def mean(nums):
return round(sum(nums)/len(nums))

def median(nums):
nums.sort()
mid = nums[len(nums)//2] # nums의 개수는 홀수

return mid

def mode(nums):
mode_dict = Counter(nums)
modes = mode_dict.most_common()

if len(nums) > 1 :
if modes[0][1] == modes[1][1]:
mod = modes[1][0]
else :
mod = modes[0][0]
else :
mod = modes[0][0]

return mod

def scope(nums):
return max(nums) - min(nums)

print(mean(numbers))
print(median(numbers))
print(mode(numbers))
print(scope(numbers))

오류의 원인

오류의 원인이었던 mode 함수만 다시 살펴본다.


1
2
3
4
5
6
7
8
9
10
11
12
13
def mode(nums):
mode_dict = Counter(nums)
modes = mode_dict.most_common() #1

if len(nums) > 1 :
if modes[0][1] == modes[1][1]: #2
mod = modes[1][0]
else :
mod = modes[0][0]
else :
mod = modes[0][0] #3

return mod

  • #1 : from collections import Counter 에서 Counter로 만든 딕셔너리는 most_common() 함수로 최빈값을 찾을 수 있다. 이때, 같은 빈도를 가지는 수는 원래 시퀀스에 있던 순서대로 나열된다. median 함수에서 sort()함수로 이미 nums 시퀀스를 정렬했으므로 다시 정렬해줄 필요 없다.

  • #2 : modes[0][1]은 가장 앞에 있는 최빈값의 빈도수이다. modes[1][1]은 그 다음 최빈값의 빈도수이다. 이 둘이 같다면 최빈값이 최소 2개 이상 있다는 뜻인데, 이 경우, 두번째로 작은 값을 채택해야 하므로 modes[1][0]을 mod에 저장한다.

  • #3 : 만약 modes의 개수가 1라면 맨 첫번째 수를 mod에 저장한다.



아래는 계속해서 틀렸던 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def mean(nums):
return round(sum(nums)/len(nums))

def median(nums):
nums = sorted(nums)
idx = round(len(nums)/2)
return nums[idx]

def mode(nums):
from collections import Counter
mode_dict = Counter(nums)
modes = [ k for k, v in mode_dict.items() if v == max(mode_dict.values())]
modes = sorted(modes)

return modes[0] if len(modes) == 1 else modes[1]

def scope(nums):
return max(nums) - min(nums)

# main
import sys

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

numbers = []
for _ in range(t):
numbers.append(int(sys.stdin.readline()))

print(mean(numbers))
print(median(numbers))
print(mode(numbers))
print(scope(numbers))