재귀 함수 작동 방식 이해


115

제목이 설명 하듯이, 저는 아직까지 파악할 수 없었던 매우 기본적인 프로그래밍 질문이 있습니다. 모든 (매우 영리한) 필터링 "재귀를 이해하려면 먼저 재귀를 이해해야합니다." 다양한 온라인 스레드에서 답장을 보내지 만 아직 잘 모르겠습니다.

우리가 모르는 것을 알지 못하는 상황에 직면했을 때 잘못된 질문을하거나 올바른 질문을 잘못하는 경향이 있음을 이해합니다. 비슷한 견해를 가진 사람이 일부를 공유 할 수 있기를 바랍니다. 저를 위해 재귀 전구를 켜는 데 도움이 될 약간의 지식!

다음은 함수입니다 (구문은 Swift로 작성 됨).

func sumInts(a: Int, b: Int) -> Int {
    if (a > b) {
        return 0
    } else {
        return a + sumInts(a: a + 1, b: b)
    }
}

2와 5를 인수로 사용합니다.

println(sumInts(a: 2, b: 5))

분명히 대답은 14입니다. 그러나 그 가치가 어떻게 달성되는지 확실하지 않습니다.

다음은 내 두 가지 끊기입니다.

  1. 함수는 조건이 충족 될 때까지 재귀 적으로 호출됩니다. 그 조건은 a> b입니다. 이 조건이 충족되면 0을 반환합니다. 언뜻보기에 반환 값은 분명히 잘못된 0이 될 것으로 예상합니다.

  2. 각 반복에서 'a'의 값을 인쇄하면 예상되는 값이 생성됩니다. 2, 3, 4, 5 (첫 번째 조건을 충족하는 5 + 1> b 지점 : a> b)하지만 여전히 그렇지 않습니다. ' t 14의 가치가 어떻게 달성되는지보십시오.

내 첫 번째 생각은 다음과 유사한 일이 마술처럼 일어나고 있다는 것입니다.

var answer = a;
answer += a+1 until a > b;
return answer;   

그래서 마법을 배제하고, 나는 뭔가를 얻지 못하고 있습니다. 나는 암묵적으로 일어나는 것보다 더 많은 일이 일어나고 있는지 이해하고 싶습니다.

누군가가 이런 종류의 기능 중에 기술적으로 일어나는 일과 결과가 0이 아닌 이유를 친절하게 설명 할 수 있다면, 결국 어떻게 a + sumInts(a: a + 1, b: b) = 14당신의 빚을지게 될 것입니다.


7
재귀는 코드에서보다 수학적 용어로 이해하기 훨씬 쉬운 프로그래밍 개념 중 하나입니다. 좋은 정의가있다 여기
blgt

5
LearnYouARecursion, 세계적 수준의 교수로부터 완전한 문제 세트!
recursion.ninja

15
난 그냥 Google 검색 창에 "재귀"를 입력하도록 촉구 할 수 있습니다. 그 부활절 달걀 중 하나. 나는 당신을 위해 놀라움을 망치지 않을 것입니다.
Floris 2014 년


답변:


107

내가 생각하는 혼란은 "동일한 기능"여러 번 호출되는 것처럼 생각에 따른된다. "호출되는 동일한 함수의 많은 복사본"으로 생각하면 더 명확 할 수 있습니다.

함수의 복사본 하나만 0을 반환하고 첫 번째가 아닙니다 (마지막 복사본입니다). 따라서 첫 번째 호출의 결과는 0이 아닙니다.

두 번째 혼란에 대해서는 영어로 재귀 철자를 쓰는 것이 더 쉬울 것이라고 생각합니다. 이 줄을 읽으십시오 :

return a + sumInts(a + 1, b: b)

as "return the value of 'a'plus (the return value of another copy of the function, which is the copy 's value of 'a'plus (the return value of the other copy 's value of ' a> b 조건이 충족 될 때까지 함수의 각 복사본이 1 씩 증가한 새 복사본을 생성합니다.

a> b 조건에 도달 할 때까지 실행되는 동안 함수의 복사본의 (잠재적으로 임의의) 긴 스택이 있으며, 모두 다음 복사본의 결과를 기다리고 있습니다. 'a'에 추가해야합니다.

(편집 : 또한, 내가 언급 한 함수의 복사본 스택이 실제 메모리를 차지하는 실제 것이므로 너무 커지면 프로그램이 중단된다는 점에 유의해야합니다. 컴파일러는이를 최적화 할 수 있습니다. 그러나 스택 공간을 소모하는 것은 많은 언어에서 재귀 함수의 중요하고 불행한 제한입니다.)


7
Catfish_Man : 당신이 못 박았다고 생각합니다! 동일한 기능의 여러 "복사본"으로 생각하면 전체적으로 의미가 있습니다. 아직도 머리를 감싸고 있지만 올바른 길로 나를 보낸 것 같아요! 바쁜 하루를 보내고 동료 프로그래머를 도와 주셔서 감사합니다! 귀하의 답변을 정답으로 표시하겠습니다. 좋은 하루 보내세요!
Jason Elwood 2014 년

13
이것은 좋은 비유입니다. 각 "복사본"이 실제로 똑같은 코드이므로 너무 문자 그대로 받아들이지 않도록주의하십시오. 각 사본마다 다른 점은 작업중인 모든 데이터입니다.
Tim B

2
나는 그것을 사본으로 생각하는 것이 너무 행복하지 않습니다. 보다 직관적 인 설명은 함수 자체 (코드, 기능)와 스택 프레임 / 실행 컨텍스트가 연결된 함수 호출 (해당 함수의 인스턴스화)을 구분하는 것입니다. 함수는 지역 변수를 소유하지 않으며 함수가 호출 (호출) 될 때 인스턴스화됩니다. 그러나 나는이 재귀을 소개 할 것 같아요
토마스

5
올바른 용어는 함수의 여러 호출 이 있다는 것 입니다. 각 호출 자체가 인스턴스 변수 ab.
Theodore Norvell 2014 년

6
예,이 답변에 추가 할 수있는 상당한 정밀도가 있습니다. 나는 의도적으로 "함수 인스턴스"와 "단일 함수 호출의 활성화 레코드"사이의 구분을 생략했습니다. 문제를 이해하는 데 실제로 도움이되지 않는 추가 개념적 부 하였기 때문입니다. 다른 문제 를 이해하는 데 도움이 되므로 다른 곳에서도 여전히 유용한 정보입니다. 이 코멘트 : 그것을 위해 좋은 장소처럼 보인다
Catfish_Man

