재귀 대 반복


109

재귀가 사용되는 모든 곳에서 for루프를 사용할 수 있다고 말하는 것이 맞 습니까? 그리고 재귀가 일반적으로 느리다면 for루프 반복을 통해 사용하는 기술적 이유는 무엇 입니까?

그리고 재귀를 for루프 로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?


3
recursioniteration? iteration = for loop나는 생각한다.
gongzhitaao

4
Tom Moertel의 블로그에는 재귀 코드를 반복 코드로 변환하는 4 개의 훌륭한 게시물이 있습니다. blog.moertel.com/tags/recursion.html
cjohnson318

답변:


146

재귀는 일반적으로 호출자 함수로 돌아갈 수 있도록 모든 함수 호출을 스택에 저장해야하기 때문에 훨씬 느립니다. 대부분의 경우 범위 격리를 구현하려면 메모리를 할당하고 복사해야합니다.

꼬리 호출 최적화 와 같은 일부 최적화 는 재귀를 더 빠르게 만들지 만 항상 가능하지는 않으며 모든 언어로 구현되지는 않습니다.

재귀를 사용하는 주된 이유는 다음과 같습니다.

  • 문제에 대한 우리의 접근 방식을 모방 할 때 많은 경우에 더 직관적입니다.
  • 트리와 같은 일부 데이터 구조는 재귀를 사용하여 탐색하기가 더 쉽습니다 (또는 어떤 경우에도 스택이 필요함).

물론 모든 재귀 일종의 루프로 모델링 될 있습니다. 이것이 CPU가 궁극적으로하는 일입니다. 그리고 재귀 자체는 더 직접적으로 함수 호출과 범위를 스택에 넣는 것을 의미합니다. 그러나 재귀 알고리즘을 반복 알고리즘으로 변경하려면 많은 작업이 필요하고 코드 유지 관리가 어려워 질 수 있습니다. 모든 최적화에 대해 일부 프로파일 링이나 증거가 필요하다는 것을 보여줄 때만 시도해야합니다.


10
덧붙여서 재귀는 많은 알고리즘과 일반적으로 CS에서 중심적인 역할을하는 환원이라는 용어와 밀접한 관련이 있습니다.
SomeWittyUsername 2013 년

3
재귀로 인해 코드를 더 유지 관리 할 수있는 예를 제공해 주시겠습니까? 내 경험으로는 항상 반대입니다. 감사합니다
Yeikel 2018

@Yeikel f(n)n 번째 피보나치 수 를 반환 하는 함수 를 작성합니다 .
Matt

54

재귀가 사용되는 모든 곳에서 for 루프를 사용할 수 있다고 말하는 것이 맞습니까?

예, 대부분의 CPU에서 재귀는 루프와 스택 데이터 구조로 모델링되기 때문입니다.

그리고 재귀가 일반적으로 느리다면 그것을 사용하는 기술적 이유는 무엇입니까?

"보통 느리다"는 것이 아닙니다. 잘못 적용되는 재귀가 더 느립니다. 게다가, 현대 컴파일러는 묻지 않고 일부 재귀를 루프로 변환하는 데 능숙합니다.

그리고 재귀를 for 루프로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?

반복적으로 설명 할 때 가장 잘 이해되는 알고리즘을위한 반복 프로그램을 작성하십시오. 재귀 적으로 가장 잘 설명 된 알고리즘에 대한 재귀 프로그램을 작성합니다.

예를 들어, 이진 트리 검색, 빠른 정렬 실행 및 많은 프로그래밍 언어에서 표현식 구문 분석은 종종 재귀 적으로 설명됩니다. 이것들도 재귀 적으로 가장 잘 코딩됩니다. 반면에 계승 계산 및 피보나치 수 계산은 반복 측면에서 설명하기가 훨씬 쉽습니다. 그들에게 재귀를 사용하는 것은 큰 망치로 파리를 날리는 것과 같습니다 : 큰 망치가 정말 좋은 일을하더라도 좋은 생각이 아닙니다 + .


