경쟁 프로그래밍에서 빠른 입출력이 중요한 이유
경쟁 프로그래밍(Competitive Programming)에서는 입력을 최대한 빠르게 읽어들이는 것이 다른 참가자보다 앞서는 핵심 요소입니다. 이 글에서는 파이썬(Python)으로 경쟁 프로그래밍을 할 때 활용할 수 있는 다양한 입출력(I/O) 방법을 하나씩 살펴봅니다.
예를 들어 Codeforces나 SPOJ 같은 온라인 저지 사이트에서 네 개의 숫자 a, b, c, d를 입력받아 곱을 출력하는 문제가 주어졌다고 가정해 봅시다. 이 문제를 해결하는 방법은 여러 가지가 있습니다.
기본적인 입력 방법
방법 1: 리스트 컴프리헨션 사용
a, b, c, d = [int(x) for x in input().split()] print(a*b*c*d)
리스트 컴프리헨션(list comprehension)을 사용하면 한 줄로 입력된 값들을 손쉽게 정수형으로 변환할 수 있습니다.
방법 2: map 함수 사용
a, b, c, d = map(int, input().split()) print(a*b*c*d)
map 함수를 사용하면 코드가 더 간결해지고, 리스트 컴프리헨션보다 실행 속도도 약간 더 빠릅니다.
sys.stdin과 sys.stdout 활용하기
내장 input() 함수보다 훨씬 빠른 처리 속도가 필요하다면 sys 모듈의 stdin과 stdout을 사용하는 것이 좋습니다.
방법 3: stdin/stdout과 리스트 컴프리헨션 조합
from sys import stdin, stdout a, b, c, d = [int(x) for x in stdin.readline().rstrip().split()] stdout.write(str(a*b*c*d) + "\n")
stdin.readline()은 줄 끝의 개행 문자(\n)까지 함께 읽어오므로, rstrip()으로 제거한 후 split()을 적용하는 것이 일반적입니다.
실전 예제: SPOJ INTEST 문제
이번에는 실제 경쟁 프로그래밍 문제인 SPOJ의 INTEST - Enormous Input Test를 통해 입출력 방식에 따른 성능 차이를 직접 확인해 보겠습니다.
문제 설명
- 입력: 첫 번째 줄에 두 개의 양의 정수 n과 k가 주어집니다(n, k ≤ 10⁷). 이후 n개의 줄에 걸쳐 10⁹ 이하의 양의 정수 tᵢ가 하나씩 주어집니다.
- 출력: k로 나누어 떨어지는 정수 tᵢ의 개수를 출력합니다.
예제
Input 7 3 1 51 966369 7 9 999996 11 Output 4
위 예제에서 k=3으로 나누어 떨어지는 수는 51, 966369, 9, 999996으로 총 4개입니다.
방법 1: 기본 input() 함수 사용
def main():
n, k = [int(c) for c in input().split()]
cnt = 0
for _ in range(n):
t = int(input())
if t % k == 0:
cnt += 1
print(cnt)
if __name__ == "__main__":
main()
가장 직관적인 방법이지만, 입력량이 많을 때는 실행 시간이 오래 걸려 시간 초과(TLE)가 발생할 수 있습니다.
방법 2: stdin.readline() 사용
from sys import stdin, stdout
def main():
n, k = [int(c) for c in input().split()]
cnt = 0
for _ in range(n):
t = int(stdin.readline())
if t % k == 0:
cnt += 1
stdout.write(str(cnt))
if __name__ == "__main__":
main()
stdin.readline()을 사용하면 input()보다 훨씬 빠르게 입력을 처리할 수 있어 실행 시간이 크게 단축됩니다.
방법 3: readlines()로 전체 입력 한 번에 읽기
from sys import stdin, stdout
def main():
n, k = stdin.readline().split()
n = int(n)
k = int(k)
cnt = 0
lines = stdin.readlines()
for line in lines:
if int(line) % k == 0:
cnt += 1
stdout.write(str(cnt))
if __name__ == "__main__":
main()
readlines()를 사용하면 남은 입력 전체를 한 번에 리스트로 불러옵니다. 반복 호출에 따른 오버헤드가 사라지기 때문에 앞선 두 방법보다도 훨씬 빠른 성능을 보여줍니다.
정리 및 추가 팁
경쟁 프로그래밍에서 파이썬의 입출력 성능을 극대화하려면 다음 사항을 기억하세요.
- input() → sys.stdin.readline(): readline으로 바꾸는 것만으로도 상당한 속도 향상을 얻을 수 있습니다.
- readlines() 또는 sys.stdin.read(): 입력량이 매우 클 경우 전체를 한 번에 읽어 처리하는 것이 가장 빠릅니다.
- print() → sys.stdout.write(): 출력이 많은 문제에서도 write를 사용하면 도움이 되며, 출력 횟수 자체를 줄이는 것도 효과적입니다.
- rstrip() 습관 들이기: 운영체제에 따라 줄바꿈 문자가 \r\n일 수 있으므로 rstrip()으로 안전하게 제거하는 것이 좋습니다.
입력 데이터의 크기가 큰 문제일수록 입출력 방식의 선택이 결과를 좌우합니다. 위에서 소개한 방법들을 상황에 맞게 활용해 시간 초과를 피하고 좋은 성적을 거두시길 바랍니다.