재귀에 대한 이해를 향상시키기위한 자료? [닫은]


13

나는 재귀가 무엇인지 (패튼이 조건 내에서 돌파 한 후 일반적으로 그 라인 중 하나에서 자신을 호출하는 함수 인 패틴이 재발 할 때 ...) 재귀 함수를 자세히 연구하면 이해할 수 있습니다. 내 문제는 새로운 예를 볼 때 항상 처음에는 혼란 스럽다는 것입니다. 루프 또는 매핑, 압축, 중첩, 다형성 호출 등이 보이면 그냥 보고서 무슨 일이 일어나고 있는지 알 수 있습니다. 재귀 코드를 볼 때 내 생각 과정은 일반적으로 'wtf입니까?'입니다. 그 뒤에 '오 재귀 적'이 뒤 따른다.

이 분야의 기술을 쌓을 수있는 팁 / 계획 / 자원이 있습니까? 재귀는 일종의 이상한 개념이므로 문제를 해결하는 방법도 마찬가지로 이상하고 모호하지 않을 수 있다고 생각합니다.


28
재귀를 이해하려면 먼저 재귀를 이해해야합니다.
Andreas Johansson

1
닥터 수스의 '모자에 고양이가 돌아온다'는 이것이 완전히 유용한 것은 아니지만 고양이에 대한 재귀 호출은 그 성가신 얼룩을 제거합니다. :-) 또한 매우 빠르게 읽을 수 있다는 이점이 있습니다!
DKnight

2
연습, 연습, 연습.
David Thornley

20
이미이 질문에 답변했습니다 : programmers.stackexchange.com/questions/57243/…
Graham Borland

3
@Graham Borland : 이것이 무한 재귀의 예입니다. 대부분의 프로그램에서 기본 사례가 없으면 일반적으로 스택 오버플로 또는 메모리 부족 오류가 발생합니다. 웹 사이트 사용자의 경우 혼란을 초래할 수 있습니다. ;)
FrustratedWithFormsDesigner

답변:


10

간단한 것으로 시작하고 연필과 종이로 그것을 추적하십시오. 세 리오 술리. 시작하기에 좋은 곳은 트리 순회 알고리즘입니다. 정규 반복보다 재귀를 사용하여 훨씬 쉽게 처리 할 수 ​​있기 때문입니다. 복잡한 예일 필요는 없지만 간단하고 작업 할 수있는 것입니다.

그렇습니다. 이상하고 때로는 반 직관적이지만 클릭하면 "유레카"라고 말하면됩니다. 당신은 당신이 그것을 어떻게 이해 하지 못 했는지 궁금해 할 것입니다 ! ;) 나는 나무가 재귀에서 이해하기 가장 쉬운 구조이기 때문에 나무를 제안했으며 연필과 종이를 사용하여 작업하기가 쉽습니다. ;)


1
+1 이것이 내가 grok 한 방법입니다. 예를 들어 OO를 사용하는 경우 부모 자식 관계가있는 클래스를 만든 다음 객체에 특정 조상이 있는지 확인하는 함수 / 메소드를 시도하십시오.
Alb

5

The Little Lisper 책을 사용하여 Scheme을 강력히 권장합니다. 당신이 그것으로 끝나면, 당신 깊이 재귀 이해합니다. 거의 보장됩니다.


1
+1이 책은 정말 저를 위해 해냈습니다. 그러나 "작은 Schemer"로 개명되었습니다
mike30

4

나는 확실히 SICP를 제안한다. 또한 저자의 소개 과정 비디오를 확인 하십시오 . 그들은 엄청나게 마음을 열었습니다.

프로그래밍과 관련이없는 또 다른 길은 Hofstadter의 Gödel, Escher, Bach : Eternal Golden Braid 를 읽는 것 입니다. 일단 통과하면 재귀는 산술처럼 자연스럽게 보입니다. 또한, 당신은 P = nP라고 확신 할 것이고 당신은 사고 기계를 만들고 싶어 할 것입니다 – 그러나 그것은 이익과 비교할 때 아주 작은 부작용입니다.


GEB 는 어쨌든 읽을 가치가 있습니다. 그가 말한 것 중 일부가 약간 오래되었지만 (지난 40 년 동안 기초 CS 연구의 일부 진전이 있었다) 기본 이해는 그렇지 않다.
Donal Fellows

2