130

1. 조건이 충족 될 때까지 함수가 재귀 적으로 호출됩니다. 그 조건은 a > b입니다. 이 조건이 충족되면 0을 반환합니다. 언뜻보기에 반환 값은 분명히 잘못된 0이 될 것으로 예상합니다.

다음은 컴퓨터 컴퓨팅 sumInts(2,5)이 다음과 같이 생각할 수있는 것입니다.

I want to compute sumInts(2, 5)
for this, I need to compute sumInts(3, 5)
and add 2 to the result.
  I want to compute sumInts(3, 5)
  for this, I need to compute sumInts(4, 5)
  and add 3 to the result.
    I want to compute sumInts(4, 5)
    for this, I need to compute sumInts(5, 5)
    and add 4 to the result.
      I want to compute sumInts(5, 5)
      for this, I need to compute sumInts(6, 5)
      and add 5 to the result.
        I want to compute sumInts(6, 5)
        since 6 > 5, this is zero.
      The computation yielded 0, therefore I shall return 5 = 5 + 0.
    The computation yielded 5, therefore I shall return 9 = 4 + 5.
  The computation yielded 9, therefore I shall return 12 = 3 + 9.
The computation yielded 12, therefore I shall return 14 = 2 + 12.

보시다시피, 함수에 대한 일부 호출은 sumInts실제로 0을 반환하지만 이것은 컴퓨터가 여전히 그 0에 5를 더한 다음 결과에 4를 더한 다음 3, 2를 더해야하기 때문에 최종 값이 아닙니다. 우리 컴퓨터의 생각. 재귀에서 컴퓨터는 재귀 호출을 계산할뿐만 아니라 재귀 호출에서 반환 된 값으로 수행 할 작업을 기억해야합니다. 이러한 종류의 정보가 저장 되는 스택 이라는 컴퓨터 메모리의 특별한 영역이 있습니다 .이 공간은 제한적이며 너무 재귀적인 기능은 스택을 소모 할 수 있습니다. 이것은 가장 사랑받는 웹 사이트에 이름을 부여 하는 스택 오버플로 입니다.

귀하의 진술은 컴퓨터가 재귀 호출을 할 때 있었던 것을 잊었다는 암시적인 가정을하는 것 같지만 그렇지 않습니다. 이것이 귀하의 결론이 귀하의 관찰과 일치하지 않는 이유입니다.

2. 각 반복에서 'a'의 값을 인쇄하면 예상되는 값이 생성됩니다. 2, 3, 4, 5 (첫 번째 조건을 충족하는 5 + 1> b 지점 : a> b)하지만 여전히 14의 가치가 어떻게 달성되는지 보지 마십시오.

이는 반환 값이 a자체가 아니라 a재귀 호출에 의해 반환 된 값과 값의 합계 이기 때문 입니다.


3
이 훌륭한 답변을 작성해 주셔서 감사합니다. Michael! +1!
Jason Elwood 2014 년

9
@JasonElwood sumInts실제로 "컴퓨터 생각"을 기록하도록 수정하면 도움이 될 수 있습니다 . 이러한 함수를 직접 작성했다면 아마도 "알았을 것입니다"!
Michael Le Barbier Grünewald 2014 년

4
이것은 좋은 대답이지만 "스택"이라는 데이터 구조에서 함수 활성화가 발생 해야 한다는 요구 사항 은 없습니다 . 재귀는 연속 전달 스타일로 구현할 수 있으며이 경우 스택이 전혀 없습니다. 스택은 특히 효율적이므로 일반적으로 사용되는 연속 개념의 수정일뿐입니다.
Eric Lippert 2014 년

1
@EricLippert 재귀를 구현하는 데 사용되는 기술 은 그 자체 로 흥미로운 주제 이지만, "작동 방식"을 이해하려는 OP가 사용되는 다양한 메커니즘에 노출되는 것이 유용할지 확신 할 수 없습니다. 계속 더 일반적인 프로그래밍 패러다임보다 본질적으로 더 열심히 스타일 또는 확장 기반 언어 (예를 들면 텍 M4)를하지 않는 통과하는 동안, 나는하지 않습니다이 "이국적인"그리고 약간의 라벨에 의해 누구 범죄 의 거짓말 "항상에서 발생 같은 스택을"해야 OP가 개념을 이해하도록 도와주세요. (그리고 일종의 스택은 항상 관련됩니다.)
Michael Le Barbier Grünewald 2014 년

1
소프트웨어가 수행중인 작업을 기억하고 함수를 재귀 적으로 호출 한 다음 반환 될 때 원래 상태로 돌아갈 수있는 방법이 있어야합니다. 이 메커니즘은 스택처럼 작동하므로 다른 데이터 구조가 사용 되더라도 스택이라고 부르는 것이 편리합니다.
Barmar 2014 년

48

재귀를 이해하려면 문제를 다른 방식으로 생각해야합니다. 전체적으로 의미가있는 대규모 논리적 단계 순서 대신에 큰 문제를 가져 와서 작은 문제로 나누어 해결 한 후 하위 문제에 대한 답을 얻은 후 하위 문제의 결과를 결합하여 더 큰 문제에 대한 해결책. 당신과 당신의 친구들이 거대한 양동이에있는 구슬의 수를 세어야한다고 생각하세요. 당신은 각각 더 작은 양동이를 가져 와서 그것들을 개별적으로 세고, 당신이 끝났을 때 당신은 총계를 더합니다. 이제 여러분 각자가 친구를 찾아서 양동이를 더 나눈다면, 당신은 다른 친구들이 할 때까지 기다려야합니다. 총계를 알아 내서 여러분 각자에게 다시 가져 와서 합산하세요. 등등.

함수가 자신을 재귀 적으로 호출 할 때마다 문제의 하위 집합이 포함 된 새 컨텍스트를 만들고 해당 부분이 해결되면 이전 반복이 완료 될 수 있도록 반환된다는 점을 기억해야합니다.

단계를 보여 드리겠습니다.

sumInts(a: 2, b: 5) will return: 2 + sumInts(a: 3, b: 5)
sumInts(a: 3, b: 5) will return: 3 + sumInts(a: 4, b: 5)
sumInts(a: 4, b: 5) will return: 4 + sumInts(a: 5, b: 5)
sumInts(a: 5, b: 5) will return: 5 + sumInts(a: 6, b: 5)
sumInts(a: 6, b: 5) will return: 0

