문제

이 있 성능 만약 우리가 사용하는 대신 루프를 재귀 또는 그 반대에서 알고리즘을 모두 할 수 있을까요?예를 들어:체크인 경우에는 지정된 문자열은 회문.본 많은 프로그래머 재귀를 사용하는 수단으로 보여주면 간단한 알고리즘 반복할 수 있습니다.는 컴파일러는 중요한 역할을 결정에서 무엇을 사용합니까?

도움이 되었습니까?

해결책

그것은 가능한 재귀는 것이 더 비싸고,경우에 따라 재귀적 기능 꼬리 recursive (이 마지막 줄은 귀).꼬리 재귀 인식할 수 있는 컴파일러 최적화 반복적인 대응(을 유지하면 간결하고,명확한 구현에 있는 코드).

쓰겠다고 알고리즘에서는 방식에 가장 적합하고 깨끗한 가난한 빠(그것은 자신이나 다른 사람)를 유지하는 코드에서 몇 달이나 몇 년.로 실행하면 성능 문제에 대한 후 프로파일 코드,그리고 다음 및 다음으로 보고 최적화하여 이를 통해 반복적인 구현합니다.할 수 있으로 보고 메모이 제이션동적 프로그래밍.

다른 팁

루프를 달성할 수 있는 성능 향상을 위해 귀하의 프로그램입니다.재귀할 수 있을 달성하는 성능 향상을 위한 프로그래머입니다.은 더 중요한 선택에서 당신의 상황!

비교 재귀를 반복 같은 비교하자면 십자 스크루 드라이버를 평 드라이버.대부분은 당신 을 제거하는 모든 십자 머리 나사를 가진 평평한 머리이지만,그냥 쉽게 사용한 경우 드라이버에 대한 설계는 나사 맞죠?

일부를 알고리즘에 자신을 빌려 재귀는 방식 때문에 그들은 설계(피보나치 시퀀스를 통과,나무 같은 구조,etc.).재귀를 만드는 알고리즘이 더 간결하고 이해하기 쉽게(그러므로 공유하고 재사용 가능).

또한,일부 재귀적 알고리즘을 사용하여"게으른 평가"그들에게 보다 더 효율적의 반복적인 형제입니다.이것이 의미하는 것은 그들만의 비싼 계산에서 그들이 필요하다는 오히려 이마다 반복 실행합니다.

해야 하는 충분히 당신을 얻을 시작했다.나는 발굴하고 일부 기사와 예제입니다.

링크 1: Haskel 대 PHP(재귀 vs 반복)

여기에 예를 들어 프로그래머했 프로세스는 대형 데이터를 사용하여 설정 PHP.그는 얼마나 쉽게 그것이었을 것입 거래에서 Haskel 재귀를 사용하지만,이후 PHP 없었다는 쉬운 방법을하기 위해 최선을 다하고 있 방법을 사용하여 반복하여 얻을 결과입니다.

http://blog.webspecies.co.uk/2011-05-31/lazy-evaluation-with-php.html

링크 2: 마스터 재귀

대부분의 재귀의 나쁜 평판을에서 높은 비용과 비효율성에 필수적 언어입니다.이 문서의 저자는 방법에 대해 이야기를 최적화하는 재귀적 알고리즘들을 더욱 빠르고 효율적입니다.그는 또한 넘는 어떻게 변환하는 전통적인 반복으로 재귀적 기능을 사용하는의 이득 꼬리 최종 재귀.그 닫는 말이 정말로 요약된 몇 가지의 주요 포인트 나는 생각한다:

"재귀 프로그램을 제공합한 프로그래머의 더 나은 방법을 조직 코드에서는 방법은 모두 유지 관리하고 논리적으로 일치한다."

https://developer.ibm.com/articles/l-recurs/

링크 3: 은 재귀 어느 때보다 빠르게 반복?(답변)

