BAEKJOON 2108
문제
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자. »산술평균 : N개의 수들의 합을 N으로 나눈 값 »중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값 »최빈값 : N개의 수들 중 가장 많이 나타나는 값 »범위 : N개의 수들 중 최댓값과 최솟값의 차이 N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
출력
첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값 을 출력한다. 둘째 줄에는 중앙값을 출력한다. 셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값 을 출력한다. 넷째 줄에는 범위를 출력한다.
나의 처음 코드
statistics 모듈을 import해서 푼 문제이다. 입출력 시간 줄이는건 10989 문제 포스팅에서 언급한 것과 똑같이 했다. 제일 까다로웠던 부분은, 최빈값을 구하는 과정이었다. statistics 안에 최빈값을 구하는 mode가 있지만, 중복시에는 에러가 발생해서 직접 구현해줘야 했다. key idea는 num_dic(입력받은 숫자 : 횟수)을 횟수 내림차순과 중복시에는 숫자 오름차순으로 정렬해준다. 이렇게 하면, num_dic은 우리가 원하는 모습을 갖추게 된다. 입력값이 하나일 때는, 제일 앞에 있는 item의 key가 정답이 되겠지만, 2개 이상일 경우에는 2번째 값이 우리가 원하는 값이므로 추출해주는 과정을 또 한번 시행해야한다. 그 코드는 다음과 같다.
import sys
import statistics as stat
N = int(input())
num_list = []
num_dic = {}
#숫자 입력받고 dictionary에 나온 횟수 삽입 과정
for i in range(N):
tmp = int(sys.stdin.readline())
num_list.append(tmp)
if tmp in num_dic:
num_dic[tmp] += 1
else:
num_dic[tmp] = 1
#평균은 반올림 함수 round에 전체합/개수
average = round(sum(num_list) / N)
#중앙값은 statistics 멤버함수로 있는 median_low를 사용했다.
median = stat.median_low(num_list)
#최대값과 최소값의 차이
diff = max(num_list) - min(num_list)
#최빈값 추출 과정
i = 0
pre_val = 0 #두 번째로 작은 값을 찾기 위한 변수
max_val = 0 #두 번째로 작은 값을 찾기 위한 최대 횟수가 저장되어있는 변수
#횟수로 내림차순, 값으로 오름차순으로 정렬
tmp_dic = sorted(num_dic.items(), key= lambda x : (-x[1], x[0]))
for item in tmp_dic:
if i == 0: #제일 처음 시행 되었을 때
if N == 1: #입력 받은 값이 1개일 때
mode = item[0] #그 값이 정답
break
#2개 이상일 때
max_val = item[1]
pre_val = item[0]
i += 1
else: #두 번째로 시행될 때
#같다는 것은 최빈값중에 두 번쨰로 작은 값이라는 뜻
if max_val == item[1]:
mode = item[0]
#같지 않다는 것은 바로 전 값이 유일하다는 것
else:
mode = pre_val
break
print(average)
print(median)
print(mode)
print(diff)
다른 아이디어
모두다 알다시피 dictionary는 순서가 없다. 내가 어떠한 순서로 값을 배정했다고 해도 print했을 시에는 다르게 나오는 것을 볼 수 있을 것이다. 그런데 순서가 있는 dictionary가 있다?
import collections
o_d = collections.OrderedDict()
o_d['a'] = 1
o_d['c'] = 2
o_d['b'] = 3
for item in o_d.items():
print(item)
위와 같이 했을 경우에는 순서에 맞게 a, c, b 순서대로 출력이 된다. 참 신기하죵!? 그런데 이것 말고도, 내가 몇십줄로 구현한 최빈값을 구할 수 있는 함수가 또 포함이 되어있다?
import collections
mList = ['a', 'b', 'b', 'a', 'a', 'z', 'c', 'd']
mCounter = collections.Counter(mList)
print(mCounter) #Counter({'a': 3, 'b': 2, 'z': 1, 'c': 1, 'd': 1}) 반환
print(mCounter['a']) #3을 반환, 없는 내역 집계시 0을 반환
print(mCounter.most_common(1))
#[('a', 3)]를 반환, most_common의 인자는 내역 몇 개까지 가지고 올 것이냐.
#type이 리스트이므로, 접근시 list처럼 접근해서 값을 가져오면 됨
sorted
어떠한 기준으로 정렬된 무엇인가를 리스트로 반환한다. 예를 들면 나의 코드에서 sorted(num_dic.items(), key= lambda x : (-x[1], x[0])) 부분이 있는데 num_dic을 key의 기준에 맞춰서 정렬하는 것이다. key부분의 (-x[1], x[0]) 부분은 x[1] 기준으로 내림차순으로 먼저 정렬한 후에 x[0] 기준으로 오름차순으로 정렬해준 다는 것이다. 정렬 관련되서 매우 자주 쓰는 문장들이므로, 알아두면 매우 간편해진다.
순서가 있는 dictionary라,,, 매우 신기하구먼 :)