재귀 함수가 반복 / 루프를 가질 수 있습니까?


12

나는 재귀 함수에 대해 연구 해 왔으며 분명히 함수를 호출하고 반복 / 루프를 사용하지 않는 함수입니다 (그렇지 않으면 재귀 함수가 아닙니다).

그러나 예제를 위해 웹을 서핑하는 동안 (여덟 여왕 재귀 문제)이 기능을 발견했습니다.

private boolean placeQueen(int rows, int queens, int n) {
    boolean result = false;
    if (row < n) {
        while ((queens[row] < n - 1) && !result) {
            queens[row]++;
            if (verify(row,queens,n)) {
                ok = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

while포함 된 루프.

... 그래서 나는 지금 조금 길을 잃었다. 루프를 사용할 수 있습니까?


5
컴파일합니까? 예. 왜 물어?
Thomas Eding

6
재귀 의 전체 정의는 어떤 시점에서 함수가 반환되기 전에 자체 실행의 일부로 다시 호출 될 수 있다는 것입니다 (자체 또는 호출하는 다른 함수에 의해 다시 호출되는지 여부). 그 정의에 관한 어떤 것도 루핑 가능성을 배제하지 않습니다.
cHao

cHao의 의견에 대한 부록으로 재귀 함수는 더 쉬운 버전의 자체 에서 다시 호출 됩니다 (그렇지 않으면 영원히 반복됩니다). 인용문을 작성하려면 ( 일반 영어에서 재귀 란 무엇입니까? ) : "재귀 프로그래밍은 자체 버전을보다 쉽게 ​​해결하기 위해 문제를 점진적으로 줄이는 프로세스입니다." 이 경우 가장 어려운 버전 placeQueen은 "place 8 queens"이고 더 쉬운 버전 placeQueen은 "place 7 queens"(6 번 등)
Brian

오메가가 작동하는 모든 것을 사용할 수 있습니다. 학교에 있거나 과제가 그렇게 말하지 않는 한 소프트웨어 사양에 따라 사용할 프로그래밍 스타일을 지정하는 경우는 거의 없습니다.
Apoorv Khurasia

@ThomasEding : 물론 컴파일하고 작동합니다. 그러나 나는 단지 지금 엔지니어링을 공부하고 있습니다.이 시점에서 나에게 중요한 것은 프로그래머가 그것을 사용하는 방식이 아니라 엄격한 개념 / 정의입니다. 그래서 나는 가지고있는 개념이 올바른지 묻습니다 (그렇지 않은 것 같습니다).
Omega

답변:


41

재귀를 잘못 이해했습니다. 반복을 대체하는 데 사용할 수는 있지만 재귀 함수가 내부에 반복을 포함하지 않아야 할 필요는 없습니다.

재귀로 간주되는 함수의 유일한 요구 사항은 직접 또는 간접적으로 호출되는 코드 경로의 존재입니다. 모든 올바른 재귀 함수는 일종의 조건을 가지므로 영원히 "재귀"하지 못합니다.

재귀 함수는 역 추적을 사용한 재귀 검색 구조를 설명하는 데 이상적입니다. 종료 조건을 확인하는 것으로 시작하여 row < n재귀 수준에서 검색 결정을 내립니다 (즉, 퀸 번호에 대한 가능한 위치 선택 row). 각 반복 후에 함수가 지금까지 찾은 구성을 기반으로 재귀 호출이 작성됩니다. 결국, 수준이 깊은 재귀 호출에 row도달하면 "하단"됩니다 .nn


1
"올바른"재귀 함수의 경우 +1 조건부, 잘못된 함수가 많이 있습니다.
Jimmy Hoffa

6
+1 "반복"영원히`Turtle () {Turtle ();}
Mr.Mindor

1
@ Mr.Mindor 나는 "거북이 끝까지 내려왔다"인용구를 좋아한다 :)
dasblinkenlight

그것은 나를 미소 짓게했다 :-)
Martijn Verburg

2
"올바른 모든 재귀 함수는 조건이있어"재귀 "할 수 없습니다." 엄격하지 않은 평가에서는 사실이 아닙니다.
Pubby

12

재귀 함수의 일반적인 구조는 다음과 같습니다.

myRecursiveFunction(inputValue)
begin
   if evaluateBaseCaseCondition(inputValue)=true then
       return baseCaseValue;
   else
       /*
       Recursive processing
       */
       recursiveResult = myRecursiveFunction(nextRecursiveValue); //nextRecursiveValue could be as simple as inputValue-1
       return recursiveResult;
   end if
end

내가 표시 한 텍스트는 /*recursive processing*/무엇이든 가능합니다. 그것은 수있는 해결되는 문제는 그것을 필요로하고,에 재귀 호출도 포함 할 수 있다면, 루프를 포함한다 myRecursiveFunction.


1
재귀 호출이 하나만 있음을 암시하고 재귀 호출 자체가 루프 내부에있는 경우 (예 : B- 트리 통과)를 거의 배제하기 때문에 오해의 소지가 있습니다.
피터 테일러

@ PeterTaylor : 예, 간단하게 유지하려고했습니다.
FrustratedWithFormsDesigner

또는 일반 이진 트리를 탐색하는 것과 같이 루프가없는 여러 호출조차도, 각 노드에는 자식이 2 개 있기 때문에 호출이 2 개 있습니다.
Izkata

6

재귀 함수에서 루프를 반드시 사용할 수 있습니다. 함수를 재귀 적으로 만드는 것은 함수가 실행 경로의 특정 지점에서 자신을 호출한다는 것입니다. 그러나 함수가 반환 할 수없는 무한 재귀 호출을 방지하기위한 조건이 있어야합니다.


1

재귀 호출 및 루프는 반복 계산을 구현하는 두 가지 방법 / 구성입니다.

while꼬리 재귀 호출에 루프 대응 (예 참조 여기에 ), 즉 두 개의 반복 사이의 중간 결과를 저장하지 않아도되는 반복 (당신이 다음 사이클을 입력 할 때 한주기의 모든 결과는 준비). 나중에 다시 사용할 수있는 중간 결과를 저장해야하는 경우 while스택과 함께 루프 를 사용하거나 ( 여기 참조 ) 꼬리가 아닌 재귀 적 (즉, 임의의) 재귀 호출을 사용할 수 있습니다.

많은 언어에서 두 가지 메커니즘을 모두 사용할 수 있으며 자신에게 더 적합한 메커니즘을 선택하고 코드에서 함께 사용할 수도 있습니다. C, C ++, Java 등과 같은 명령형 언어에서는 일반적으로 스택이 필요하지 않을 때 whileor for루프를 사용하고 스택이 필요할 때 재귀 호출을 사용합니다 (암시 적으로 런타임 스택을 사용함). Haskell (기능적 언어)은 반복 제어 구조를 제공하지 않으므로 반복 호출 만 사용하여 반복을 수행 할 수 있습니다.

귀하의 예에서 (내 의견 참조) :

// queens should have type int [] , not int.
private boolean placeQueen(int row, int [] queens, int n)
{
    boolean result = false;
    if (row < n)
    {
        // Iterate with queens[row] = 1 to n - 1.
        // After each iteration, you either have a result
        // in queens, or you have to try the next column for
        // the current row: no intermediate result.
        while ((queens[row] < n - 1) && !result)
        {
            queens[row]++;
            if (verify(row,queens,n))
            {
                // I think you have 'result' here, not 'ok'.
                // This is another loop (iterate on row).
                // The loop is implemented as a recursive call
                // and the previous values of row are stored on
                // the stack so that we can resume with the previous
                // value if the current attempt finds no solution.
                result = placeQueen(row + 1,queens,n);
            }
        }
        if (!result) {
            queens[row] = -1;
        }
    }else{
        result = true;
    }
    return result;
}

1

재귀와 반복 또는 루핑 사이에 관계가 있다고 생각하는 것이 옳습니다. 재귀 알고리즘은 종종 테일 콜 최적화를 사용하여 반복적 솔루션으로 수동 또는 자동으로 변환됩니다.

퀸 8 개에서 재귀 부분은 역 추적에 필요한 데이터 저장과 관련이 있습니다. 재귀를 생각할 때 스택에 무엇이 푸시되는지 생각하는 것이 중요합니다. 스택에는 알고리즘에서 중요한 역할을하는 전달 매개 변수 및 로컬 변수가 포함되거나 반환 주소와 관련이없는 것으로 보이는 경우 나 사용 된 퀸 (Queen) 수와 함께 전달 된 값이 포함될 수 있습니다. 알고리즘에 의해 변경되지 않습니다.

여덟 여왕에서 일어나는 행동은 본질적으로 우리는 처음 몇 열에서 몇 개의 여왕에 대해 부분적인 해결책을 제공한다는 것입니다.이 열에서 우리는 재귀 적으로 통과하도록 현재 열에서 유효한 현재 선택을 반복적으로 결정합니다. 나머지 열. 로컬로, 8 명의 퀸이 시도하는 행을 추적하고 역 추적이 발생하면 나머지 행을 단계별로 진행하거나 다른 행이 발견되지 않으면 다시 리턴하여 준비를 계속할 수 있습니다.


0

"문제의 더 작은 버전 생성"부분에는 루프가있을 수 있습니다. 메소드가 더 작은 버전의 문제점을 매개 변수로 전달하여 호출하는 한, 메소드는 재귀 적입니다. 물론 종료 조건은 가능한 가장 작은 버전의 문제점이 해결되고 메소드가 값을 리턴 할 때 스택 오버 플로우 조건을 피하기 위해 제공되어야합니다.

귀하의 질문에 대한 방법은 재귀 적입니다.


0

재귀는 기본적으로 함수를 다시 호출하며 재귀의 주요 장점은 메모리를 절약한다는 것입니다. 재귀에는 루프가있을 수 있으며 다른 작업을 수행하는 데 사용됩니다.


달라지다. 많은 알고리즘은 재귀 적이거나 반복적 일 수 있으며 스택에서 푸시해야하는 리턴 주소, 매개 변수 및 로컬 변수를 계산할 경우 재귀 솔루션은 종종 메모리를 훨씬 더 많이 사용합니다. 일부 언어는 꼬리 재귀 또는 꼬리 호출 최적화를 감지하고 최적화하는 데 도움이되지만 때로는 언어 또는 코드에 따라 다릅니다.
DeveloperDon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.