재귀함수 관련 질문 (하노이의 탑 등등)

조회수 1259회

제가 하노이 탑을 구현하려고 혼자 공부를 해 보았는데 도저히 안 되겠어서

구글링을 통하여 코드를 보았습니다.

네. 코드를 보아서 무슨 말인지 몰라 디버깅을 해 보았습니다.

근데 디버깅을 해도 정말 이해가 쉽지 않아서

유튜브 관련 영상을 찾아 보다가 좋은 강의를 보았습니다.

근데 그 강의에선 하노이의 탑의 규칙을 식으로 도출하여 바로 재귀함수를 구현하더군요.

제가 궁금해 하는 건 이렇게 식을 도출해서 바로 재귀함수를 구현해도 되나는 겁니다. 이미지

위 이미지를 보시면 왼쪽이 식, 오른쪽이 코드입니다.

저는 옆 식을 보지 않고 바로 오른쪽 코드를 보고 디버깅을 했습니다.

별반 다를 거 없이 제 머리로는 포기를 했습니다.

어떻게 이런 로직이 나온 거고 이걸 어떻게 생각해 냈는지부터 다시 알아 보려 강의를 보던 중

강의자가 왼쪽 식을 오른쪽 코드에 비유하며 설명을 하는데 1번 식이 else문의 첫번 째 재귀함수 코드이고, 2번 식이 else의 move코드이다 ... 라고 설명을 하더군요.

듣고나서 저는 로직을 돌려 보지 않고도 바로 재귀함수를 구현할 수 있구나라고 생각했습니다.

제가 생각한 것이 맞나요?

로직을 돌려 보지 않고도 바로 재귀함수를 구현할 수 있는 건가요?

저는 재귀함수가 꼭 머리로 전부 흐름을 파악하고 구현하는 건줄 알았거든요.

(꼭 재귀함수가 아니더라도 반복만 해도 전부 디버깅 하고 코드를 구현합니다.)

재귀함수는 다른 알고리즘보다 조금 복잡하니깐 그렇게 생각한 거 같아요

좀 설명이 장황했습니다. 알아 들으시는 분이 계실지는 모르겠지만

많은 조언 부탁 드립니다.

  • (•́ ✖ •̀)
    알 수 없는 사용자