본질적으로 그것은 연습에 온다 ... 일반적인 문제 (정렬, 검색, 수학 문제 등)를 취하고 단일 함수를 여러 번 적용하면 이러한 문제를 해결할 수있는 방법을 볼 수 있는지 확인하십시오.

예를 들어 빠른 정렬은 목록의 요소를 두 개의 반쪽으로 이동 한 다음 각 반쪽에 다시 적용됩니다. 초기 정렬이 수행 될 때 해당 시점에서 두 개의 절반이 정렬되는 것에 대해 걱정하지 않아도됩니다. 오히려 피벗 요소를 가져 와서 한 요소의 모든 요소를 ​​한쪽보다 작게하고 다른 요소의 모든 요소를 ​​다른 쪽보다 크거나 같습니다. 이 시점에서 두 개의 새로운 작은 목록을 정렬하기 위해 재귀 적으로 호출하는 방법이 의미가 있습니까? 그들도 목록입니다. 더 작습니다. 그러나 여전히 정렬해야합니다.

재귀의 힘은 나누고 정복하는 개념입니다. 본질적으로 동일하지만 단지 작은 문제로 반복해서 문제를 나누십시오. 당신이 충분히 그렇게하면 결국 유일한 조각이 이미 해결 된 지점에 도달하면 루프 밖으로 돌아가서 문제가 해결됩니다. 이해 하기 전까지 언급 한 공부하십시오 . 시간이 걸리지 만 결국 더 쉬워 질 것입니다. 그런 다음 다른 문제를 해결하고 재귀 함수를 사용하여 문제를 해결하십시오! 행운을 빕니다!

편집 : 또한 재귀의 핵심 요소는 함수가 중지 할 수있는 보장 된 능력이라는 것을 추가해야합니다. 즉, 원래 문제의 분류가 지속적으로 작아 져야 하며 결국에는 중단 점이 보장 되어야합니다 (새로운 하위 문제를 해결할 수 있거나 이미 해결 된 시점).


예, 빠른 정렬 설명을 본 적이 있다고 생각합니다. 위의 알림에서 어떻게 작동하는지 상상할 수 있습니다. 재귀 표현력 / ​​유연성-대부분의 문제가 재귀 적 접근 방식으로 강제 될 수 있습니까 (최적이 아니더라도)? 나는 사람들이 그냥 지옥에서 원할 때마다 재귀를 사용할 수있는 것처럼 대부분의 사람들이 절차 적으로 다루는 그물에 코딩 퍼즐에 답하는 것을 보았습니다. 또한 루프 구문을 대체하기 위해 일부 언어가 의존하거나 재귀한다고 생각합니다. 그리고 당신은 보장 된 정지 점을 언급합니다. 나는 그중 하나가 열쇠가 될 수 있다고 생각합니다.
Andrew M

스스로 작성하는 좋은 간단한 시작 문제는 숫자의 계승을 찾는 재귀 프로그램을 작성하는 것입니다.
Kenneth

루핑 구조는 재귀 구조에 넣을 수 있습니다. 모든 재귀 구조는 루핑 구조에 넣을 수 있습니다. 재귀를 사용할 때 하드웨어 수준에서 사용되는 리소스 측면에서 많은 오버 헤드가 있음을 기억해야하기 때문에 재귀를 사용하지 않을 때와 언제를 배울 수 있으려면 시간과 연습이 필요합니다.
Kenneth

예를 들어, 빠른 정렬을 수행하는 루핑 구조를 만드는 것이 실현 가능하다는 것을 알 수 있었지만 ... 엄청나게 큰 고통이었고 어떻게했는지에 따라 재귀 함수보다 더 많은 시스템 리소스를 사용할 수 있습니다. 큰 배열의 경우.
Kenneth

여기 계승에 대한 나의 시도가 있습니다. 공정하게 말해서, 나는 이것을 전에 보았고, 메모리가 아니라 처음부터 그것을 썼지 만, 아마도 이전보다 더 쉬울 것입니다. JS에서 시도했지만 구문 분석 오류가 있었지만 Python에서는 작동합니다. def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Andrew M

2

개인적으로 최선의 방법은 연습을 통한 것이라고 생각합니다.

LOGO로 재귀를 배웠습니다. LISP를 사용할 수 있습니다. 이러한 언어에서는 재귀가 자연 스럽습니다. 그렇지 않으면 수학 스위트와 시리즈의 연구에 비유 할 수 있습니다. 예를 들어 u (n + 1) = f (u (n)) 또는 여러 변수가있는 복잡한 시리즈와 다중 의존성, 예를 들어 u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...