여기에 대한 링크에 대한 답을 직접 질문과 비슷한 당신입니다.저자는 많이 벤치마크와 관련된 중 recursing 또는 반복은 언어별로 되어 있습니다.필수적 언어가 일반적으로 더 빠르게 사용하고 느린 재귀와 반대로 기능적 언어입니다.나는 주요 포인트를 취할 이 링크에서는 이것은 아주 어려운 질문에 대답을 언어에 상관없/상황 맹인 감각이다.

은 재귀 어느 때보다 빠르게 반복?

재귀는 더 비용이 많이 드는 메모리에서,각각적화는 일반적으로 필요한 메모리 주소를 밀어 스택에 그는 나중에 프로그램을 반환 할 수 있습니다.

여전히,많은 경우에는 재귀는 더 많은 자연적이고 읽을 수 있는 이 루프 같은 작업을 할 때입니다.이러한 경우에는 내가 고집하는 것이 좋습을 재귀.

일반적으로,하나의 기대 성능 저하 속에서 다른 방향입니다.재귀화로 이어질 수 있습 건축의 스 프레임;페널티를 위해 이에 따라 다릅니다.또한,일부 언어에서 파일이 들어(더 정확하게,일부 구현을의 일부가 생각보다 멀었습니다)실행할 수 있습으로 스택한 더 쉽게 작업을 지정할 수 있습을 재귀적으로,같은를 찾아에서 최대값을 나무 데이터는 구조입니다.이러한 경우에,당신은 정말 지키고 싶습니다.

쓰기에 좋을 재귀적 기능을 줄일 수 있고 성능 저하 다고 가정하면,당신이 컴파일러 최적화하는 꼬리 recursions,etc.(또한 두 배는지 확인하는 기능이 진짜로는 꼬리 재귀---그것은 것들 중 하나는 많은 사람들이 실수합니다.)

서 제공자는 경우(고성능 컴퓨팅,매우 큰 깊이 등) 그것은 바람직한을 채택하는 방식을 가장 명확하게 표현의 의도를 잘 설계하고 유지할 수 있습니다.최적화된 후에만을 식별하는 필요합니다.

재귀는 더 이상 반복하는 문제로 나눌 수 있습니다 , 작은 조각합니다.

예를 들어,재귀 Fibonnaci 알고리즘,당신은 휴식의 아래에 거짓말(n)으로 거짓말(n-1),거짓말(n-2)및 컴퓨팅 두 부분입니다.반복할 수 있습을 반복하는 단 하나 기능니다.

그러나,피보나치 실제로 깨진 예고 생각 반복이 실제로 더 효율적입니다.알 수 있는 거짓말(n)=fib(n-1)+fib(n-2)고 거짓말(n-1)=fib(n-2)+fib(n-3).거짓말(n-1)가 계산한다.

더 나은 예를 들어 알고리즘을 재귀한다.......의 문제를 분석하는 부모 노드로 나눌 수 있습니다 작은 문제들을 분석하는 각 하위 노드가 있습니다.과는 달리는 피보나치를 들어,이 작은 문제들은 서로 독립적입니다.

그래서-재귀는 더 이상 반복하는 문제로 나눌 수 있습니다 여러 개의 작은 독립,이와 비슷한 문제를 일으켰습니다.

의 성능이 저하된 경우 재귀를 사용하기 때문에 메소드 호출에 어떤 언어를 의미한 준비를 많이:호출하는 코드는 게시물 주소,전화 매개변수,다른 컨텍스트와 같은 정보를 등록 프로세서 할 수 있는 곳,그리고는 반환 시에는 방법이라고 게시물을 반환 값은 다음 검색에 의해 호출자,그리고 어떤 상황 정보는 이전에 저장되었던 것이 복원됩니다.성과 사이의 비교는 반복과 재귀적 접근이 거짓말 시간에 이러한 작업이다.

에서 구현한 관점,당신은 정말 시작 차이를 깨닫는 때에 걸리는 시간을 처리하는 호출되는 상황에 비해 걸리는 시간을 당신의 방법을 실행합니다.하는 경우 재귀적 방법을 실행한 다음 호출되는 상황 관리가 부분을 이동,재귀적 방법으로는 코드는 일반적으로 더 읽기 쉽고 이해하기 쉽고,당신이 통지하지 않습 성과 손실이다.그렇지 않으면 반복적인 효율성에 대한 이유입니다.

