| English | 의미 |
|---|---|
| recursive/rɪˈkɜːsɪv/ | 재귀적 |
| base case/beɪs keɪs/ | 기본 경우 |
| recursive case/rɪˈkɜːsɪv keɪs/ | 재귀 경우 |
| call stack/kɔːl stæk/ | 호출 스택 |
| stack frame/stæk freɪm/ | 스택 프레임 |
| stack overflow/stæk ˌəʊvəˈfləʊ/ | 스택 오버플로우 |
| memoisation/ˌmeməʊaɪˈzeɪʃn/ | 메모이제이션 |
자신을 포함하는 정의
- 조상(ancestor)이 무엇인지 어떻게 정의할 수 있는가? 당신의 부모는 조상이다. 또한 부모의 조상도 조상이다. 이 두 문장은 무한한 사슬을 정의하며, 두 번째 문서는 그 자체가 정의하는 단어를 사용하고 있다.
- 이는 순환 논리가 아니다. 첫 번째 문장이 사슬이 멈출 수 있는 지점을 제시하기 때문이다. 이것이 없다면 정의는 끝없이 풀려 나가게 된다.
- 프로그램은 같은 방식으로 작성될 수 있으며, 이러한 사슬 형태로 형성된 문제에 대해 재귀적 해법은 루프보다 현저히 짧다.
- 이 수업은 기본 경우(base case) 와 재귀적 경우(recursive case), 재귀 호출이 추적되는 방식, 그리고 아래에서 호출 스택(call stack) 이 무엇을 하는지 다룬다.
두 가지 경우
FUNCTION Factorial(n : INTEGER) RETURNS INTEGER
IF n = 0 OR n = 1 THEN
RETURN 1 // base case
ELSE
RETURN n * Factorial(n - 1) // recursive case
ENDIF
ENDFUNCTION
- 기본 경우는 더 이상 호출 없이 직접 답할 수 있을 만큼 작은 문제의 버전이며, 이것이 재귀를 멈춘다.
- 재귀적 경우는 더 작은 입력으로 함수를 다시 호출하여 기본 경우로 나아가게 한다.
- 둘 다 필요하다. 기본 경우가 없는 재귀는 절대 멈추지 않으며, 입력이 shrinking되지 않는 재귀는 기본 경우에 도달하지 못한다.
모든 재귀 알고리즘에는 기본 경우(base case)가 있어야 하는 이유는:
난이도 1/5 · 100 코인 + 오늘 첫 완료 시 소액 보너스
기본 경우는 호출 사슬을 끝내는 조건입니다; 없으면 재귀가 영원히 실행됩니다.
재귀는 자기 유사성 문제(트리, 분할 정복)에 자연스러운 적합성을 가지지만, 각 호출마다 스택 프레임이 추가되므로 기본 경우가 없으면 스택 오버플로우가 발생합니다.
난이도 1/5 · 100 코인 + 오늘 첫 완료 시 소액 보너스
단순한 카운팅 루프는 순수 반복에 더 깔끔합니다; 문제는问题时自身의 작은 복사본을 포함할 때 재귀가 빛을 발합니다.
재귀 routine이 종료를 위해 갖추어야 할 것을 모두 선택하십시오.
난이도 2/5 · 140 코인 + 오늘 첫 완료 시 소액 보너스
기본 경우만으로는 충분하지 않습니다: 입력이 절대 shrinking되지 않으면 기본 CASE에 도달하지 못해 프레임이 쌓이고 스택이 오버플로우됩니다.
worked example: 재귀 추적(tracing)
Factorial(4)추적(trace).- Winding up:
Factorial(4)needs4 * Factorial(3), which needs3 * Factorial(2), which needs2 * Factorial(1). Nothing has been multiplied yet; each call is waiting. - 기본 경우:
Factorial(1)은 아무것도 호출하지 않고 1을 반환한다. - 호출 축소(Unwinding):
2 * 1 = 2이 먼저 반환되고, 이후3 * 2 = 6, ثم4 * 6 = 24가 반환된다. - 양방향 모두를 보여라. 아래로만 가거나 위로만 돌아오는 추적은 점수의 절반을 잃게 된다.
재귀는 잎(leaf)에서 위로 unfolds됩니다.
fib(4) 평가 시 실제 호출이 완료되는 순서대로 단계별 진행: 잎(기본 경우)이 먼저 해결되고, 각 부모가 자식을 결합합니다. fib(2)가 두 번 계산됨을 주의하십시오—이 반복 작업이 naive 재귀가 느린 이유입니다.
Factorial(4)는 무엇을 반환합니까?
난이도 2/5 · 140 코인 + 오늘 첫 완료 시 소액 보너스
4 × 3 × 2 × 1 = 24.
기계가 하는 일
- 각 호출은 개별적인 매개변수와 로컬 변수의 복사본을 필요로 한다. 왜냐하면
Factorial(3)과Factorial(2)은 서로 다른n값을 가진 별개의 호출이기 때문이다. - 이 복사본들은 호출 스택(call stack) 위의 스택 프레임(stack frame) 에 위치한다: 진행 중인 호출마다 하나의 프레임이 있으며, 매개변수, 로컬 변수, 반환 주소를 담고 있다.
- 프레임은 각 호출 시 밀어放入(pushed) 되고, 반환 시 꺼냄(popped) 된다. 이것이 값들이 호출된 순서의 역순으로 돌아오는 이유이다: 호출 스택은 정확히 주제 10의 ADT인 스택이다.
Factorial(4) 평가 시 발생한 사건들을 순서대로 나열하십시오.
난이도 3/5 · 200 코인 + 오늘 첫 완료 시 소액 보너스
내려가는在这个过程中 아무 것도 곱해지지 않습니다; 모든 호출이 대기합니다. 곱셈은 모두 스택이 unfold될 때 일어납니다.
문제가 발생하는 경우
- 기본 CASE가 없거나 도달할 수 없는 기본 CASE: 재귀가 멈추지 않으며 프레임이 쌓이고 호출 스택의 메모리가 고갈된다. 이를 스택 오버플로우(stack overflow) 라고 한다.
- 깊은 재귀:百万 번의 레벨에 대한 올바른 재귀even도 million frames를 필요하므로, 루프에서는 사용하지 않는 메모리를 소모할 수 있다.
- 중복 작업:-naive recursive Fibonacci는 동일한 값을 지수적으로 많은回数重新计算。루프로 수정하거나, 메모이제이션(memoisation) 을 사용하여 계산된 결과를 처음에 저장함으로써 해결한다.
각 재귀 용어를 그 의미로 매칭하십시오.
난이도 2/5 · 140 코인 + 오늘 첫 완료 시 소액 보너스
재귀에는 멈추기를 위한 기본 경우와 문제를 축소하기 위한 재귀 경우가 필요하며, 각 호출마다 스택 프레임이 추가됩니다.
함수 호출용 스택 프레임이 포함하는 것은:
난이도 1/5 · 100 코인 + 오늘 첫 완료 시 소액 보너스
각 프레임은 해당 호출의 매개변수, 로컬 변수 및 재시작 위치를 저장하므로, 호출들이 서로를 덮어쓰지 않습니다.
진행 중인 각 호출은 자신의 ____에 매개변수와 로컬 변수를 유지합니다. (call stack)
난이도 1/5 · 100 코인 + 오늘 첫 완료 시 소액 보너스
호출 시 스택에 적재(pushed)되고, 반환 시 제거(popped)됩니다. 이것이 값이 호출된 순서와 반대로 돌아오는 이유입니다.
재귀 또는 반복(iteration)
- 재귀는 자기 유사(self-similar) 문제에 적합하다. 즉, 문제가 자체의 더 작은 복사본을 포함하는 경우: 트리 Traverse, 이진 검색 및 병합 정렬과 같은 분할 정복(divide and conquer), 그리고 위 조상 사슬.
- 반복(iteration)은其余 everything에 적합하며, 반복에 대해 추가 메모리를 사용하지 않는다.
- 모든 재귀적인 것은 반복적으로 작성할 수 있으며, 그 반대도 true이다. 선택은 스택 비용이라는 메모리와 대비하여 어떤 것이 문제를 명확하게 표현하는지에 관한 것이다.
재귀 routine이 스택 오버플로우로 충돌했습니다. 올바른 설명은 무엇입니까?
난이도 1/5 · 100 코인 + 오늘 첫 완료 시 소액 보너스
스택 오버플로fram는 산술 연산이 아닌 프레임 문제입니다. 레지스터에 담을 수 없는 숫자는 산술 오버플로우로, 완전히 다른 현상입니다.
worked example: 위험을 서술하라
- 학생이 재귀 routine을 작성하고 스택 오버플로우로 crash된다. 가능한 원인을 두 가지给出.
- 기본 CASE가 없거나, 입력이 각 호출마다 작아지지 않아 기본 CASE에 도달할 수 없어 호출이 계속되고 프레임이 축적된다.
- 재귀는 correct하지만 너무 깊다: 매우 많은 호출 중 각각이 자체의 스택 프레임을 유지하므로, 기본 CASE에 도달하기 전에 호출 스택의 메모리가 고갈된다.
- 두 원인 모두 프레임이 축적되는 것과 관련이 있다. 무엇이 축적되고 왜 멈추지 않는지 말하라.
놓칠 수 있는 점
- 재귀에는 기본 CASE와 더 작아지는 입력이 둘 다 필요하다. 기본 CASE만 언급하는 것은 조건的一半이다.
- 추적(tracing)에서 호출이 확장(winding up) 되는 것과 값이 축소(unwinding) 되는 것을 보여라. 양 방향 모두 점수를 부여한다.
- 각 호출은 자체 스택 프레임에 개별적인 매개변수와 로컬 변수를持有。이것이 재귀가 반복(iteration)이 소모하지 않는 메모리를 소모하는 이유이다.
- 스택 오버플로우는 산술 오버플로우가 아니라 프레임이 너무 많아 스택 메모리를 고하는 것이다.
이해했습니다
- 재귀 routine은 기본 CASE를 직접 해결하고, 재귀적 경우에서 더 작은 입력으로 self를 호출해야 한다.
- 양방향으로 추적하라: 호출이 기본 CASE까지 확장되고,値이 다시 축소되어 돌아옴.
- 각 호출은 호출 스택 위에 자체 스택 프레임을持有하며, 호출 시 밀어放入되고 반환 시 꺼내므로, 이것이 재귀가 메모리를 소모하는 이유이다
- 리스크: 도달 가능한 기본 사례가 없어 무한 재귀와 스택 오버플로우가 발생하며, 깊은 재귀는 메모리를 소모하고, 반복적인 작업은 루프나 메모이제이션이 필요합니다