그래서 제 제안은 단순한 (표준 표현에서) 표준 재귀 "문제"를 찾아서 선택한 언어로 시도해보십시오. 실습은 이러한 "문제"를 생각하고 읽고 표현하는 방법을 배우는 데 도움이됩니다. 이러한 문제 중 일부는 반복을 통해 표현 될 수 있지만 재귀는보다 우아한 방법으로 해결할 수 있습니다. 그중 하나는 계승 수의 계산입니다.

그래픽 "문제"를보다 쉽게 ​​볼 수 있습니다. Koch의 플레이크, Fibonacci, 드래곤 커브 및 일반적으로 프랙탈을 찾으십시오. 또한 빠른 정렬 알고리즘도 살펴보십시오.

몇 가지 프로그램 (무한 루프, 무한 자원의 임시 사용) 및 잘못된 처리 조건 (예상치 못한 결과를 얻기 위해)을 충돌해야 모든 문제를 해결할 수 있습니다. 그리고 당신이 그것을 얻을 때에도 당신은 여전히 ​​덜 자주 그 실수를 할 것입니다.


2

컴퓨터 프로그램의 구조와 해석

그것은이다 , 학습뿐만 아니라 재귀하지만, 여러 대학에서 일반적으로 프로그래밍에 사용되는 책. 이 책은 기초 지식 중 하나이며, 더 많은 경험을할수록 더 많은 정보를 얻을 수 있습니다.


0

많이 나는 등 SICP괴델, 에셔, 바흐 : 영원한 황금 브레이드 , Touretzky의 LISP : 젠틀 소개 기호 계산에이 또한 재귀 도입에 좋은 일을한다.

기본 개념은 다음과 같습니다. 첫째, 재귀 함수가 완료된 시점을 알아야 결과를 반환 할 수 있습니다. 그런 다음 완료되지 않은 사례를 처리하고 재발 가능한 것으로 줄이십시오. 전통적인 factorial (N) 예제의 경우 N <= 1 일 때 완료되었으며 완료되지 않은 경우 N * factorial (N-1)입니다.

훨씬 못생긴 예를 들어, Ackermann의 함수 A (m, n)이 있습니다.

A(0,n) = n+1.                                   This is the terminal case.
A(m,0) = A(m-1,1) if m > 0.                     This is a simple recursion.
A(m,n) = A(m-1, A(m, n-1)) if m > 0 and n > 0.  This one is ugly.

0

OCaml 또는 Haskell과 같은 ML 스타일의 기능적 언어를 가지고 놀 것을 제안합니다. 패턴 일치 구문 은 Scheme 및 Statement 보다 훨씬 더 나은 비교적 복잡한 재귀 함수조차 이해하는 데 실제로 도움이 된다는 것을 알았 습니다 . (저는 Haskell과 Scheme을 동시에 배웠습니다.)ifcond

다음은 대비에 대한 간단한 예입니다.

(define (fib n)
   (cond [(= n 0) 0]
         [(= n 1) 1]
         [else (+ (fib (- n 1)) (fib (- n 2)))]))

패턴 일치와 함께 :

fib 0 = 0
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

이 예제는 실제로 차이 정의를 수행하지 않습니다. 함수 버전에 문제가 없었습니다. 두 옵션이 어떻게 보이는지 설명하기 위해서입니다. 리스트와 트리 같은 것들을 사용하여 훨씬 더 복잡한 함수에 도달하면, 그 차이는 훨씬 더 뚜렷해진다.

나는 매우 좋은 구문을 가진 간단한 언어이기 때문에 특히 Haskell을 추천합니다. 또한 corecursion 과 같은 고급 아이디어로 작업하기가 훨씬 쉽습니다 .

fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
fib n = fibs !! n

(하스켈로 조금 연주하기 전까지는 위의 코드를 이해하지 못하지만 기본적으로 마술이라고 확신합니다.) Scheme에서 스트림으로 동일하게 할 수는 있지만 하스켈에서는 훨씬 더 자연 스럽습니다.


0

인쇄본이 인쇄되어 있지 않지만 Richard Lorentz의 "재귀 알고리즘"은 재귀에 관한 것입니다. 재귀의 기본 사항과 특정 재귀 알고리즘을 다룹니다.

예제는 Pascal에 있지만 언어 선택이 귀찮을 정도로 크지는 않습니다.

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