내가 믿는 꼬리 재귀에서 java 현재 최적화되어 있습니다.세부사항은 뿌리내 토론에 LtU 및 관련 링크입니다.그 있는 기능 향후 버전에서 7 지만,분명히 그것은 어려움과 결합하면 Stack Inspection 이후 특정 프레임을 것 없습니다.스 검사되었을 구현하는 데 사용되는 그들의 세밀한 보안 모델이기 때문 Java2.

http://lambda-the-ultimate.org/node/1333

많은 경우 그것은 훨씬 우아한 솔루션을 통해 반복적인 방법 일반적인 예로는 탐색의 이진 나무,그것은 아닙니다 반드시 더 유지하기 어렵습니다.일반적으로,반복적인 버전은 일반적으로 빠른 비트(고 최적화하는 동안 대체 할 수 있습을 재귀 버전),그러나 재귀 버전은 간단하게 이해하고 구현습니다.

재귀를 아주 유용한 몇 가지 상황입니다.예를 들어 고려한 코드를 찾기 위해 계승

int factorial ( int input )
{
  int x, fact = 1;
  for ( x = input; x > 1; x--)
     fact *= x;
  return fact;
}

지금 그것을 고려하여 재귀적 기능

int factorial ( int input )
{
  if (input == 0)
  {
     return 1;
  }
  return input * factorial(input - 1);
}

을 관찰하여 이러한 두 가지,우리가 볼 수 있는 재귀를 이해하기 쉽습니다.그러나 사용하지 않는 경우으로 관리할 수 있도록 많은 오류가 발생하기 쉬운 너무입니다.가정하자면 우리는 미 if (input == 0), 다음 코드를 실행한 시간이 끝나 일반적으로 스택 오버플로우가 발생합니다.

많은 경우에는 재귀가 빠르기 때문에 캐싱하는 성능을 향상시킵니다.예를 들어,여기에는 반복적인 버전의 병합 사용하여 정렬 전통적인 병합니다.실행됩니다 보다 느린 재귀 구현 때문에 캐싱을 개선된 공연이다.

반복 구현

public static void sort(Comparable[] a)
{
    int N = a.length;
    aux = new Comparable[N];
    for (int sz = 1; sz < N; sz = sz+sz)
        for (int lo = 0; lo < N-sz; lo += sz+sz)
            merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}

재귀 구현

private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
    if (hi <= lo) return;
    int mid = lo + (hi - lo) / 2;
    sort(a, aux, lo, mid);
    sort(a, aux, mid+1, hi);
    merge(a, aux, lo, mid, hi);
}

PS-이것은 무슨 말을 들었 교수 Kevin Wayne(프린스턴대학교)과정에서 알고리즘에 사용.

재귀를 사용하여,당신은 당신의 비용을 들이지 함수 호출과 각각의"반복"하는 반면으로 반복만 당신은 일반적으로 지불하는 앞으로/뒤로 가게 할 수 있습니다.경우에 따라서,코드에 대한 반복하지 않보다 훨씬 더 복잡는 코드에 대한 재귀적 솔루션,루프가 일반적으로 우수한 것을 재귀.

재귀하고 반복에 따라 비즈니스 로직 구현을 원하는지만,대부분의 경우에는 같은 의미로 사용할 수 있습니다.대부분의 개발자들을 위해 갈 재귀기 때문에 그것은 더 쉽게 이해할 수 있습니다.

그것은 언어에 따라 달라집니다.에서 Java 를 사용해야 합니다.기능적인 언어를 최적화하는 재귀.

는 경우에 당신은 단지 반복 목록,다음,반복습니다.

의 몇 가지 다른 답변을 언급했(깊이 첫 번째)트리 traversal.그것은 정말 좋은 예기 때문에,그것은 매우 일반적인 것을 매우 일반적인 데이터 구조입니다.재귀은 매우 직관적이 이 문제를 해결합니다.

