본문 바로가기
IT/CodingTest

[차근차근 풀기] [python] 백준 2108 - 통계학

by Echinacea 2025. 11. 9.
반응형

 

 

 

이 문제는 겉만 보면 통계 문제인데, 알고 보면 구현 디테일을 검증하기 위한 문제이다.

N이 최대 50만 개나 되니 구현시 속도에 신경써야 한다.

 

먼저 산술평균, 중앙값, 최빈값, 범위 네 개를 구해야 하므로 일단 입력을 받는 라인을 작성한다.

 

1단계: 입력 받기 및 환경 설정

N이 최대 50만개라, 그냥 input() 쓰면 시간 초과 날 수 있다.

그래서 안전하게 sys 모듈 가져와서 입력 속도 빠르게 바꾸는 걸 추천한다.

 
import sys
input = sys.stdin.readline

 

N번 반복해서 숫자들을 리스트에 담아야 하므로 리스트 이름은 numbers로 지정해 보겠다.

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

 


 

2단계: 중앙값, 최솟값, 최댓값 준비 (정렬)

중앙값이나 범위를 구하려면 일단 숫자들이 순서대로 있어야 편리하다.

정렬하는건 어렵지 않으므로 정렬해두는 게 나중에 디버깅도 편하다.

# 중앙값, 범위 계산을 위해 정렬이 필수야.
nums.sort()

 

1. 중앙값 (Median)

N은 홀수라고 했으니, 정렬된 리스트에서 딱 중간에 있는 값을 찾으면 된다.

중간 인덱스는 N을 2로 나눈 몫만 구하면 되므로 N // 2로 구현하면 된다.

# N이 홀수이므로, 정렬 후 N/2 인덱스가 중앙값이야.
median = nums[N // 2]

 

2. 범위 (Range)

범위는 가장 큰 값에서 가장 작은 값을 빼면 된다.

정렬했으니까 제일 작은 건 numbers[0]이고, 제일 큰 건 numbers[-1]로 처리하면 됨

# 정렬했으니, 마지막 값(최댓값) - 첫 번째 값(최솟값)
range_val = nums[-1] - nums[0]

 

3단계: 산술평균 계산

평균은 모든 숫자를 더한 다음 N으로 나누면 된다.

파이썬의 sum() 함수로 간단하게 구현하자.

여기서 주의할 점은

소수점 첫째 자리에서 반올림 < 이 부분이다. 파이썬의 round 함수룰 사용하면 안 되는게 이 문제의 함정이다

왜냐면 round 함수는 '오사오입 규칙'을 갖기 때문이다. 하필 귀찮게...

 

오사오입 (Banker's Rounding) 규칙

파이썬의 round() 함수는 소수점 첫째 자리가 5일 경우에만 특수한 규칙을 적용합니다.

  • 5 미만: 버림 (일반 반올림과 동일)
  • 5 초과: 올림 (일반 반올림과 동일)
  • 딱 5일 때: 앞자리가 짝수면 버리고, 홀수면 올립니다. (즉, 결과가 짝수가 되도록 반올림합니다.)
일반적인 반올림 (문제 요구사항) round() 함수의 결과 (오사오입)
2.5 3 2 (앞자리 2 짝수이므로 버림)
3.5 4 4 (앞자리 3 홀수이므로 올림)
4.5 5 4 (앞자리 4 짝수이므로 버림)

 

컴퓨터의 원리 때문에 이렇게 지정되었다지만 코테 푸는 입장에선 번거로운 포인트가 아닐 수 없다,,

그래서 아래와 같이 코드를 입력해야 하는데, 그 이유는 다음과 같다.

total = sum(nums)
avg = total / N
mean = int(avg + 0.5) if avg >= 0 else int(avg - 0.5)

 

 

1. 양수 처리시

int(avg + 0.5)
  • 원리: 반올림할 때, 0.5를 더한 후 정수 부분만 취하면 된다.
  • 예시:
    • 3.4의 반올림 :  3.4 + 0.5 = 3.9 = 결과값 : 3 (버림)
    • 3.5의 반올림 : 3.5 + 0.5 = 4.0 = 결과값 : 4 (올림)
    • 3.6의 반올림 : 3.6 + 0.5 = 4.1 = 결과값 : 4 (올림)

2. 음수 처리시

int(avg - 0.5)
  • 원리: 음수를 반올림할 때 0.5를 빼준 후 정수 부분만 취해야 한다.
  • 예시:
    • -3.4의 반올림 : -3.4 - 0.5 = -3.9  = 결과값 : -3 
    • -3.5의 반올림 : -3.5 - 0.5 = -4.0  = 결과값 : -4 
    • -3.6의 반올림 : -3.6 - 0.5 = -4.1  = 결과값 : -4 

 

 

 

4단계: 최빈값 계산 (가장 까다로운 부분)

가장 많이 나오는 숫자를 찾아야 하는데, N이 50만 개라서 하나하나 세면 속도가 느려질 수 있다.

문제에서 '입력되는 정수의 절댓값은 4,000을 넘지 않는다.'라고 했으므로. 이걸 활용해야 한다.

 

일단 collections의 Counter 없이 딕셔너리를 써서 빈도수를 계산해보자.

 

딕셔너리(freq)를 만들어고, get(x, 0) + 1 을 이용해 세면 깔끔하게 셀 수 있다.

freq = {}
for x in nums:
    # 딕셔너리에 x가 없으면 0을 반환받아 1을 더하고, 있으면 기존 값에 1을 더함.
    freq[x] = freq.get(x, 0) + 1

 

 

이렇게 만들어진 딕셔너리에서 가장 큰 값이 몇인지만 찾아서 변수에 넣어주면 된다.

max_cnt = max(freq.values())

 

 

만들어진 freq 딕셔너리에서 max_cnt로 나온 값을 기준으로 '두 번째로 작은 값'을 찾으면 된다.

# 최대 빈도수를 가진 숫자들만 모아서 오름차순 정렬
cands = sorted([k for k, v in freq.items() if v == max_cnt])

 

이때 최빈값이 여러 개면 두 번째로 작은 값인 cands[1]을 뽑고, 아니면 그냥 cands[0]이 정답이 된다.

# 최빈값이 여러 개(cands 길이가 1 초과)면 두 번째 값(인덱스 1), 아니면 첫 번째 값(인덱스 0) 선택
mode = cands[1] if len(cands) > 1 else cands[0]
 

 

5단계: 최종 출력

이대로 출력만 하면 된다.

print(avg)
print(median)
print(mode)
print(range_val)

 

 

최종 코드

import sys
input = sys.stdin.readline

N = int(input())
nums = [int(input()) for _ in range(N)]
nums.sort()

# 1. 산술평균 (반올림: half-up 방식)
total = sum(nums)
avg = total / N
mean = int(avg + 0.5) if avg >= 0 else int(avg - 0.5)

# 2. 중앙값
median = nums[N // 2]

# 3. 최빈값
freq = {}
for x in nums:
    freq[x] = freq.get(x, 0) + 1

max_cnt = max(freq.values())
cands = sorted([k for k, v in freq.items() if v == max_cnt])
mode = cands[1] if len(cands) > 1 else cands[0]

# 4. 범위
range_val = nums[-1] - nums[0]

print(mean)
print(median)
print(mode)
print(range_val)
반응형

댓글