+ Dijkstra의 "Discipline of Programming"에서 쇠 망치 비유를 빌 렸습니다.


7
재귀는 일반적으로 스택 프레임 등을 생성하기 때문에 더 비쌉니다 (느리거나 더 많은 메모리). 충분히 복잡한 문제에 올바르게 적용하면 차이가 작을 수 있지만 여전히 더 비쌉니다. 꼬리 재귀 최적화와 같은 가능한 예외가 있습니다.
Bernhard Barker

모든 경우에 단일 for 루프 에 대해 잘 모르겠습니다 . 하나의 변수를 초과하는 더 복잡한 재귀 또는 재귀를 고려
SomeWittyUsername

@dasblinkenlight 이론적으로 여러 루프를 단일 루프로 줄이는 것이 가능할 수 있지만 확실하지 않습니다.
SomeWittyUsername 2013 년

@icepack 예, 가능합니다. 예쁘지 않을 수도 있지만 가능합니다.
Bernhard Barker

첫 번째 진술에 동의하는지 잘 모르겠습니다. CPU 자체는 실제로 재귀를 모델링하지 않습니다. 재귀를 모델링하는 것은 CPU에서 실행되는 명령입니다. 둘째, 루프 구조에는 동적으로 증가 및 축소되는 데이터 세트가 없습니다 (필수). 여기서 재귀 알고리즘은 일반적으로 각 레벨에 대해 재귀가 단계적으로 진행되어야합니다.
trumpetlicks

28

질문 :

그리고 재귀가 일반적으로 느리다면 for 루프 반복을 통해 그것을 사용하는 기술적 이유는 무엇입니까?

대답 :

일부 알고리즘에서는 반복적으로 해결하기가 어렵 기 때문입니다. 심도 우선 검색을 재귀 적 및 반복적으로 해결하십시오. 반복을 통해 DFS를 풀기가 어렵다는 생각을 갖게 될 것입니다.

시도해 볼만한 또 다른 좋은 점 : Merge sort를 반복적으로 작성해보십시오. 꽤 시간이 걸릴 것입니다.

질문 :

재귀가 사용되는 모든 곳에서 for 루프를 사용할 수 있다고 말하는 것이 맞습니까?

대답 :

예. 이 스레드 는 이에 대한 매우 좋은 대답을 가지고 있습니다.

질문 :

그리고 재귀를 for 루프로 변환하는 것이 항상 가능하다면 그것을 수행하는 경험적 방법이 있습니까?

대답 :

날 믿어. 깊이 우선 검색을 반복적으로 해결하기 위해 자체 버전을 작성하십시오. 일부 문제는 재귀 적으로 해결하기가 더 쉽다는 것을 알 수 있습니다.

힌트 : 재귀는 분할 정복 기법 으로 해결할 수있는 문제를 풀 때 좋습니다 .


3
권위있는 답변을 제공하려는 시도에 감사 드리며 저자는 똑똑하다고 확신하지만 대답이 즉각적으로 명확하지 않은 의미있는 질문에 대해 "나를 믿으십시오"는 유용한 답변이 아닙니다. 반복적 인 깊이 우선 검색을 수행하기위한 매우 간단한 알고리즘이 있습니다. 의사 코드의 알고리즘에 대한 설명은이 페이지 하단의 예를 참조하십시오. csl.mtu.edu/cs2321/www/newLectures/26_Depth_First_Search.html
jdelman

3

느린 것 외에도 재귀는 얼마나 깊이가 있는지에 따라 스택 오버플로 오류를 일으킬 수 있습니다.


3

반복을 사용하여 동등한 메서드를 작성하려면 명시 적으로 스택을 사용해야합니다. 반복적 버전에 솔루션에 대한 스택이 필요하다는 사실은 문제가 재귀로부터 이익을 얻을 수있을만큼 충분히 어렵다는 것을 나타냅니다. 일반적으로 재귀는 고정 된 양의 메모리로 해결할 수없는 문제에 가장 적합하며 결과적으로 반복적으로 해결 될 때 스택이 필요합니다. 재귀와 반복은 서로 다른 패턴을 따르면서 동일한 결과를 보여줄 수 있습니다. 어떤 방법이 더 잘 작동하는지 결정하는 것은 사례별로 이루어지며 모범 사례는 문제가 따르는 패턴을 기반으로 선택하는 것입니다.

