재귀가 사용되는 모든 곳에서 for
루프를 사용할 수 있다고 말하는 것이 맞 습니까? 그리고 재귀가 일반적으로 느리다면 for
루프 반복을 통해 사용하는 기술적 이유는 무엇 입니까?
그리고 재귀를 for
루프 로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?
답변
재귀는 일반적으로 호출자 함수로 돌아갈 수 있도록 모든 함수 호출을 스택에 저장해야하기 때문에 훨씬 느립니다. 대부분의 경우 범위 격리를 구현하려면 메모리를 할당하고 복사해야합니다.
꼬리 호출 최적화 와 같은 일부 최적화 는 재귀를 더 빠르게 만들지 만 항상 가능하지는 않으며 모든 언어로 구현되지는 않습니다.
재귀를 사용하는 주된 이유는 다음과 같습니다.
- 문제에 대한 우리의 접근 방식을 모방 할 때 많은 경우에 더 직관적입니다.
- 트리와 같은 일부 데이터 구조는 재귀를 사용하여 탐색하기가 더 쉽습니다 (또는 어떤 경우에도 스택이 필요함).
물론 모든 재귀 는 일종의 루프로 모델링 될 수 있습니다. 이것이 CPU가 궁극적으로하는 일입니다. 그리고 재귀 자체는 더 직접적으로 함수 호출과 범위를 스택에 넣는 것을 의미합니다. 그러나 재귀 알고리즘을 반복 알고리즘으로 변경하려면 많은 작업이 필요하고 코드 유지 관리가 어려워 질 수 있습니다. 모든 최적화에 대해 일부 프로파일 링이나 증거가 필요하다는 것을 보여줄 때만 시도해야합니다.
답변
재귀가 사용되는 모든 곳에서 for 루프를 사용할 수 있다고 말하는 것이 맞습니까?
예, 대부분의 CPU에서 재귀는 루프와 스택 데이터 구조로 모델링되기 때문입니다.
그리고 재귀가 일반적으로 느리다면 그것을 사용하는 기술적 이유는 무엇입니까?
“보통 느리다”는 것이 아닙니다. 잘못 적용되는 재귀가 더 느립니다. 게다가, 현대 컴파일러는 묻지 않고 일부 재귀를 루프로 변환하는 데 능숙합니다.
그리고 재귀를 for 루프로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?
반복적으로 설명 할 때 가장 잘 이해되는 알고리즘을위한 반복 프로그램을 작성하십시오. 재귀 적으로 가장 잘 설명 된 알고리즘에 대한 재귀 프로그램을 작성합니다.
예를 들어, 이진 트리 검색, 빠른 정렬 실행 및 많은 프로그래밍 언어에서 표현식 구문 분석은 종종 재귀 적으로 설명됩니다. 이것들도 재귀 적으로 가장 잘 코딩됩니다. 반면에 계승 계산 및 피보나치 수 계산은 반복 측면에서 설명하기가 훨씬 쉽습니다. 그들에게 재귀를 사용하는 것은 큰 망치로 파리를 날리는 것과 같습니다 : 큰 망치가 정말 좋은 일을하더라도 좋은 생각이 아닙니다 + .
+ Dijkstra의 “Discipline of Programming”에서 쇠 망치 비유를 빌 렸습니다.
답변
질문 :
그리고 재귀가 일반적으로 느리다면 for 루프 반복을 통해 그것을 사용하는 기술적 이유는 무엇입니까?
대답 :
일부 알고리즘에서는 반복적으로 해결하기가 어렵 기 때문입니다. 심도 우선 검색을 재귀 적 및 반복적으로 해결하십시오. 반복을 통해 DFS를 풀기가 어렵다는 생각을 갖게 될 것입니다.
시도해 볼만한 또 다른 좋은 점 : Merge sort를 반복적으로 작성해보십시오. 꽤 시간이 걸릴 것입니다.
질문 :
재귀가 사용되는 모든 곳에서 for 루프를 사용할 수 있다고 말하는 것이 맞습니까?
대답 :
예. 이 스레드 는 이에 대한 매우 좋은 대답을 가지고 있습니다.
질문 :
그리고 재귀를 for 루프로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?
대답 :
날 믿어. 깊이 우선 검색을 반복적으로 해결하기 위해 자체 버전을 작성하십시오. 일부 문제는 재귀 적으로 해결하기가 더 쉽다는 것을 알 수 있습니다.
힌트 : 재귀는 분할 정복 기법 으로 해결할 수있는 문제를 풀 때 좋습니다 .
답변
느린 것 외에도 재귀는 얼마나 깊이가 있는지에 따라 스택 오버플로 오류를 일으킬 수 있습니다.
답변
반복을 사용하여 동등한 메서드를 작성하려면 명시 적으로 스택을 사용해야합니다. 반복적 버전에 솔루션에 대한 스택이 필요하다는 사실은 문제가 재귀로부터 이익을 얻을 수있을만큼 충분히 어렵다는 것을 나타냅니다. 일반적으로 재귀는 고정 된 양의 메모리로 해결할 수없는 문제에 가장 적합하며 결과적으로 반복적으로 해결 될 때 스택이 필요합니다. 재귀와 반복은 서로 다른 패턴을 따르면서 동일한 결과를 보여줄 수 있습니다. 어떤 방법이 더 잘 작동하는지 결정하는 것은 사례별로 이루어지며 모범 사례는 문제가 따르는 패턴을 기반으로 선택하는 것입니다.
예를 들어, Triangular 시퀀스의 n 번째 삼각 수를 찾으려면 : 1 3 6 10 15… 반복 알고리즘을 사용하여 n 번째 삼각 수를 찾는 프로그램 :
반복 알고리즘 사용 :
//Triangular.java
import java.util.*;
class Triangular {
public static int iterativeTriangular(int n) {
int sum = 0;
for (int i = 1; i <= n; i ++)
sum += i;
return sum;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
iterativeTriangular(n));
}
}//enter code here
재귀 알고리즘 사용 :
//Triangular.java
import java.util.*;
class Triangular {
public static int recursiveTriangular(int n) {
if (n == 1)
return 1;
return recursiveTriangular(n-1) + n;
}
public static void main(String args[]) {
Scanner stdin = new Scanner(System.in);
System.out.print("Please enter a number: ");
int n = stdin.nextInt();
System.out.println("The " + n + "-th triangular number is: " +
recursiveTriangular(n));
}
}
답변
대부분의 답변은 iterative
= for loop
. for 루프가 제한되지 않은 경우 ( a la C, 루프 카운터로 원하는 모든 작업을 수행 할 수 있음), 맞습니다. 그것이 있다면 실제 for
루프는 다음이다 (수동 루프 카운터를 수정할 수있는 파이썬이나 대부분의 기능적인 언어로 말) 하지 올바른.
모든 (계산 가능한) 함수는 반복적으로 그리고 while
루프 (또는 기본적으로 동일한 조건부 점프)를 사용하여 구현할 수 있습니다 . 진정으로 자신을로 제한 for loops
하면 해당 함수의 하위 집합 만 얻을 수 있습니다 (기본 작업이 합리적이면 기본 재귀 함수). 당연히, 그것은 당신이 실제로 encouter 할 가능성이있는 모든 단일 기능을 포함하는 꽤 큰 부분 집합입니다.
훨씬 더 중요한 것은 많은 함수가 재귀 적으로 구현하기가 매우 쉽고 반복적으로 구현하기가 매우 어렵다는 것입니다 (수동으로 호출 스택을 관리하는 것은 중요하지 않습니다).
답변
예, 말했다 에 의해 Thanakron Tandavas ,
재귀는 나누기 정복 기법으로 해결할 수있는 문제를 풀 때 좋습니다.
예 : 하노이 타워
- 증가하는 크기의 N 링
- 3 극
- 링은 폴 1에 쌓이기 시작합니다. 목표는 링이 폴 3에 쌓 이도록 이동하는 것입니다.
- 한 번에 하나의 링만 이동할 수 있습니다.
- 작은 것 위에 큰 반지를 놓을 수 없습니다.
- 반복적 인 솔루션은 “강력하지만 추악”합니다. 재귀 솔루션은 “우아함”입니다.