https://www.acmicpc.net/problem/2108
2108번: 통계학
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
www.acmicpc.net
문제
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.
- 산술평균 : N개의 수들의 합을 N으로 나눈 값
- 중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
- 최빈값 : N개의 수들 중 가장 많이 나타나는 값
- 범위 : N개의 수들 중 최댓값과 최솟값의 차이
N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.
출력
첫째 줄에는 산술평균을 출력한다. 소수점 이하 첫째 자리에서 반올림한 값을 출력한다.
둘째 줄에는 중앙값을 출력한다.
셋째 줄에는 최빈값을 출력한다. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력한다.
넷째 줄에는 범위를 출력한다.
코드 정답
import sys
from collections import Counter
input = sys.stdin.readline
n = int(input())
li = []
# 입력 조건
if (n % 2 == 0 or n > 500000 or n < 1):
exit()
for _ in range(n):
li.append(int(input()))
li.sort()
# 산술평균
if sum(li) / n >= 0:
print(int(sum(li) / n + 0.5))
elif sum(li) / n < 0:
print(int(sum(li) / n - 0.5))
# 중앙값
print(li[(n) // 2])
# 최빈값
cnt = Counter(li).most_common(2)
if len(li) > 1:
if cnt[0][1] == cnt[1][1]:
print(cnt[1][0])
else:
print(cnt[0][0])
else:
print(cnt[0][0])
# 범위
print(abs(li[-1] - li[0]))
풀이
1~3행 : 모듈들을 사용하기 위해 선언해주는 곳이다. 일반적으로 input()을 사용하게 된다면 파이썬은 시간 초과가 나오기에 import sys와 imput = sys.stdin.readline 을 해주게 된다면 input 을 사용해도 sys.stdin.readline 을 사용하는 것과 같게 된다. from collections import Counter 는 collections 모듈의 Counter 클래스를 사용한다는 의미로 Counter 는 기본적으로 여러 형태의 데이터를 인자로 받아 중복된 데이터가 저장된 배열을 인자로 넘기면 각 원소가 몇 번씩 나오는지가 저장된 객체를 얻게 된다.
import sys
from collections import Counter
input = sys.stdin.readline
5~6행 : 변수 n 에는 입력될 값의 갯수를 담게 되고 변수 li 는 리스트 형식으로 선언해준다.
n = int(input())
li = []
9~10행 : 문제의 입력 조건 부분이다. n 이 짝수이거나, 1보다 작고, 50만보다 커도 안되기에 한 가지라도 해당된다면 종료되게끔 만들어주었다.
if (n % 2 == 0 or n > 500000 or n < 1):
exit()
12~14행 : n 에 저장된 값의 횟수만큼 반복하며 리스트에 정수값을 입력받아 저장해준다. 이후 저장된 값들을 오름차순으로 정렬시킨다.
for _ in range(n):
li.append(int(input()))
li.sort()
17~20행 : sum() 을 이용하여 리스트에 저장된 값들을 전부 더해 n 의 값으로 나눈다. 그 값이 0보다 크거나 같을 경우 나눈 값이 0.5를 더하고 정수형으로 강제변환 해주였고, 0보다 작다면 n으로 나눈 값에 0.5를 빼낸 값을 정수형으로 바꿔주었다. round() 를 사용하면 되지 않나? 라고 생각할 수 있지만 round 함수에는 치명적인 문제가 존재한다. 바로 1.5 / 3.5 처럼 정수 부분이 홀수일때 0.5가 붙어있다면 값을 반올림해주고, 2.5 / 4.5 처럼 정수 부분이 짝수일때 0.5가 붙어있다면 내림 현상이 발생된다. 그렇기에 확실하게 해주기 위하여 0.5를 더하거나 빼내주는 방식을 채택하게 되었다.
# 산술평균
if sum(li) / n >= 0:
print(int(sum(li) / n + 0.5))
elif sum(li) / n < 0:
print(int(sum(li) / n - 0.5))
23행 : n의 값에 2를 나눈 몫(n이 5일 경우 2)을 인덱스 값으로 가져와 리스트에 저장된 값을 출력한다. 어차피 조건에서 홀수만 입력이 되기에 홀수일 경우는 생각해 줄 필요가 없다.
# 중앙값
print(li[(n) // 2])
26~33행 : 이 부분을 혼자 해보려고 했다가 계속되는 실패로 구글링의 힘을 빌리게 되었다. li에 저장된 값들을 Counter을 사용하여 중복을 확인해주며 .most_common(2) 를 활용하여 빈도수가 높은 숫자 2개를 가져와준다. li의 길이가 2이상. 즉, 값이 2개 이상 저장되어 있을 경우 조건문으로 들어가 다시 한 번 Counter로 빼내온 값 2개의 갯수를 비교한다. 만약에 갯수가 같다면 두 번째 저장된 값을 출력하고 아니라면 첫번째에 저장된 값을 출력한다. 또한, 길이가 2가 넘지 않더라고 cnt에 저장된 첫번째 값이 출력이 된다.
# 최빈값
cnt = Counter(li).most_common(2)
if len(li) > 1:
if cnt[0][1] == cnt[1][1]:
print(cnt[1][0])
else:
print(cnt[0][0])
else:
print(cnt[0][0])
36행 : 인덱스 값에 -1 이 들어가게 된다면 가장 끝 값(문제 기준 가장 큰 값)이 선택이 된다. 가장 큰 값에서 가장 작은 값을 빼내고 abs() 를 이용하여 절대값으로 만들어 준 다음 출력을 해준다.
print(abs(li[-1] - li[0]))
내 수준에서는 아직 어려웠던 문제라고 생각이 든다. 이 문제는 나중에 실력을 쌓게 된 후 다시 찾아와서 풀어보고 싶다.
'BaekJoon > 단계별로 풀어보기' 카테고리의 다른 글
[백준] 정렬 - 11650번 (Python) (0) | 2023.02.12 |
---|---|
[백준] 정렬 - 1427번 (Python) (0) | 2023.02.10 |
[백준] 정렬 - 10989번 (Python) (0) | 2023.02.08 |
[백준] 정렬 - 2751번 (Python) (0) | 2023.02.07 |
[백준] 정렬 - 25305번 (Python) (0) | 2023.02.06 |