기초 개념
재귀
거울 앞에 거울을 두면
맨 안쪽 상자를 열면 무엇이 나올까요? 먼저 예상을 고르세요
예상을 고르면 상자가 열립니다
처음 예상은 달랐습니다. 맨 안쪽에는 구슬만 있었고 더 열 상자는 없었습니다.
구슬 4개를 세려면 상자를 4개 거쳐야 했습니다. 닫을 때마다 안쪽 답에 1을 더해 바깥 답이 정해졌습니다.
상자 1 열기 → 상자 2 열기 → 상자 3 열기 → 상자 4 열기 → 구슬닫을 때 답이 1 → 2 → 3 → 4 로 이어졌습니다.
- 열 때 — 안쪽으로 한 겹씩. 답은 아직 모른다
- 맨 안쪽 — 더 열 상자가 없다. 여기서 첫 답(1)이 생긴다
- 닫을 때 — 안쪽 답에 1 을 더해 바깥 답이 정해진다. 안쪽이 끝나야 바깥을 안다
자기 안에서 같은 일을 다시 하는 방법 = 재귀
거울 앞에 거울을 두면 거울 속에 거울이 끝없이 이어집니다. 상자는 맨 안쪽에서 끝났습니다. 그래서 답이 나왔습니다.
재귀에는 두 가지가 꼭 있다
- 멈추는 자리 — 구슬만 있는 맨 안쪽 상자. 더 부르지 않고 답을 돌려준다
- 작아지는 입력 — 열 때마다 남은 상자가 하나씩 줄었다
- 하나라도 빠지면 — 끝없이 열기만 한다
코드는 상자 열기를 그대로 옮긴 것이다
factorial(4) 는 1 부터 4 까지 곱합니다. 다음을 눌러 한 줄씩 넘기며 층이 쌓이고 걷히는 걸 보세요. 중간에 한 번 물어봅니다.
- factorial(4)실행 중
맨 처음 부른 곳
다음을 눌러 한 줄씩 넘겨 보세요
n = 4
factorial.py
- 1def factorial(n):
- 2 if n == 1:
- 3 return 1
- 4 return n * factorial(n - 1)
0 / 12
if n == 1— 멈추는 자리. 여기서만 자기를 부르지 않는다factorial(n - 1)— 한 칸 작아진 입력으로 자기를 부른다. 층이 하나 쌓인다n * …— 안쪽 층이 값을 돌려주면 그제야 곱한다. 층이 걷힌다
자기를 두 번 부르면 나무가 된다
factorial 은 한 줄로 쌓입니다. 이런 문제는 반복문으로 써도 쉽습니다. 호출이 둘로 갈라지면 모습이 달라집니다.
- 한 줄로 쌓이면 — 반복문으로 바꿔도 쉽다
- 둘로 갈라지면 — 반복문으로는 어디까지 했는지 직접 적어 두어야 해서 훨씬 어렵다
- 폴더 안에 폴더 — 안에 같은 것이 든 구조도 재귀로 쓰면 짧아진다
factorial(4) 에서 곱셈은 어떤 순서로 일어날까요?
멈추는 자리를 빼먹으면
if n == 1이 없으면 — n 이 0, −1, −2 … 로 끝없이 내려간다- 1000층쯤 쌓이면 — 파이썬이
RecursionError를 내고 멈춘다. 기본 한도가 1000 이다 factorial(0)— 이 코드는 n 이 1 일 때만 멈춘다. 0 에서 시작하면 한도까지 내려가다 터진다sys.setrecursionlimit(10**6)— 코딩테스트에서는 한도를 이렇게 늘려 쓴다. 너무 깊으면 한도에 닿기 전에 메모리가 먼저 터질 수 있다
깊이가 1만, 10만이 될 수 있는 문제는 재귀 대신 반복문으로 바꾸는 쪽이 안전합니다.
문제에서 이 말이 보이면
같은 모양의 더 작은 문제자기를 둘 이상으로 나눈다폴더·트리처럼 안에 같은 것이 든다
다음 장에서는 일단 전부 해 보는 완전탐색을 봅니다.