재귀를 사용하여 문제를 해결할 수 있는지 여부를 결정하기 위해 고려해야 할 사항은 무엇입니까?


10

때로는 인터뷰에서 재귀를 사용하여 문제를 해결하기 위해 (예 : 1무한 정밀도 정수 추가 ) 또는 문제 자체가 재귀를 사용하기에 적합한 것으로 나타날 때. 때로는 문제 해결을 위해 재귀를 많이 사용하기 때문일 수 있으므로 많은 생각없이 재귀를 사용하여 문제를 해결합니다.

그러나 재귀를 사용하여 문제를 해결하는 것이 적합한 지 결정하기 전에 고려해야 할 사항은 무엇입니까?


내가 가진 생각 :

매번 절반으로 줄어든 데이터에 대해 재귀를 사용하는 경우 16GB RAM 또는 8TB 하드 드라이브에 들어갈 수있는 모든 데이터는 42 레벨 깊이의 재귀로 처리 할 수 ​​있으므로 재귀를 사용하는 데 아무런 문제가없는 것 같습니다. (따라서 스택 오버플로가 없습니다 (일부 환경에서는 스택이 42 레벨 이상인 4000 레벨 깊을 수 있지만 동시에 각 호출 스택이 더 많은 메모리를 차지하는 로컬 변수 수에 따라 달라집니다) 로컬 변수가 많고 스택 오버플로를 결정하는 메모리 크기 (레벨이 아닌))).

순수한 재귀를 사용하여 피보나치 수를 계산하는 경우 중간 결과를 캐시하지 않으면 시간 복잡성에 대해 걱정해야합니다.

그리고 1무한 정밀도 정수를 추가 하는 것은 어떻습니까? 아마도 3000 자리 길이 또는 4000 자리 길이의 숫자로 작업하여 스택 오버플로를 일으킬 수 있습니까? 나는 그것을 생각하지 않았지만 대답은 아니요, 재귀를 사용해서는 안되지만 평범한 루프를 사용해야합니다. 어떤 응용 프로그램에서는 숫자가 실제로 4000 자릿수 여야하기 때문에 일부는 확인해야합니다. 숫자가 소수인지 여부와 같은 숫자의 속성.

궁극적 인 질문은 재귀를 사용하여 문제를 해결하기 전에 고려해야 할 사항은 무엇입니까?


7
실제로는 다소 간단합니다. "약간 작은 문제에 대한 해결책을 알고 있다고 가정 할 수 있다면 해결책이 사소한 것입니까?"
Kilian Foth

그러나 피보나치 수 또는 1무한 정밀도 정수를 추가하는 것은 어떻습니까? 당신은 말할 수 있습니다, 예, 그들은 더 작은 문제로 감소하지만 순수한 재귀는 적합하지 않습니다
nonopolarity

도움이 될 것입니다.- stackoverflow.com
questions

답변:


15

한 가지 고려 사항은 알고리즘이 추상 솔루션인지 실용적인 실행 솔루션인지 여부입니다. 전자의 경우, 찾고자하는 속성은 정확하고 대상 사용자에 대한 이해의 용이성입니다 1 . 후자의 경우 성능도 문제입니다. 이러한 고려 사항은 선택에 영향을 줄 수 있습니다.

두 번째 고려 사항 (실제 솔루션의 경우)은 사용중인 프로그래밍 언어 (또는 더 엄격하게 구현)가 테일 콜 제거를 수행하는지 여부입니다. 테일 콜 제거가 없으면 재귀는 반복보다 느리게 진행되며 깊은 재귀는 스택 오버플로 문제를 일으킬 수 있습니다.

(올바른) 재귀 솔루션은 동등한 비 재귀 솔루션으로 변환 될 수 있으므로 두 접근 방식 중에서 반드시 어려운 선택을 할 필요는 없습니다.

마지막으로, 때때로 재귀 적 및 비재 귀적 포 뮬레이션 사이의 선택은 알고리즘에 대한 (공식적인 의미에서) 속성을 증명할 필요성에 의해 동기가 부여됩니다. 재귀 제제는 유도에 의한 증명을 더 직접적으로 허용합니다.


