생태학에서 나무의 분포도를 측정하는 것은 중요하다. 그러므로 당신은 미국 전역의 나무들이 주어졌을 때, 각 종이 전체에서 몇 %를 차지하는지 구하는 프로그램을 만들어야 한다.
프로그램은 여러 줄로 이루어져 있으며, 한 줄에 하나의 나무 종 이름이 주어진다. 어떤 종 이름도 30글자를 넘지 않으며, 입력에는 최대 10,000개의 종이 주어지고 최대 1,000,000그루의 나무가 주어진다.
주어진 각 종의 이름을 사전순으로 출력하고, 그 종이 차지하는 비율을 백분율로 소수점 4째자리까지 반올림해 함께 출력한다.
'생태학' 문제는 입력으로 주어진 여러 개의 나무 이름이 각각 전체 입력에서 차지하는 비율을 구하는 문제이다.
출력 시에는 나무 이름을 사전순으로 정렬하고, 각 비율을 소수점 네 자리까지 출력해야 한다.
문제 자체는 어렵지 않으며, 접근 방식도 직관적이다.
즉, 나무의 빈도를 세고, 이를 전체 개수로 나눈 후 정렬하여 출력하면 되는 문제이다.
처음에는 아래와 같은 방식으로 풀었다.
from collections import Counter
a = []
while True:
try:
a.append(input())
except:
break
for key, value in sorted(Counter(a).items()):
print(key, round((value / len(a)) * 100, 4))
while True 루프를 사용하여 입력을 계속 받는다.try-except를 활용하여 EOF(End of File)가 입력될 때까지 데이터를 읽는다.Counter를 이용하여 나무의 출현 빈도를 계산한다.round(%, 4)로 출력한다.위 코드에서 시간 초과가 발생했다.
이유를 분석해보면 다음과 같다.
input()을 여러 번 호출
input()은 호출할 때마다 표준 입력에서 한 줄을 읽고 처리하는데,sys.stdin.read()를 이용하는 것이 훨씬 빠르다.sys.stdin.read() 사용위 문제를 해결하기 위해 빠른 입력 방법을 적용하였다.
import sys
from collections import Counter
a = sys.stdin.read().splitlines() # 한 번에 입력을 모두 읽고 리스트로 변환
for key, value in sorted(Counter(a).items()):
print(key, round((value / len(a)) * 100, 4))
sys.stdin.read()를 사용하여 한 번에 모든 입력을 읽음 → 입력 속도 향상 splitlines()를 사용하여 줄바꿈을 기준으로 리스트 변환 → 기존 방식과 동일한 데이터 구조 유지 이번에는 시간 초과는 해결되었지만, 오답이 발생하였다.
이유를 살펴보니, round(%, 4) 함수의 동작 방식 때문이었다.
round() 함수의 문제점round() 함수는 "은행가 반올림" (Bankers' Rounding) 방식을 따른다..xxxx5와 같은 값이 나올 경우, 가장 가까운 짝수 방향으로 반올림된다.예를 들어:
print(round(2.5)) # 2 (짝수 방향으로 반올림)
print(round(3.5)) # 4 (짝수 방향으로 반올림)
따라서 문제에서 요구하는 정밀한 반올림 방식과 다를 수 있다.
즉, round()를 사용하면 정확한 결과를 출력하지 못할 가능성이 있다.
import sys
from collections import Counter
a = sys.stdin.read().splitlines()
for key, value in sorted(Counter(a).items()):
print(f"{key} {(value / len(a)) * 100:.4f}")
f"{:.4f}"를 사용한 이유"{:.4f}".format() 또는 f"{:.4f}"는 반올림이 아닌 소수점 4자리까지만 유지하는 방식이다.sys.stdin.read() + splitlines()를 사용하면 속도가 빨라진다.round() 사용 금지 round()는 문제의 요구사항과 다르게 동작할 수 있다.f"{:.4f}" 사용