예를 들어, 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)); 
   }
}

1

대부분의 답변은 iterative= for loop. for 루프가 제한되지 않은 경우 ( a la C, 루프 카운터로 원하는 모든 작업을 수행 할 수 있음), 맞습니다. 그것이 있다면 실제 for 루프는 다음이다 (수동 루프 카운터를 수정할 수있는 파이썬이나 대부분의 기능적인 언어로 말) 하지 올바른.

모든 (계산 가능한) 함수는 반복적으로 그리고 while루프 (또는 기본적으로 동일한 조건부 점프)를 사용하여 구현할 수 있습니다 . 진정으로 자신을로 제한 for loops하면 해당 함수의 하위 집합 만 얻을 수 있습니다 (기본 작업이 합리적이면 기본 재귀 함수). 당연히, 그것은 당신이 실제로 encouter 할 가능성이있는 모든 단일 기능을 포함하는 꽤 큰 부분 집합입니다.

훨씬 더 중요한 것은 많은 함수가 재귀 적으로 구현하기가 매우 쉽고 반복적으로 구현하기가 매우 어렵다는 것입니다 (수동으로 호출 스택을 관리하는 것은 중요하지 않습니다).


1

예, 말했다 에 의해 Thanakron Tandavas ,

재귀는 나누기 정복 기법으로 해결할 수있는 문제를 풀 때 좋습니다.

예 : 하노이 타워

  1. 증가하는 크기의 N 링
  2. 3 극
  3. 링은 폴 1에 쌓이기 시작합니다. 목표는 링이 폴 3에 쌓 이도록 이동하는 것입니다.
    • 한 번에 하나의 링만 이동할 수 있습니다.
    • 작은 것 위에 큰 반지를 놓을 수 없습니다.
  4. 반복적 인 솔루션은 "강력하지만 추악"합니다. 재귀 솔루션은 "우아함"입니다.

흥미로운 예입니다. MC Er "The Towers of Hanoi and Binary Numerals"의 논문을 알고 계신 것 같습니다. 또한 3brown1blue의 환상적인 비디오로 처리되었습니다.
Andrestand 2019-07-18

0

나는 컴퓨터 과학 교수가 재귀 적 해결책을 가진 모든 문제에 반복적 해결책이 있다고 말했던 것을 기억하는 것 같습니다. 그는 재귀 솔루션이 일반적으로 더 느리지 만 반복 솔루션보다 추론하고 코딩하기가 더 쉬울 때 자주 사용된다고 말합니다.

그러나 고급 재귀 솔루션의 경우 항상 간단한 for루프를 사용하여 구현할 수 있다고 생각하지 않습니다 .


재귀 알고리즘을 반복 알고리즘으로 변환하는 것은 항상 가능합니다 (스택 사용). 특히 단순한 루프로 끝나지 않을 수도 있지만 가능합니다.
Bernhard Barker

-4

재귀 + 암기는 순수한 반복적 접근 방식에 비해 더 효율적인 솔루션으로 이어질 수 있습니다. 예를 들어 다음을 확인하십시오. http://jsperf.com/fibonacci-memoized-vs-iterative-for-large-n


3
모든 재귀 코드는 스택을 사용하여 기능적으로 동일한 반복 코드로 변환 될 수 있습니다. 당신이 보여주는 차이점은 재귀와 반복의 차이가 아니라 동일한 문제를 해결하기위한 두 가지 접근법의 차이입니다.
Bernhard Barker

-6

짧은 대답 : 거의 모든 경우에 재귀가 더 빠르고 for 루프가 메모리를 덜 차지한다는 단점이 있습니다. 그러나 일반적으로 for 루프 또는 재귀를 변경하여 더 빠르게 실행하는 방법이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.