sumInts (a : 6, b : 5)가 실행되면 결과를 계산할 수 있으므로 얻은 결과로 체인을 다시 올라갑니다.

 sumInts(a: 6, b: 5) = 0
 sumInts(a: 5, b: 5) = 5 + 0 = 5
 sumInts(a: 4, b: 5) = 4 + 5 = 9
 sumInts(a: 3, b: 5) = 3 + 9 = 12
 sumInts(a: 2, b: 5) = 2 + 12 = 14.

재귀 구조를 나타내는 또 다른 방법 :

 sumInts(a: 2, b: 5) = 2 + sumInts(a: 3, b: 5)
 sumInts(a: 2, b: 5) = 2 + 3 + sumInts(a: 4, b: 5)  
 sumInts(a: 2, b: 5) = 2 + 3 + 4 + sumInts(a: 5, b: 5)  
 sumInts(a: 2, b: 5) = 2 + 3 + 4 + 5 + sumInts(a: 6, b: 5)
 sumInts(a: 2, b: 5) = 2 + 3 + 4 + 5 + 0
 sumInts(a: 2, b: 5) = 14 

2
아주 잘 했어요, 롭 매우 명확하고 이해하기 쉬운 방식으로 배치했습니다. 시간을 내 주셔서 감사합니다!
Jason Elwood 2014 년

3
이것은 이론과 기술적 세부 사항에 들어 가지 않고 진행되는 일을 가장 명확하게 표현하여 실행의 각 단계를 명확하게 보여줍니다.
Bryan

2
기뻐요. :) 이러한 것들을 설명하는 것이 항상 쉬운 것은 아닙니다. 칭찬 해주셔서 감사합니다.

1
+1. 이것이 내가 그것을 설명하는 방법이며, 특히 구조의 마지막 예를 사용하여 설명합니다. 무슨 일이 일어나고 있는지 시각적으로 펼치면 도움이됩니다.
KChaloux 2014 년

40

재귀는 이해하기 까다로운 주제이며 여기서 완전히 정의 할 수 없다고 생각합니다. 대신 여기에있는 특정 코드에 초점을 맞추고 솔루션이 작동하는 이유에 대한 직관과 코드가 결과를 계산하는 방법에 대한 메커니즘을 모두 설명하려고 노력할 것입니다.

여기에 제공 한 코드는 다음 문제를 해결합니다. a에서 b까지의 모든 정수의 합계를 알고 싶습니다. 예를 들어, 2에서 5까지의 숫자 합계를 원합니다.

2 + 3 + 4 + 5

문제를 재귀 적으로 해결하려고 할 때 첫 번째 단계 중 하나는 문제를 동일한 구조의 작은 문제로 나누는 방법을 파악하는 것입니다. 따라서 2에서 5까지의 숫자를 더하고 싶다고 가정 해 보겠습니다. 이를 단순화하는 한 가지 방법은 위의 합계가 다음과 같이 다시 작성 될 수 있음을 확인하는 것입니다.

2 + (3 + 4 + 5)

여기에서 (3 + 4 + 5)는 3과 5 사이의 모든 정수의 합입니다. 즉, 2에서 5 사이의 모든 정수의 합을 알고 싶다면 3에서 5 사이의 모든 정수의 합을 계산하여 시작한 다음 2를 더합니다.

그렇다면 3과 5 사이의 모든 정수의 합을 어떻게 계산합니까? 음, 그 합계는

3 + 4 + 5

대신 다음과 같이 생각할 수 있습니다.

3 + (4 + 5)

여기에서 (4 + 5)는 4와 5 사이의 모든 정수의 합입니다. 따라서 3에서 5 사이의 모든 숫자의 합계를 계산하려면 4에서 5 사이의 모든 정수의 합계를 계산 한 다음 3을 더합니다.

여기에 패턴이 있습니다! a와 b (포함) 사이의 정수 합계를 계산하려면 다음을 수행 할 수 있습니다. 먼저 a + 1과 b (포함) 사이의 정수 합계를 계산합니다. 다음으로 해당 합계에를 추가합니다. "a + 1과 b 사이의 정수의 합을 계산하라"는 것은 우리가 이미 풀려고하는 문제와 거의 비슷하지만 매개 변수가 약간 다르다는 것을 알게 될 것입니다. a에서 b로 계산하는 대신 a + 1에서 b로 계산합니다. 이것이 재귀 적 단계입니다. 더 큰 문제 ( "a에서 b 로의 합, 포함")를 해결하기 위해 문제를 더 작은 버전 ( "a + 1에서 b 로의 합, 포함")으로 줄입니다.

위에있는 코드를 살펴보면 다음 단계가 있음을 알 수 있습니다.

return a + sumInts(a + 1, b: b)

이 코드는 위의 논리를 간단히 번역 한 것입니다. a에서 b까지 합산하려면 a + 1에서 b까지 합산하여 시작합니다 (즉, s에 대한 재귀 호출 sumInt) a.

물론이 접근 방식 자체는 실제로 작동하지 않습니다. 예를 들어, 5에서 5까지의 모든 정수의 합을 어떻게 계산할까요? 음, 현재 논리를 사용하여 6과 5 사이의 모든 정수의 합을 계산 한 다음 5를 더합니다. 그러면 6과 5 사이의 모든 정수의 합을 어떻게 계산합니까? 글쎄, 우리의 현재 논리를 사용하여 7과 5 사이의 모든 정수의 합을 계산하고 6을 더할 것입니다. 여기에 문제가 있음을 알 수 있습니다-이것은 계속 진행됩니다!

재귀 문제 해결에서는 문제 단순화를 중단하고 대신 직접 해결하는 방법이 있어야합니다. 일반적으로 답을 즉시 결정할 수있는 간단한 사례를 찾은 다음 간단한 사례가 발생할 때 직접 해결하도록 솔루션을 구성합니다. 이를 일반적으로 기본 사례 또는 재귀 적 기반 이라고 합니다 .

그렇다면이 특정 문제의 기본 사례는 무엇입니까? a에서 b까지의 정수를 합산 할 때 a가 b보다 크면 답은 0입니다. 범위에 숫자가 없습니다! 따라서 솔루션을 다음과 같이 구성합니다.

  1. a> b이면 답은 0입니다.
  2. 그렇지 않으면 (a ≤ b) 다음과 같이 답을 얻으십시오.
    1. a + 1과 b 사이의 정수 합계를 계산합니다.
    2. 대답을 얻으려면를 추가하십시오.

이제이 의사 코드를 실제 코드와 비교하십시오.

func sumInts(a: Int, b: Int) -> Int {
    if (a > b) {
        return 0
    } else {
        return a + sumInts(a + 1, b: b)
    }
}