3 답변

  • 질문이 문제를 재귀함수로 구현이 가능한가 불가한가에 대한 판단 여부를 어떻게 결정할지에 대한 고민인가요?

    단계가 명확히 정해져 있거나 논리(로직)가 명확하다면, 재귀함수를 사용 시 쉽게 구현이 가능하다는건 어렵지 않게 도출 할 수 있습니다.(다수의 문제는 재귀를 사용하지 않는 반복문으로도 충분히 구현이 가능합니다.)

    다만 이 경우는 단계별로 작업할 내용이 명확하게 제시되어 있기 때문에 가능했던 것으로 보이며 직접 알고리즘을 고려하신다면 로직을 따라가지 않는 한 단계를 명확히 정의하기 어려울 것입니다.

    우측의 식이 쉽게 도출됬던 것도 윗 답변분도 말하셨듯이 강사가 "로직을 돌려보지 않고 바로 도출"했던게 아니라 논리를 이해하고 단계를 명확히 정의했기 때문입니다. 또한 실제 사용 시 생각지 못한 문제가 발생할 수 있으니 기본적인 로직에 대한 이해를 갖추고 도구(코딩)을 사용하는걸 권해드립니다.

    감사합니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 만약 정말 이해하기 어려운 로직도 이해를 어쩔 수 없이 해야 하는 건가요? 그런 노하우를 얻으려면 노력밖에는 답이 없는 거겠군요 ... ㅠㅠ 알 수 없는 사용자 2020.7.19 23:09
  • 재귀함수를 구현하는 방법은 우선 구현을 생각하지 않고, 문제를 해결하는 과정에서 현재의 문제와 닮은 꼴의 동일한 문제를 찾아내는 것입니다. 그리고 가장 단순한 문제에 대한 해답도 갖고 있어야 합니다. 이때 요령은 해당 문제가 이미 해결된 상태라고 가정하는 겁니다.

    보통 이런 접근 방식을 수학적 귀납법이라 부르고 (Inductive Reasoning) 수학적 귀납법은 다음의 과정으로 이루어집니다.

    1. 기반조건 p(0)이 참이다.
    2. p(k) -> p(k+1) 가 참이다
    3. p(n)이 참이다

    말하자면 재귀함수는 1과 2를 통하여 3을 구현하는 함수입니다.

    예를 들어서 n 팩토리얼 (1 * 2 * 3 * ... * n ) 을 구하는 재귀함수를 작성한다고 해봅시다.

    n 팩토리얼의 정의는 1 * 2 * 3 * ... * n 입니다.

    이는 (1 * 2 * ... * n - 1) * n 이고, (n-1 팩토리얼) * n 입니다.

    솔루션을 구하는 과정에서 동일한부분 문제를 발견하였습니다.

    그리고 가장 단순한 문제인 1 팩토리얼의 값은 1입니다.

    함수를 f라고 하면 다음과 같이 쓸 수 있습니다.

    f(n):
      if (n == 1) return 1
      return f(n-1) * n
    

    하노이탑 문제로 넘어가 봅시다.

    하노이탑은 쌓여있는 원판을 다른 기둥으로 옮기는 문제입니다.

    a, b, c 세 기둥이 있고 원판은 편의상 아래처럼 배열로 표기하겠습니다.

    [1, 2, 3, 4], [], [] 크기 1, 2, 3, 4의 원판이 첫 번째 기둥에 있는 예를 들겠습니다.

    우리는 이미 n개의 하노이탑을 a에서 c로 옮기는 방법을 안다고 가정 합니다. 그건 f(n, a, b, c)입니다.

    자 그러면 위 문제를 [1, 2, 3], [] [] 만 놓고 생각하면 기둥 b, 기둥 c로 옮길 수 있겠죠? 즉,

    [1, 2, 3, 4], [], [] -> [4] [1, 2, 3] [] : f(3, a, c, b)

    [1, 2, 3, 4], [], [] -> [4] [] [1, 2, 3] : f(3, a, b, c)

    근데 c로 옮기면 안 됩니다. c에는 4가 깔려야 하거든요.

    그래서 [1, 2, 3]을 우선 기둥 b로 옮긴다음 4를 c로 옮겨야 합니다.

    [4] [1, 2, 3] [] -> [] [1, 2, 3] [4] : 1 (이 값은 f(1, a, b, c) 와 같습니다 )

    [] [1, 2, 3] [4] -> [] [] [1,2,3,4] : 1 + f(3, b, a, c)

    즉 f(4, a, b, c) = f(3, a, c, b) + 1 + f(3, b, a, c) 입니다.

    질문자분이 의문을 품는 부분은 이렇게 구한 식입니다.

    위 예시에서는 4를 예시로 들었지만 n으로 표현하면

    f(n, a, b, c) = f(n-1, a, c, b) + 1 + f(n-1, b, a, c) 입니다.

    그럼 기반조건을 찾아봅시다. f(1, a, b, c) = 1 입니다. (명백하죠?) 이제 이걸 코드로 써보면 다음과 같이 됩니다.

    f(n, a, b, c):
      if (n == 1) return 1
      return (
        f(n-1, a, c ,b) +
        1 +
        f(n-1, b, a, c)
      )
    
    • (•́ ✖ •̀)
      알 수 없는 사용자
  • 재귀 함수는 하고자 하는 동작의 재귀적인 특성을 파악한 후에 그것을 함수로 만들면 됩니다. 일반적인 함수들은 코드의 흐름을 위에서 아래로, 왼쪽에서 오른쪽으로 쫓아가면서 분석하는 경우가 대부분인데요. 유일하게 재귀함수는 그 흐름을 쫓을 필요도 없고, 쫓아가면서 생각할 경우 그 단계가 깊을수록 전혀 이해가 안가버리는 경우가 대부분입니다.

    재귀 함수는 재귀적인 동작을 하는 것들, 예를 들어, 피보나치 수열, 팩토리얼, 하노이 타워, 트리의 순회 등에서 주로 사용됩니다.

    보통 나좀 짠다고 생각하는 분들도 재귀함수를 처음에 잘 이해 못하는 경우가 있는데 아마도 기존의 코딩 고정관념을 가지고 이해하려고 해서 그런거 같아요.

    질문하신대로 식을 도출해서 그 식대로 함수를 짜고, 그 함수가 몇몇 테스트에서 잘 동작한다면 흐름을 구지 생각안하셔도 되요. 흐름을 처음 학습 단계에서 한번정도 검토해 보는 것은 좋지만 2번 이상 검토할 필요는 절대 없습니다.

    재귀 함수는 함수 내에서 자기 자신을 쓰면 재귀 함수이지만, 실용적인 재귀 함수가 되기 위해서는 2가지 조건이 필요합니다. 하나는 재귀 함수로부터 탈출할 수 있는 탈출 조건이 반드시 필요하고요. 두번째는 재귀 함수 내부에서 다시 자기 자신을 호출할 때, 호출시 인수가 함수의 매개변수와 달라야 합니다. 그래야만 언젠가 탈출 조건이 성립될 수 있습니다. 이 두가지가 사용할 수 있는 재귀 함수의 특징이고, 어떤 재귀함수든 다들 이 두가지를 만족할 거에요.

    재귀함수가 이해되기 전까지는 "이게 먼소리지?" 막 이럴수 있거든요. 그런데 이해 되고 나신 후에는 "별것도 아니네." 이런식으로 마음 편해지실 거에요.

    너무 코드의 흐름 쫓아가면서 분석하지 마시고, 고정관념 버리시고, 인터넷 글이나 책에서 설명하는 대로 식을 먼저 세우시고 그걸 그대로 코드로 만들면 됩니다. 재귀적인 식을 만들 수 있다면, 그것을 그대로 코드로 만들수 있다는 것도 재귀 함수의 장점입니다.

    재귀함수는 잘 사용하면 프로그램의 속도를 높일수도 있고요. 잘못 사용하면 반대로 속도가 느려지기도 합니다. 또한, 재귀함수를 모르면 절대 해결할 수 없는 하노이 타워나 트리의 순회 같은 것들도 있어서 익숙해지면 좋습니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)