이 포스팅에서는 문제를 풀 때 알아본 파이썬의 꼬리재귀를 정리해두었다.
3개 째 풀고 있는 분할정복 문제라서 간단할 줄 알았다.
처음에 생각했던 로직대로
이 방법이 맞았지만, 세부사항에서 오래 애를 먹었다. 이런 작은 것에 막힐 땐 정말 답답하다 -_-
아래는 정답 코드
# 1074 Z
rst = 0
def getZNum(N, r, c, znum):
offset = 2**(N-1)
# print("offset, znum", offset, znum)
# print("r c", r, c)
if offset == 1:
if r<1 and c==1:
znum += 1
elif r==1 and c<1:
znum += 2
elif r==1 and c==1:
znum += 3
global rst
rst = znum
return
if r<offset and c<offset:
getZNum(N-1, r, c, znum)
elif r<offset and c>=offset:
znum += offset**2
getZNum(N-1, r, c-offset, znum)
elif r>=offset and c<offset:
znum += 2 * offset**2
getZNum(N-1, r-offset, c, znum)
else:
znum += 3 * offset**2
getZNum(N-1, r-offset, c-offset, znum)
# main
N, r, c = map(int, input().split())
getZNum(N, r, c, 0)
print(rst)
4분할 시, 범위는 어떻게 되는가?
파이썬의 꼬리재귀 문제, 재귀 None 문제
원래 return 부분 코드는 아래와 같았다.
return znum
이렇게 하니 함수의 리턴값이 None이라고 출력된다. 그래서 전역변수의 값을 수정하는 방법으로 바꿨다.
depth가 쌓이면서 이어지는 재귀를 꼬리 재귀라고 하는데, 몇 언어에서는 컴파일러 단계에서 꼬리 재귀를 최적화해준다. n-1단계의 함수를를 호출하면서, n단계의 함수를 스택에서 정리해준다는 뜻이다.
파이썬에서는 꼬리 재귀 최적화를 지원하지 않기 때문에, 스택이 계속 쌓인다. 메모리를 차지하기 때문에 (기본값) 재귀도 depth 999까지만 가능하다.
마찬가지로, 가장 안쪽의 재귀에서 znum의 값을 리턴하여도, 그 바깥의 함수에서 리턴되는 None 값이 overwrite 되어서 결국엔 None값이 getZNum의 리턴값이 된다.
offset = 1인 상태
if offset == 1:
if r<1 and c==1:
znum += 1
elif r==1 and c<1:
znum += 2
elif r==1 and c==1:
znum += 3