의사 코드로 설명 된 솔루션과이 실제 코드 사이에는 거의 정확히 일대일 맵이 있습니다. 첫 번째 단계는 기본 케이스입니다. 빈 숫자 범위의 합계를 요청하면 0이됩니다. 그렇지 않으면 a + 1과 b 사이의 합계를 계산 한 다음 a를 더합니다.

지금까지 코드에 대한 높은 수준의 아이디어를 제공했습니다. 하지만 두 가지 다른 아주 좋은 질문이있었습니다. 첫째, 함수가 a> b이면 0을 반환한다고하는데 왜 항상 0을 반환하지 않습니까? 둘째, 14는 실제로 어디에서 왔습니까? 차례대로 살펴 보겠습니다.

아주 아주 간단한 경우를 시도해 봅시다. 전화하면 sumInts(6, 5)어떻게 되나요? 이 경우 코드를 살펴보면 함수가 0을 반환하는 것을 알 수 있습니다. 그게 옳은 일입니다. 범위에 숫자가 없습니다. 이제 더 열심히 시도하십시오. 전화하면 sumInts(5, 5)어떻게 되나요? 음, 다음과 같은 일이 발생합니다.

  1. 전화 sumInts(5, 5)하세요. 우리 else는 'a + sumInts (6, 5)'의 값을 반환 하는 분기에 들어갑니다.
  2. sumInts(5, 5)무엇인지 확인 하려면 현재 sumInts(6, 5)수행중인 작업을 일시 중지하고을 호출해야 sumInts(6, 5)합니다.
  3. sumInts(6, 5)호출됩니다. if분기에 들어가서를 반환합니다 0. 그러나이 인스턴스는 sumInts에서 호출 sumInts(5, 5)되었으므로 반환 값은 sumInts(5, 5)최상위 호출자가 아닌로 다시 전달됩니다 .
  4. sumInts(5, 5)이제 5 + sumInts(6, 5)다시 계산할 수 있습니다 5. 그런 다음 최상위 호출자에게 반환합니다.

여기서 값 5가 어떻게 형성되었는지 확인하십시오. 에 대한 한 번의 활성 통화로 시작했습니다 sumInts. 이로 인해 또 다른 재귀 호출이 시작되었고 해당 호출에서 반환 된 값은 정보를 sumInts(5, 5). sumInts(5, 5)그런 다음에 대한 호출 은 계산을 수행하고 호출자에게 값을 반환했습니다.