Check out the"찾기"방법을 여기:http://penguin.ewu.edu/cscd300/Topic/BSTintro/index.html

재귀는 더 간단한(그리고 더 근본적인)보다는 가능한 모든 정의 반복이다.를 정의할 수 있습니다 Turing-전체 시스템으로만 쌍의 콤비네이터 (그래도 재귀 자체 유래물 개념 이러한 시스템에서). 람다 미적분은 동등하게 강력한 기본적인 시스템 특징으로,재귀적 기능입니다.하지만 당신이 원하는 경우를 정의 반복 제대로,당신은 훨씬 더 필요한 기본 형식으로 시작입니다.

로를 위한 코드가 없,재귀적 코드를 실제로는 훨씬 쉽게 이해하고 유지하기보다는 순수한 반복적인 중 하나,대부분의 데이터 구조는 재귀적입니다.물론을 얻기 위해서,그것은 바로 하나가 필요와 언어에 대한 지원이 높은 함수와 클로저,이상-모든 표준 콤비네이터 및 반복기를 깔끔한 방법입니다.C++에서는 물론,복잡한 재귀적인 솔루션을 볼 수 있는 조금 못생긴하지 않는 한,당신은 하드 코어용의 FC++ 과 비슷합니다.

내가 생각하는 것에(비 꼬리)재귀가 될 것이 성능에 대한 할당하는 새로운 스택 등 모든 시간의 함수를 호출(에 의존하의 언어 코스).

에 따라 달라"깊이".에 따라 얼마나 함수 호출에 오버헤드 영향을 미칠 것이다 총 실행 시간입니다.

예를 들어,계산적인 요인에서 재귀는 방법이 매우 비효율적이가 발생할 수 있습니다. 위험의 데이터 넘치 -의 위험을 쌓 넘치 -함수 호출 오버헤드의 80%를 차지 실행 시간

을 개발하면서 최소-최대한 알고리즘에 대한 위치 분석의 게임에서 체스하는 분석 이후 N 움직이 구현될 수 있 재귀를 통해서"분석을 깊이다"(하고 있어요^_^)

재귀?나는 어디로 시작,위키에 당신을 말할 것이다"이것은 반복하는 과정에 항목이 자기와 유사한 방법으로"

뒤에서 일할 때었는데 C,C++재귀었다 하나님 보내기,물건은"꼬리 재귀".당신은 또한이 많이 찾을 정렬 알고리즘을 사용하여 재귀.빠른 정렬 예제: http://alienryderflex.com/quicksort/

재귀 같은 다른 알고리즘에 대한 유용한 특정 문제입니다.아마도 당신은 그렇지 않을 수도 찾을 바로 사용이나 자주지가 될 것이 문제가 당신은 드리겠습니다 그것을 사용할 수 있습니다.

C++에서는 경우 재귀적 기능입니다 템플릿 중 하나,그때 컴파일러가 더 기회를 최적화하는것으로 모든 유형이론과 기능을 인스턴스에서 발생은 컴파일때 정해진다.현대 컴파일러한 인라인 기능을 가능한 경우.그렇다면 사용을 최적화 같은 플래그 -O3-O2g++, 다음 recursions 할 수 있는 기회가 될 것보다 더 빨리 번 반복합니다.에서 반복적인 코드를 컴파일러는 더 적은을 얻을 최적화 할 수있는 기회 그것은,그것이에서 이미 더 많거나 적은 최적의 상태(잘 쓰는 경우에만).

나의 케이스에서,나를 구현하기 위해 노력하고 매트릭스 지수에 의해 제곱을 사용하여 딜 행렬 개체 모두에서,재귀적이며 반복적인 방법입니다.알고리즘은 여기에서 찾을 수 있습니다... https://en.wikipedia.org/wiki/Exponentiation_by_squaring.내 기능 템플릿 및 내가 계산 1,000,000 12x12 매트릭스를 발생 전원 10.나는 다음과 같은 결과를 얻을 수 있습니다

iterative + optimisation flag -O3 -> 2.79.. sec
recursive + optimisation flag -O3 -> 1.32.. sec