1-여기 에는 대상 독자인지 여부와 같은 고려 사항 이 포함 됩니다. 여기 에는 실용적인 코드를 읽는 프로그래머가 포함될 수 있습니다. 한 스타일의 솔루션이 다른 스타일보다 "보다 자연스러운"것으로 간주됩니다. "자연"이라는 개념은 프로그래밍이나 알고리즘을 배운 방법에 따라 사람마다 다릅니다. (저는 "자연 스러움"을 객관적인 용어로 정의하기 위해 재귀를 사용하거나 사용하지 않기로 결정 하는 기본 기준 으로 제안하는 사람에게 도전 합니다.


2
일부 문제는 재귀를 사용하여 더 자연스럽게 표현됩니다. 예를 들어, 나무 순회.
Frank Hileman

그 요점을 해결하기 위해 내 대답을 업데이트했습니다.
Stephen C

1
예를 들어, "자연성"과 관련하여 : 재귀없는 트리 순회는 더 큰 범용 코드를 생성하는 경향이 있습니다. 예를 들어 리프와 컴포지트 노드에 대해 다른 동작으로 트리를 탐색하기 위해 다형성 호출을 사용하는 것을 고려하십시오. 재귀 없이는 불가능합니다.
Frank Hileman

1) "천연"을 정의하는 데 어려움을 겪었습니까? 2) 스택 데이터 구조를 사용하여 재귀를 시뮬레이션 할 수 있으므로 트리 탐색도 그런 방식으로 구현할 수 있습니다. 가장 효율적인 방법은 아니지만 ... 가장 읽기 쉬운 코드를 제공하지는 않지만 분명히 가능하고 실용적입니다.
Stephen C

그건 그렇고, 내가 배운 첫 번째 프로그래밍 언어 (FORTRAN 4)는 재귀를 전혀 지원하지 않았습니다.
Stephen C

1

C / C ++ 프로그래머로서 제가 가장 고려해야 할 것은 성능입니다. 내 결정 과정은 다음과 같습니다.

  1. 통화 스택의 최대 깊이는 얼마입니까? 너무 깊으면 재귀를 제거하십시오. 얕은 경우 2로 이동하십시오.

  2. 이 기능이 프로그램의 병목 현상 일 가능성이 있습니까? 그렇다면 3으로 가십시오. 그렇지 않으면 재귀를 유지하십시오. 확실하지 않으면 프로파일 러를 실행하십시오.

  3. 재귀 함수 호출에 소요되는 CPU 시간의 일부는 얼마입니까? 함수 호출이 나머지 함수 본문보다 훨씬 적은 시간이 걸리면 재귀를 사용하는 것이 좋습니다.


0

그러나 재귀를 사용하여 문제를 해결하는 것이 적합한 지 결정하기 전에 고려해야 할 사항은 무엇입니까?

Scheme에서 함수를 작성할 때 너무 많이 생각하지 않고 꼬리 재귀 함수를 작성하는 것이 당연하다는 것을 알았습니다.

C ++로 함수를 작성할 때 재귀 함수를 사용하기 전에 토론을합니다. 내가 묻는 질문은 다음과 같습니다.

  • 반복 알고리즘을 사용하여 계산을 수행 할 수 있습니까? 그렇다면 반복적 인 접근 방식을 사용하십시오.

  • 모델의 크기에 따라 재귀 수준이 커질 수 있습니까? 최근에는 모델 크기로 인해 재귀 깊이가 거의 13000으로 증가한 경우가 발생했습니다. 반복 알고리즘 사후 저장을 사용하도록 함수를 변환해야했습니다.

    이러한 이유로 재귀 함수를 사용하여 트리 탐색 알고리즘을 작성하지 않는 것이 좋습니다. 런타임 환경에 트리가 너무 깊어지면 알 수 없습니다.

  • 반복 알고리즘을 사용하여 함수가 너무 복잡해 질 수 있습니까? 그렇다면 재귀 함수를 사용하십시오. qsort반복적 인 접근 방식을 사용하여 글 을 쓰지 않았지만 재귀 함수를 사용하는 것이 더 자연스러운 느낌이 듭니다.


0

피보나치 수의 경우, 순진한 "재귀"는 완전히 바보입니다. 동일한 하위 문제가 반복해서 해결되기 때문입니다.

실제로 재귀가 매우 효율적인 피보나치 수의 사소한 변형이 있습니다. 숫자 n ≥ 1이 주어지면 fib (n)과 fib (n-1)을 모두 계산하십시오. 따라서 두 가지 결과 를 반환하는 함수가 필요합니다 .이 함수를 fib2라고합니다.

구현은 매우 간단합니다.

function fib2 (n) -> (fibn, fibnm1) {
    if n ≤ 1 { return (1, 1) }
    let (fibn, fibnm1) = fib2 (n-1)
    return (fibn + fibnm1, fibn)
}

프로그램을 공용 언어로 작성할 수 있다고 생각하십니까? 그리고 당신 fib2은 숫자 쌍을 반환하고, fib2()의 인터페이스에 맞지 않습니다 fib(), 즉 숫자가 주어지면 숫자를 반환합니다. fib(n)귀국하는 것 같지만 fib2(n)[0]구체적으로 말씀해주십시오
비극성
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.