을 사용하여 시도하면 다음과 같은 sumInts(4, 5)결과가 발생합니다.

  • sumInts(4, 5)반환을 시도합니다 4 + sumInts(5, 5). 이를 위해sumInts(5, 5) .
    • sumInts(5, 5)반환을 시도합니다 5 + sumInts(6, 5). 이를 위해sumInts(6, 5) .
    • sumInts(6, 5)sumInts(5, 5).</li> <li>sumInts (5, 5) now has a value forsumInts (6, 5) , namely 0. It then returns5 + 0 = 5` 로 0을 반환합니다 .
  • sumInts(4, 5)이제에 대한 값 sumInts(5, 5), 즉 5가 4 + 5 = 9있습니다. 그런 다음을 반환합니다 .

즉, 반환되는 값은 한 번에 하나씩 값을 합산하여 구성되며, 매번 특정 재귀 호출에 의해 반환 된 값 하나 sumIntsa. 재귀가 바닥에 도달하면 가장 깊은 호출은 0을 반환합니다. 그러나이 값은 재귀 호출 체인을 즉시 종료하지 않습니다. 대신, 그 값을 한 계층 위의 재귀 호출에 다시 전달합니다. 그런 식으로 각 재귀 호출은 하나의 숫자를 더 추가하고 체인에서 더 높은 값을 반환하여 전체 합산으로 절정에 이릅니다. 연습으로 이것을 추적 해보십시오.sumInts(2, 5) 으로 시작하고 싶은을 .

도움이 되었기를 바랍니다!


3
이러한 포괄적 인 답변을 공유하기 위해 바쁜 하루를 보내 주셔서 감사합니다! 여기에는 재귀 함수에 대해 머리를 숙이고 미래에이 게시물을 우연히 발견하는 다른 사람들에게 확실히 도움이되는 많은 훌륭한 정보가 있습니다. 다시 한 번 감사 드리며 좋은 하루 되세요!
Jason Elwood 2014 년

22

지금까지 여기에 좋은 답변이 있지만 다른 방법을 사용하는 답변을 하나 더 추가하겠습니다.

먼저, 흥미로울 수있는 간단한 재귀 알고리즘에 대한 많은 기사를 작성했습니다. 보다

http://ericlippert.com/tag/recursion/

http://blogs.msdn.com/b/ericlippert/archive/tags/recursion/

그것들은 최신 순서로되어 있으므로 아래에서 시작하십시오.

둘째, 지금까지 모든 답변은 함수 활성화 를 고려하여 재귀 적 의미를 설명했습니다 . 각각의 호출은 새로운 활성화 를 만들고 재귀 호출은이 활성화의 컨텍스트에서 실행됩니다. 그것은 그것을 생각하는 좋은 방법이지만, 또 다른 동등한 방법이 있습니다 : 스마트 텍스트 찾기 및 바꾸기 .

함수를 좀 더 간결한 형식으로 다시 작성하겠습니다. 이것을 특정 언어로 생각하지 마십시오.

s = (a, b) => a > b ? 0 : a + s(a + 1, b)

그게 말이 되길 바랍니다. 조건부 연산자에 익숙하지 않은 경우 형식 condition ? consequence : alternative이며 그 의미가 명확 해집니다.

이제 우리는 평가하고자하는 s(2,5) 함수 본문이있는 통화의 대체 텍스트를 수행하여 우리는 그렇게, 다음 교체 a2b함께 5:

s(2, 5) 
---> 2 > 5 ? 0 : 2 + s(2 + 1, 5)

이제 조건부를 평가하십시오. 텍스트 2 > 5false.

---> false ? 0 : 2 + s(2 + 1, 5)

이제 모든 거짓 조건을 대안으로 텍스트로 바꾸고 모든 참 조건을 결과로 바꿉니다. 거짓 조건부 만 있으므로 해당 표현식을 대안으로 텍스트로 대체합니다.

---> 2 + s(2 + 1, 5)

이제 모든 +기호 를 입력하지 않아도되도록 상수 산술을 해당 값으로 텍스트로 대체합니다. (이것은 약간의 속임수이지만 모든 괄호를 추적하고 싶지는 않습니다!)

---> 2 + s(3, 5)

이제 검색 및 바꾸기, 이번에는 호출의 본문, 3for a5for b. 호출에 대한 대체를 괄호 안에 넣습니다.

---> 2 + (3 > 5 ? 0 : 3 + s(3 + 1, 5))

이제 우리는 동일한 텍스트 대체 단계를 계속 수행합니다.

---> 2 + (false ? 0 : 3 + s(3 + 1, 5))  
---> 2 + (3 + s(3 + 1, 5))                
---> 2 + (3 + s(4, 5))                     
---> 2 + (3 + (4 > 5 ? 0 : 4 + s(4 + 1, 5)))
---> 2 + (3 + (false ? 0 : 4 + s(4 + 1, 5)))
---> 2 + (3 + (4 + s(4 + 1, 5)))
---> 2 + (3 + (4 + s(5, 5)))
---> 2 + (3 + (4 + (5 > 5 ? 0 : 5 + s(5 + 1, 5))))
---> 2 + (3 + (4 + (false ? 0 : 5 + s(5 + 1, 5))))
---> 2 + (3 + (4 + (5 + s(5 + 1, 5))))
---> 2 + (3 + (4 + (5 + s(6, 5))))
---> 2 + (3 + (4 + (5 + (6 > 5 ? 0 : s(6 + 1, 5)))))
---> 2 + (3 + (4 + (5 + (true ? 0 : s(6 + 1, 5)))))
---> 2 + (3 + (4 + (5 + 0)))
---> 2 + (3 + (4 + 5))
---> 2 + (3 + 9)
---> 2 + 12
---> 14

여기서 우리가 한 모든 것은 단순한 텍스트 대체 였습니다. 정말로 나는 "2 + 1"을 "3"으로 대체하지 말았어야했는데, 그렇게해야 할 때까지 계속해서 그렇게해야했지만, 교육적으로는 읽기가 어려워 졌을 것입니다.

함수 활성화는 함수 호출을 호출 본문으로 대체하고 형식 매개 변수를 해당 인수로 대체하는 것입니다. 괄호를 지능적으로 도입하는 데주의해야하지만 그 외에는 텍스트 교체 일뿐입니다.

물론 대부분의 언어는 실제로 활성화를 텍스트 대체로 구현 하지 않지만 논리적으로 그게 다입니다.

그렇다면 무한 재귀는 무엇입니까? 텍스트 대체가 멈추지 않는 재귀! 결국 우리가 더 이상 s대체 할 것이없는 단계에 도달 한 다음 산술에 대한 규칙을 적용 할 수 있음을 주목 하십시오.


좋은 예이지만 더 복잡한 계산을 진행하면 마음이 아프다. 예 : 이진 트리에서 공통 조상 찾기.
CodeYogi

11

일반적으로 재귀 함수가 어떻게 작동하는지 알아내는 방법은 기본 케이스를보고 거꾸로 작업하는 것입니다. 이 기능에 적용된 기술은 다음과 같습니다.

먼저 기본 케이스 :

sumInts(6, 5) = 0

그런 다음 호출 스택 에서 바로 위에있는 호출 :

sumInts(5, 5) == 5 + sumInts(6, 5)
sumInts(5, 5) == 5 + 0
sumInts(5, 5) == 5

그런 다음 호출 스택에서 바로 위에있는 호출 :

sumInts(4, 5) == 4 + sumInts(5, 5)
sumInts(4, 5) == 4 + 5
sumInts(4, 5) == 9

등등:

sumInts(3, 5) == 3 + sumInts(4, 5)
sumInts(3, 5) == 3 + 9
sumInts(3, 5) == 12

등등:

sumInts(2, 5) == 2 + sumInts(3, 5)
sumInts(4, 5) == 2 + 12
sumInts(4, 5) == 14

통지 우리가 함수에 우리의 원래 통화에 도착했습니다 sumInts(2, 5) == 14

이러한 호출이 실행되는 순서 :

sumInts(2, 5)
sumInts(3, 5)
sumInts(4, 5)
sumInts(5, 5)
sumInts(6, 5)

이러한 호출이 반환되는 순서 :

sumInts(6, 5)
sumInts(5, 5)
sumInts(4, 5)
sumInts(3, 5)
sumInts(2, 5)

반환 되는 순서대로 호출을 추적하여 함수가 작동하는 방식에 대한 결론에 도달했습니다 .


5

나는 그것을 줄 것이다.

방정식 a + sumInts (a + 1, b)를 실행하면 최종 답이 어떻게 14인지 보여 드리겠습니다.

//the sumInts function definition
func sumInts(a: Int, b: Int) -> Int {
    if (a > b) {
        return 0
    } else {
        return a + sumInts(a + 1, b)
    }
}

Given: a = 2 and b = 5

1) 2 + sumInts(2+1, 5)

2) sumInts(3, 5) = 12
   i) 3 + sumInts(3+1, 5)
   ii) 4 + sumInts(4+1, 5)
   iii) 5 + sumInts(5+1, 5)
   iv) return 0
   v) return 5 + 0
   vi) return 4 + 5
   vii) return 3 + 9

3) 2 + 12 = 14.

추가 질문이 있으면 알려주십시오.

다음은 재귀 함수의 또 다른 예입니다.

한 남자가 방금 대학을 졸업했습니다.

t는 년 단위의 시간입니다.

은퇴하기 전에 총 실제 근무 연수는 다음과 같이 계산할 수 있습니다.

public class DoIReallyWantToKnow 
{
    public int howLongDoIHaveToWork(int currentAge)
    {
      const int DESIRED_RETIREMENT_AGE = 65;
      double collectedMoney = 0.00; //remember, you just graduated college
      double neededMoneyToRetire = 1000000.00

      t = 0;
      return work(t+1);
    }

    public int work(int time)
    {
      collectedMoney = getCollectedMoney();

      if(currentAge >= DESIRED_RETIREMENT_AGE 
          && collectedMoney == neededMoneyToRetire
      {
        return time;
      }

      return work(time + 1);
    }
}

그리고 그것은 누군가를 우울하게하기에 충분할 것입니다. ;-피


5

재귀. 컴퓨터 과학에서 재귀는 Finite Automata의 주제에서 자세히 다룹니다.

가장 단순한 형태로는 자기 참조입니다. 예를 들어, "my car is a car"라는 말은 재귀 적 표현입니다. 문제는 문이 끝나지 않을 것이라는 점에서 무한 재귀라는 것입니다. "자동차"의 정의는 "자동차"이므로 대체 될 수 있다는 것입니다. 그러나 대체의 경우에도 "내 차는 차"가되기 때문에 끝이 없다.

"내 차는 벤틀리입니다. 내 차는 파란색입니다."라는 문구가 있으면 다를 수 있습니다. 이 경우 두 번째 상황에서 자동차의 대체는 "bentley"가되어 "my bentley is blue"가됩니다. 이러한 유형의 대체는 문맥 자유 문법을 통해 컴퓨터 과학에서 수학적으로 설명됩니다 .

실제 대체는 생산 규칙입니다. 문이 S로 표현되고 자동차가 "bentley"가 될 수있는 변수라는 점을 감안하면이 문은 재귀 적으로 재구성 될 수 있습니다.

S -> "my"S | " "S | CS | "is"S | "blue"S | ε
C -> "bentley"

이것은 |선택의 여지가 있다는 것을 의미하기 때문에 여러 방법으로 구성 될 수 있습니다 . S이러한 선택 사항 중 하나로 대체 될 수 있으며 S는 항상 비어있는 상태로 시작합니다. ε수단은 생성을 종료한다. S대체 될 수있는 것처럼 다른 변수도 마찬가지입니다 (하나만 있고 C"bentley"를 나타냄).

그래서 시작으로 S비어있는, 그리고 첫 번째 선택으로 대체 "my"S S된다

"my"S

S변수를 나타내므로 여전히 대체 될 수 있습니다. "my"를 다시 선택하거나 ε을 선택하여 종료 할 수 있지만 원래 진술을 계속 작성하겠습니다. 우리는 수단 공간 선택 S으로 대체됩니다" "S

"my "S

다음으로 C를 선택하겠습니다.

"my "CS

그리고 C는 교체를위한 선택권이 하나뿐입니다.

"my bentley"S

그리고 다시 S를위한 공간

"my bentley "S

등등 "my bentley is"S, "my bentley is "S, "my bentley is blue"S,"my bentley is blue" (ε에 대한 S를 교체하면 생산을 종료) 우리는 반복적으로 우리의 문 "내 벤틀리이 파란색"을 구축했다.

재귀를 이러한 생산 및 대체라고 생각하십시오. 프로세스의 각 단계는 최종 결과를 생성하기 위해 이전 단계를 대체합니다. 2에서 5까지의 재귀 합계의 정확한 예에서는 생산으로 끝납니다.

S -> 2 + A
A -> 3 + B
B -> 4 + C
C -> 5 + D
D -> 0

이것은

2 + A
2 + 3 + B
2 + 3 + 4 + C
2 + 3 + 4 + 5 + D
2 + 3 + 4 + 5 + 0
14

유한 상태 오토마타 또는 문맥없는 문법이 재귀에 대한 첫 번째 직관을 구축하는 데 도움이 될 수있는 최고의 예라고 확신하지 않습니다. 좋은 예이지만 이전 CS 배경이없는 프로그래머에게는 다소 익숙하지 않을 수 있습니다.
chi

4

재귀 함수를 이해하는 가장 좋은 방법은 재귀 데이터 구조를 처리하도록 만들어 졌다는 사실을 깨닫는 것입니다. 그러나 원래의 기능에 sumInts(a: Int, b: Int)재귀 적 계산에서 숫자의 합 것을 a하려면 b, 재귀 데이터 구조 ...하자 시도 약간 수정 된 버전이하지 않는 것 같습니다 당신이 추가 할 것입니다 얼마나 많은 숫자입니다.sumInts(a: Int, n: Int)n

이제 sumInts는 자연수에 대해 재귀 적 n입니다. 여전히 재귀 데이터가 아닙니다. 음, 자연수는 Peano 공리를 사용하는 재귀 데이터 구조로 간주 될 수 있습니다.

enum Natural = {
    case Zero
    case Successor(Natural)
}

따라서 0 = 0, 1 = Succesor (Zero), 2 = Succesor (Succesor (Zero)) 등입니다.

재귀 적 데이터 구조가 있으면 함수에 대한 템플릿이 있습니다. 각 비 재귀 사례에 대해 값을 직접 계산할 수 있습니다. 재귀 사례의 경우 재귀 함수가 이미 작동하고 있다고 가정 하고이를 사용하여 사례를 계산하지만 인수를 분해합니다. Natural의 경우 대신을 Succesor(n)사용하거나을 사용 n하는 대신을 n사용 n - 1합니다.

// sums n numbers beginning from a
func sumInts(a: Int, n: Int) -> Int {
    if (n == 0) {
        // non recursive case
    } else {
        // recursive case. We use sumInts(..., n - 1)
    }
}

이제 재귀 함수는 프로그래밍하기가 더 간단합니다. 첫째, 기본 케이스,n=0 . 숫자를 추가하지 않으려면 무엇을 반환해야합니까? 물론 대답은 0입니다.

재귀 사례는 어떻습니까? 로 n시작하는 숫자 를 추가하고 싶고 a이미 작동하는 sumInts함수 가있는 경우 n-1? 음, 우리는 추가해야 a하고있는 invoke sumIntsa + 1, 우리가 끝낼 수 있도록 :

// sums n numbers beginning from a
func sumInts(a: Int, n: Int) -> Int {
    if (n == 0) {
        return 0
    } else {
        return a + sumInts(a + 1, n - 1)
    }
}

좋은 점은 이제 낮은 수준의 재귀에서 생각할 필요가 없다는 것입니다. 다음 사항 만 확인하면됩니다.

  • 재귀 데이터의 기본 사례에 대해서는 재귀를 사용하지 않고 답을 계산합니다.
  • 재귀 데이터의 재귀 사례의 경우 구조화 해제 된 데이터에 대한 재귀를 사용하여 답을 계산합니다.

4

Nisan과 Schocken의 함수 구현에 관심이있을 수 있습니다 . 링크 된 pdf는 무료 온라인 과정의 일부입니다. 학생이 가상 머신 언어 대 머신 언어 컴파일러를 작성해야하는 가상 머신 구현의 두 번째 부분을 설명합니다. 그들이 제안하는 함수 구현은 스택 기반이기 때문에 재귀가 가능합니다.

함수 구현을 소개하려면 다음 가상 머신 코드를 고려하십시오.

여기에 이미지 설명 입력

Swift가이 가상 머신 언어로 컴파일 된 경우 다음 Swift 코드 블록 :

mult(a: 2, b: 3) - 4

아래로 컴파일됩니다

push constant 2  // Line 1
push constant 3  // Line 2
call mult        // Line 3
push constant 4  // Line 4
sub              // Line 5

가상 머신 언어는 글로벌 스택을 중심으로 설계되었습니다 .push constant n이 전역 스택에 정수를 푸시합니다.

라인 1과 2를 실행 한 후 스택은 다음과 같습니다.

256:  2  // Argument 0
257:  3  // Argument 1

256그리고 257메모리 주소입니다.

call mult 반환 줄 번호 (3)를 스택에 푸시하고 함수의 지역 변수를위한 공간을 할당합니다.

256:  2  // argument 0
257:  3  // argument 1
258:  3  // return line number
259:  0  // local 0

... 라벨로 이동 function mult합니다. 내부 코드 mult가 실행됩니다. 이 코드를 실행 한 결과 함수의 0 번째 지역 변수에 저장된 2와 3의 곱을 계산합니다.

256:  2  // argument 0
257:  3  // argument 1
258:  3  // return line number
259:  6  // local 0

returnmult에서 ing 직전 에 다음과 같은 줄을 알 수 있습니다.

push local 0  // push result

제품을 스택으로 밀어 넣습니다.

256:  2  // argument 0
257:  3  // argument 1
258:  3  // return line number
259:  6  // local 0
260:  6  // product

돌아 오면 다음과 같은 일이 발생합니다.

  • 스택의 마지막 값을 0 번째 인수 (이 경우 256)의 메모리 주소로 팝합니다. 이것은 그것을 놓기에 가장 편리한 장소입니다.
  • 0 번째 인수의 주소까지 스택의 모든 것을 버립니다.
  • 반환 라인 번호 (이 경우 3)로 이동 한 다음 진행합니다.

돌아온 후 4 행을 실행할 준비가되었으며 스택은 다음과 같습니다.

256:  6  // product that we just returned

이제 4를 스택에 넣습니다.

256:  6
257:  4

sub가상 머신 언어의 기본 기능입니다. 두 개의 인수를 취하고 결과를 0 번째 인수의 주소로 반환합니다.

이제 우리는

256:  2  // 6 - 4 = 2

이제 함수 호출의 작동 방식을 알았으므로 재귀 작동 방식을 이해하는 것은 비교적 간단합니다. 마법이 없다 이 아니라 스택입니다.

sumInts이 가상 머신 언어로 기능을 구현했습니다 .

function sumInts 0     // `0` means it has no local variables.
  label IF
    push argument 0
    push argument 1
    lte              
    if-goto ELSE_CASE
    push constant 0
    return
  label ELSE_CASE
    push constant 2
    push argument 0
    push constant 1
    add
    push argument 1
    call sumInts       // Line 15
    add                // Line 16
    return             // Line 17
// End of function

이제 나는 그것을 부를 것입니다.

push constant 2
push constant 5
call sumInts           // Line 21

코드가 실행되고를 lte반환 하는 중지 지점까지 도달 합니다 false. 이 시점에서 스택은 다음과 같습니다.

// First invocation
256:  2   // argument 0
257:  5   // argument 1
258:  21  // return line number
259:  2   // augend
// Second
260:  3   // argument 0
261:  5   // argument 1
262:  15  // return line number
263:  3   // augend
// Third
264:  4   // argument 0
265:  5   // argument 1
266:  15  // return line number
267:  4   // augend
// Fourth
268:  5   // argument 0
269:  5   // argument 1
270:  15  // return line number
271:  5   // augend
// Fifth
272:  6   // argument 0
273:  5   // argument 1
274:  15  // return line number
275:  0   // return value

이제 재귀를 "풀어"보겠습니다. return0, 15 행으로 이동하여 진행합니다.

271:  5
272:  0

16 행 : add

271:  5

17 번 줄 : return5 번, 15 번 줄로 가서 전진

267:  4
268:  5

16 행 : add

267:  9

17 번 줄 : return9 번, 15 번 줄로 이동하여 전진합니다.

263:  3
264:  9

16 행 : add

263:  12

17 번 줄 : return12 번, 15 번 줄로 이동하여 전진합니다.

259:  2
260:  12

16 행 : add

259:  14

행 17 : return14 고토 라인 (21)과 사전.

256:  14

거기에 있습니다. 재귀 : Glorified goto.


4

재귀를 배우고 이해하면서 알게 된 정말 좋은 팁 중 하나는 재귀를 통한 것 외에는 어떤 형태의 루프 구조도 갖지 않는 언어를 배우는 데 시간을 보내는 것입니다. 이렇게하면 연습을 통해 재귀를 사용하는 방법에 대한 좋은 느낌을 얻을 수 있습니다.

나는 http://www.htdp.org/ 를 따랐다 . 이것은 Scheme 튜토리얼 일뿐만 아니라 아키텍처와 디자인 측면에서 프로그램을 디자인하는 방법에 대한 훌륭한 소개이기도하다.

하지만 기본적으로 시간을 투자해야합니다. 재귀에 대한 '확고한'이해가 없다면 역 추적과 같은 특정 알고리즘은 항상 '어렵거나'심지어 '마법'처럼 보일 것입니다. 그래서 인내하십시오. :-디

도움이 되었기를 바랍니다. 행운을 빕니다!


3

이미 좋은 답변이 많이 있습니다. 여전히 나는 시도하고 있습니다.
호출 될 때 함수는 할당 된 메모리 공간을 가져와 호출자 함수 의 메모리 공간 에 쌓 입니다. 이 메모리 공간에서 함수는 전달 된 매개 변수, 변수 및 값을 유지합니다. 이 메모리 공간 은 함수의 종료 반환 호출과 함께 사라집니다. 스택의 개념이 진행됨에 따라 메모리 공간 에 따라 호출자 함수 이 활성화됩니다.

재귀 호출의 경우 동일한 함수가 여러 메모리 공간을 서로 쌓아 놓습니다. 그게 다야. 스택 이 컴퓨터의 메모리에서 어떻게 작동 하는지에 대한 간단한 아이디어는 구현에서 재귀가 어떻게 발생하는지에 대한 아이디어를 제공합니다.


3

약간의 주제에서 벗어난 것은 알지만 ... Google에서 재귀 를 찾아보십시오 ... 그 의미를 예를 들어 볼 수 있습니다. :-)


이전 버전의 Google은 다음 텍스트를 반환했습니다 (기억에서 인용 됨).

재귀

재귀 참조

2014 년 9 월 10 일에 재귀에 대한 농담이 업데이트되었습니다.

재귀

이것을 찾으 셨나요 : 재귀


다른 답변은 이 답변을 참조하십시오 .


3

재귀를 다중 클론 으로 생각 같은 일 .

clone [1]을 요청합니다. "2에서 5 사이의 숫자 합계"

+ clone[1]               knows that: result is 2 + "sum numbers between 3 and 5". so he asks to clone[2] to return: "sum numbers between 3 and 5"
|   + clone[2]           knows that: result is 3 + "sum numbers between 4 and 5". so he asks to clone[3] to return: "sum numbers between 4 and 5"
|   |   + clone[3]       knows that: result is 4 + "sum numbers between 5 and 5". so he asks to clone[4] to return: "sum numbers between 5 and 5"
|   |   |   + clone[4]   knows that: result is 5 + "sum numbers between 6 and 5". so he asks to clone[5] to return: "sum numbers between 6 and 5"
|   |   |   |   clone[5] knows that: he can't sum, because 6 is larger than 5. so he returns 0 as result.
|   |   |   + clone[4]   gets the result from clone[5] (=0)  and sums: 5 + 0,  returning 5
|   |   + clone[3]       gets the result from clone[4] (=5)  and sums: 4 + 5,  returning 9
|   + clone[2]           gets the result from clone[3] (=9)  and sums: 3 + 9,  returning 12
+ clone[1]               gets the result from clone[2] (=12) and sums: 2 + 12, returning 14

그리고 voilá !!


2

위의 많은 답변이 매우 좋습니다. 재귀를 해결하는 데 유용한 기술은 우리가하고 싶은 일을 먼저 설명하고 인간이 그것을 해결하는 것처럼 코딩하는 것입니다. 위의 경우 (위의 숫자를 사용하여) 연속 된 정수 시퀀스를 합산하려고합니다.

2, 3, 4, 5  //adding these numbers would sum to 14

이제이 줄은 혼란 스럽습니다 (잘못된 것은 아니지만 혼란 스럽습니다).

if (a > b) {
    return 0 
}

왜 테스트 a>b인가? 그리고 왜return 0

인간이하는 일을 더 가깝게 반영하도록 코드를 변경합시다.

func sumInts(a: Int, b: Int) -> Int {
  if (a == b) {
    return b // When 'a equals b' I'm at the most Right integer, return it
  }
  else {
    return a + sumInts(a: a + 1, b: b)
  }
}

더 인간 답게 할 수 있을까요? 예! 보통 우리는 왼쪽에서 오른쪽으로 합산합니다 (2 + 3 + ...). 그러나 위의 재귀는 오른쪽에서 왼쪽으로 합산됩니다 (... + 4 + 5). 그것을 반영하기 위해 코드를 변경하십시오 ( -조금 위협적 일 수 있지만 많지는 않습니다)

func sumInts(a: Int, b: Int) -> Int {
  if (a == b) {
    return b // When I'm at the most Left integer, return it
  }
  else {
    return sumInts(a: a, b: b - 1) + b
  }
}

어떤 사람들은 우리가 '먼'끝에서 시작하기 때문에이 기능이 더 혼란 스러울 수 있지만, 연습하면 자연스럽게 느껴질 수 있습니다 (또 다른 좋은 '사고'기법 : 재귀를 풀 때 '양쪽'을 시도합니다). 그리고 다시,이 함수는 인간 (대부분?)이하는 일을 반영합니다. 모든 왼쪽 정수의 합을 취하고 '다음'오른쪽 정수를 더합니다.


2

나는 재귀를 이해하는 데 어려움을 겪고 있었고이 블로그를 찾았고 이미이 질문을 보았으므로 공유해야한다고 생각했습니다. 이 블로그를 읽어야합니다. 저는 이것이 스택에 대해 설명하는 데 매우 도움이되었으며 심지어 두 개의 재귀가 스택에서 단계적으로 작동하는 방식을 설명합니다. 난 당신이 먼저 여기에 아주 잘 설명 할 방법 스택 작품을 이해하는 것이 좋습니다 여행 -에 - 더 - 스택

then now you will understand how recursion works now take a look of this post: 단계별 재귀 이해

여기에 이미지 설명 입력

그것의 프로그램 :

def hello(x):
    if x==1:
        return "op"
    else:
        u=1
        e=12
        s=hello(x-1)
        e+=1
        print(s)
        print(x)
        u+=1
    return e

hello(3)

여기에 이미지 설명 입력 여기에 이미지 설명 입력


2

재귀는 내가 다른 사람들이 그것에 대해 말하는 것을 읽지 않거나 내가 피할 수 있고 코드를 작성할 수 있다고 보는 것을 중단했을 때 나에게 의미가 있기 시작했습니다. 솔루션에서 문제를 발견하고 보지 않고 솔루션을 복제하려고했습니다. 나는 무기력하게 막혔을 때만 해결책을 봤습니다. 그런 다음 다시 복제하려고했습니다. 재귀 문제를 식별하고 해결하는 방법에 대한 이해와 감각을 개발할 때까지 여러 문제에 대해이 작업을 다시 수행했습니다. 이 수준에 이르면 문제를 만들고 해결하기 시작했습니다. 더 많은 도움이되었습니다. 때로는 혼자서 시도하고 고군분투해야만 배울 수 있습니다. 당신이 그것을 얻을 때까지.


0

피보나치 시리즈의 예를 들어 보겠습니다. 피보나치는

t (n) = t (n-1) + n;

n = 0이면 1

그래서 재귀 작품, 난 그냥 교체하는 방법을 살펴 보겠습니다 n에서 t(n)n-1등등. 그것은 본다:

t (n-1) = t (n-2) + n + 1;

t (n-1) = t (n-3) + n + 1 + n;

t (n-1) = t (n-4) + n + 1 + n + 2 + n;

.

.

.

t (n) = t (nk) + ... + (nk-3) + (nk-2) + (nk-1) + n;

경우 우리는 알고 t(0)=(n-k)같음 1다음은 n-k=0그래서 n=k우리는 대체 k와 함께 n:

t (n) = t (nn) + ... + (n-n + 3) + (n-n + 2) + (n-n + 1) + n;

생략 n-n하면 :

t (n) = t (0) + ... + 3 + 2 + 1 + (n-1) + n;

그래서 3+2+1+(n-1)+n자연수입니다. 그것은 다음과 같이 계산합니다.Σ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2

fib의 결과는 다음과 같습니다. O(1 + n²) = O(n²)

재귀 관계를 이해하는 가장 좋은 방법

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