iterative + No-optimisation flag  -> 2.83.. sec
recursive + No-optimisation flag  -> 4.15.. sec

이러한 결과를 사용하여 얻은 gcc-4.8c++11 플래그(-std=c++11 다)및 딜 6.1Intel mkl.인텔 컴파일러 또한 비슷한 결과를 보이고 있다.

마이크 올바른 것입니다.꼬리 재귀 을 최적화하여 Java 컴파일러 또는 JVM.당신은 항상을 얻을 stack overflow 과 함께 무언가 이것을 좋아한다:

int count(int i) {
  return i >= 100000000 ? i : count(i+1);
}

당신을 유지해야 하는 마음에 활용이 너무 깊은 재귀신으로 실행됩니다 Stack Overflow,에 따라 허용할 스택 크기를 나타냅니다.이를 방지하는지 확인을 제공하는 몇 가지 기본 경우는 끝을 재귀.

재귀는 단점이 있는 알고리즘을 사용하여 작성 재귀는 O(n)공간 복잡합니다.는 동안 반복적인 산도와 경내에 마련된 회장이 공간 복잡도 O(1).이 advantange 의를 사용하여 반복을 통해 재귀.그럼 왜 우리가 사용하는 재귀?

아래를 참조하십시오.

때때로 그것은 쉽게 쓰는 알고리즘을 사용하여 재귀하는 동안 그것은 약간 강하게 쓰는 동일한 알고리즘을 사용하여 반복이다.이 경우 선택할 경우에 따라 반복을 당신에게 접근해야하는 손잡이 스택에 자신입니다.

로,펄 최적화되지 않습니다 꼬리-재귀적인 통화,하지만 당신은 수 있습니다.

sub f{
  my($l,$r) = @_;

  if( $l >= $r ){
    return $l;
  } else {

    # return f( $l+1, $r );

    @_ = ( $l+1, $r );
    goto &f;

  }
}

때 처음이라고 그것은 공간을 할당에 스택입니다.그것은 변경의 인수,그리고 다시 시작 서브 루틴을 추가하지 않고 아무것도 더 적다는 것이다.그러므로 가지 못하는 자,그것을 변경으로 반복적인 프로세스입니다.

가 없다는 것을 참고하라."my @_;"또는"local @_;"았다면 그것은 더 이상 작동하지 않습니다.

를 사용하여 크롬 45.0.2454.85m,재귀 같은 양이 더 빠르다.

는 코드는 다음과 같습니다:

(function recursionVsForLoop(global) {
    "use strict";

    // Perf test
    function perfTest() {}

    perfTest.prototype.do = function(ns, fn) {
        console.time(ns);
        fn();
        console.timeEnd(ns);
    };

    // Recursion method
    (function recur() {
        var count = 0;
        global.recurFn = function recurFn(fn, cycles) {
            fn();
            count = count + 1;
            if (count !== cycles) recurFn(fn, cycles);
        };
    })();

    // Looped method
    function loopFn(fn, cycles) {
        for (var i = 0; i < cycles; i++) {
            fn();
        }
    }

    // Tests
    var curTest = new perfTest(),
        testsToRun = 100;

    curTest.do('recursion', function() {
        recurFn(function() {
            console.log('a recur run.');
        }, testsToRun);
    });

    curTest.do('loop', function() {
        loopFn(function() {
            console.log('a loop run.');
        }, testsToRun);
    });

})(window);

결과

//100 를 사용하여 실행되는 기준에 대한 루프

100x 대한 반복 실행합니다.를 완료하는 데 시간: 7.683ms

//100 를 사용하여 실행되는 기능을 재귀적 접근 w/꼬리 재귀

100x 재귀를 실행합니다.를 완료하는 데 시간: 4.841ms

에서 아래 스크린샷,재귀 승에 의해 다시 더 큰 마진이 실행하는 경우에는 300 당 사이클 테스트

Recursion wins again!

