리트코드 문제를 풀던 중, 재밌는 현상을 발견해서 간단한 실험과 검색을 통해 알아낸 것을 짧게 적으려고 한다.
재밌는(?) 현상
LeetCode(리트코드)라는 코딩 테스트를 준비할 수 있는 알고리즘 문제 사이트에서 발견한
Two Sum이라는 아주 쉬운 문제였는데, 처음에는 아래와 같은 풀이를 작성했다.
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
nums_dict = {}
for idx, num in enumerate(nums):
remain = target - num
if remain in nums_dict:
return [nums_dict[remain], idx]
nums_dict[num] = idx
그런데 Runtime이 80ms 정도로 생각보다 높게 나와서, 다른 사람들은 어떻게 풀었나 Discuss를 뒤져보다가
enumerate 대신 range를 사용한 코드들을 많이 볼 수 있었다.
설마하는 마음으로 enumerate(nums) 부분만 range(len(nums))로 바꾸어
(물론 for 문 안에서 리스트에 접근하는 방식도 nums[idx] 처럼 바뀌어야 한다)
실행해보았더니 Runtime이 44ms(!)로, 거의 두 배나 빨라진 결과를 볼 수 있었다.
간단한 실험
'희한하다..' 생각하며 ipython으로 간단한 두 함수를 만들어 for loop 속도를 측정해보기로 했다.
두 함수 모두 파라미터로 넘겨받은 배열(리스트)의 첫 인덱스부터 마지막 인덱스까지 순회하며
for문을 도는 데에 걸린 시간을 출력하는 기능을 하지만,
한 함수는 range(len())을, 다른 함수는 enumerate()를 사용하였다.
넘겨주는 배열의 크기를 10만, 100만, 1000만, 1억으로 실행해 보았는데,
근소한 차이지만 range(len())이 더 빠른 속도를 내는 것을 알 수 있었다.
(여담으로, 1억짜리 크기의 리스트를 만들고 실행하려니 내 2014년형 고물 맥북 에어는 터지려하더라)
검색
그래서 'python difference of spped between range and enumerate', 'python range len vs enumerate',
'python range enumerate 속도 차이' 등의 검색어로 구글링을 해보았는데,
range(len()) 대신 enumerate()이 Pythonic한 문법이기 때문에 사용을 권장한다는 내용들과,
속도의 차이는 그리 크지 않다는 글들을 볼 수 있었다.
다만, stackoverflow에서 이런 글을 찾을 수 있었는데(2012년의 글이다),
답변을 보면 내가 했던 간단한 실험처럼 시간을 측정하였고 range(len())이 더 빠른 것을 알 수 있었다.
결론
Python의 진면목을 제대로 사용하려면 검색을 했을 때 무수히 나온 글들처럼,
range(len())보다는 enumerate()을 쓰는 것이 좋을 듯 하다.
속도 차이도 드라마틱하게 크지 않을 뿐더러 enumerate()을 쓰면 코드를 읽기에도 훨씬 깔끔해진다.
다만, 리트코드처럼 Runtime을 변태처럼 극한까지 줄여보고 싶을 때는 range(len())도 사용해보면 좋지 않을까 싶다.
시간이 되면 두 기능의 소스 코드를 분석해보는 것도 좋을 듯 하다.
끝!