는 경우 반복은 원자 및 주문 크기보다 더 비싸 밀어 새로운 스택 구조 새로 만드는 스레드 이 여러 개 있는 코어 런타임 환경 모두 사용할 수 있습니,그 재귀적인 접근 방식을 얻을 수 있습니다 거대한 성능을 높일 때와 결합된 다중 스레딩.는 경우에는 평균의 반복 횟수지 예측할 수 있습니다 그것은 좋은 아이디어 스레드 풀을 사용하는 것 제어 스레드에 할당하고 방지하는 프로세스에서 너무 많은 스레드를 생성하고 좋아하는 시스템입니다.

예를 들어,어떤 언어 있는 재귀적인 멀티 스레드 병합 정렬 구현입니다.

하지만,다시 다중 스레딩을 사용할 수 있습으로 반복이 아닌 재귀,그래서 얼마나 잘 이 조합에 더 많은 요인을 포함하여 OS 고 스레드에 할당 메커니즘이 있습니다.

나는 당신의 질문에 대답하여 디자인켈 데이터 구조에 의"유도"는 종류의"dual"을 재귀.그리고 나는 방법을 보여줍니다 이 이중성에 이르게 좋은 것을 말한다.

우리는 우리를 소개하는 유형에 대한 간단한 나무:

data Tree a = Branch (Tree a) (Tree a)
            | Leaf a
            deriving (Eq)

우리는 읽을 수 있는 이 정의로 말하는"나무는 지점(을 포함하는 두 나무)또는 잎(을 포함하는 데이터 값)".그래서 잎은 일종의 최소한의 경우입니다.만약 나무가 아닙 잎,다이어야 합니다 트리 화합물을 포함하는 두 나무입니다.이러한 유일한 경우입니다.

만들자리:

example :: Tree Int
example = Branch (Leaf 1) 
                 (Branch (Leaf 2) 
                         (Leaf 3))

지금하자,우리가 추가하려는 1 을 각 값에서 나무입니다.우리는 이것을 할 수 있는 호출하여:

addOne :: Tree Int -> Tree Int
addOne (Branch a b) = Branch (addOne a) (addOne b)
addOne (Leaf a)     = Leaf (a + 1)

첫째,이는 사실을 재귀 정의합니다.그것은 데이터 생성자를 지점과 잎으로 케이스(그리고 이후 잎은 최소화하고 이들은 가능한 경우)에서,우리는 기능이 종료될 것입니다.

무엇을 가지고 갈 것 쓰 addOne 에는 반복적 스타일은?무엇이 반복으로 임의의 번호를 가지처럼 보이나요?

또한,이 류의 재귀할 수 있는 종종 고려의 관점에서,"함수".우리가 만들 수 있습니다 나무를 함수에 의해 정의:

instance Functor Tree where fmap f (Leaf a)     = Leaf (f a)
                            fmap f (Branch a b) = Branch (fmap f a) (fmap f b)

고 정의하기:

addOne' = fmap (+1)

우리할 수 있는 요소 기타 계획을 재귀 같은 catamorphism(또는 접)에 대한 대수 데이터를 입력합니다.를 사용하여 catamorphism 작성할 수 있습니다:

addOne'' = cata go where
           go (Leaf a) = Leaf (a + 1)
           go (Branch a b) = Branch a b

Stack overflow 가 발생하는 경우 당신은 프로그래밍 언어에는 없는 내장 메모리 관리....그렇지 않으면 뭔가 있는지 확인 하십시오에서 당신의 기능(나 함수 호출,STDLbs,etc.).지 않고 재귀 그것은 단순히 가능하지 않을 것처럼...Google 또는 SQL,또는 어떤 장소를 해야 하나 효율적으로 정렬을 통해 큰 데이터 구조(클래스)또는 데이터베이스가 있습니다.

재귀는 방법을 이동하려는 경우 반복을 통해 파일 확는 방법을 찾아'*|?grep*'작동합니다.좀 이중 재귀,특히 파이프(지만하지 않는 한 무리의 장애물 이렇게 많은 좋아하는 경우 그것은 아무것도록 거기 위해 사용하는 다른 사람).

높은 수준 언어로도 그 소리/cpp 를 구현할 수 있습니다 그것이 동일한 배경에 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top