본문 바로가기
Python

[Python] (recursion)재귀함수 이해해보기, 스택

by ybin.im 2024. 12. 11.

목차

1. 재귀함수란?

2. 스택 간단하게 알아보기

 

3. 재귀함수 예제

 -  팩토리얼, 피보나치수열, 하노이의 탑

 -  재귀함수 정의

 -  재귀함수 실행순서

 

4. 재귀문제를 푸는 가장 좋은 방법?


재귀함수란?

재귀(recursion는 어떠한 것을 정의할 때 자기 자신을 참조하는 것을 뜻한다

재귀함수란? 함수를 정의할 그 함수 자신을 포함해서 정의하는 것이다.

▶ 재귀함수의 특징
 - 재귀함수는 for문으로 대체할 수 있지만,
   반복적 구조를 깔끔하게 쓰기위해서 특정상황에 사용한다.

- 하지만 for문이 더 복잡도가 낮고,  효율적일때가 많다.

 - 재귀호출을 무한 반복하므로 종료조건이 필요하다.

 - 선언적인 프로그램이다. 
   코드가 간결하고 이해하기 쉬울 때 사용.

스택

재귀함수를 이해하려면 스택을 알아야한다.

"나중에 들어온 데이터가 먼저 나간다."

LIFO(Last In Last out)

마지막에 호출된 것이 먼저 종료되고, 이후 가장 위에있는 호출, 또 그 다음으로 위에있는 호출이 수행되며 넘어간다.

 

스택은 스택할 수 있는 최대 경계를 넘어가면 스택 오버플로우(Stack Overflow)가 일어난다.


재귀함수 예제

재귀함수 예제로는 팩토리얼, 피보나치수열,  하노이의 탑이 있다.

 

재귀함수를 정의하는 것과,

실행순서를 보면서 재귀함수가 어떻게 돌아가는지 알아보자..


▼팩토리얼

팩토리얼은 하나의 수에서 그 수와 같거나 작은 모든 정수의 곱이다.

 ex) 5! = 5 * 4 * 3 * 2 * 1

 

팩토리얼을 재귀함수로 구현하면 다음과 같다.

 

▼재귀함수 정의

1. else:문
  들어온 값과 바로아래의 값을 곱을 선언한다.
  ex) 5 * 4, 4 * 3
2. if: 문
   1이 들어왔을땐 1을 반환하며 종료한다.

 

▼실행 순서

num = 5
가 처음 들어가고 factorial(num-1)로 인해서 num은 4 -> 3 -> 2-> 1로 점점 스택이 쌓인다.

factorial(1)일 때, 1 리턴되며
factorial(2)상황으로 넘어간다.
그러면 2 * 1이 리턴,

결국 factorial(5) 처음 호출한 스택으로 넘어와서
factorial(5) = 5 * (4 * ( 3 * ( 2 * 1) ) )
위와 같은 형태가 된다.

▼ 피보나치 수열

피보나치 수열은 다음과 같이 정의된다.

f(0) = 0 , f(1) = 1

f(n) = f(n-1) + f(n-2)   (n >= 2)

 

피보나치수열은 정의가 나와있는대로 그대로 정의해주면 된다.

 

▼재귀함수 정의

1. if num == 0:
         return 0
   f(0) = 0 을 표현한 것이다.
2. elif num == 1:
        return 1
   f(1) = 1 을 표현한 것이다.
3. elif num >= 2:
        num = fibo(num-1) + fibo(num-2)
        return num
   n >= 2 의 조건
   f(n) = f(n-1) + f(n-2) 를 표현하였고
   return num으로 값을 리턴시키며 마무리되었다.

 

▼실행 순서

num = 0 -> 0
num = 1 -> 1

num = 2, fibo(2)
num = fibo(1) + fibo(0) = 1 + 0

num = 3 , fibo(3)
num = fibo(2) + fibo(1) = 1 + 1

num = 4, fibo(4)
num = fibo(3) + fibo(2) = 2 + 1

num = 5, fibo(5)
num = fibo(4) + fibo(3) = 3 + 2

num = 6, fibo(6)
num = fibo(5) + fibo(4) = 5 + 3

결과는 다음과 같다.
                        ▼fibo(2)               ▼fibo(3)   ▼fobo(4)   ▼fibo(5)    ▼fibo(6)
fibo(6) = ( ( ( ( ( fibo(1) + fibo(0) ) + fibo(1) ) + fibo(2)  ) + fibo(3) ) + fibo(4) )

 


▼ 하노이의 탑

하노이의 탑 (출처 : 위키피디아)

하노이의 탑은 한 곳에 모여있는 원판을 반대쪽 기둥으로 옮기는 것이다.

큰 원판은 작은 원판위로 올라가면 안된다.

 

▼ 직접 하노이의 탑을 해봤을 때, 알게되는 규칙이 있다.

총 옮기는 원판의 개수가 n이라고 하면,

가장 큰 원판을 옮기기 전에,
마지막 기둥과 연결되는 기둥에 n-1개의 탑이 세워진다는 것.

그래서 우리는 n-1개의 원판을 연결 기둥에 모두 옮기고,
마지막 큰 원판 1개를 반대쪽으로 옮기면 된다.

n-1개의 원판들도 똑같이
n-2의 원판을 n-1개의 탑을 세우려는 곳과 다른 기둥에 세워야 옮길 수 있다.
이것이 반복되어 1개의 원판일때까지 올라간다.

 

 

▼재귀함수 정의

num = 옮겨야할 원판개수
start = 1번기둥    bridge = 2번기둥     end = 3번기둥

1. if num == 1: 
 원판이 하나남았을 때 시작지점에서 끝지점으로 원판을 옮긴다.

2. else:
2-1. hanoi(num-1, start, bridge, end)
       num-1개의 탑을 전부 브릿지로 옮긴다.
      
2-2. hanoi(num-1, bridge, end, start)
       num-1개의 탑을 전부 브릿지에서 끝지점까지 옮긴다.

프린트문을 통해서 원판의 이동을 나타낸다.
      
이렇게 정의해주면 된다.

 

▼ 실행 순서

hanoi(5, 1, 3, 2)일 때,

hanoi(num-1, start, bridge, end) 첫번째 재귀문
뜻 : n-1개의 탑을 연결기둥으로 옮긴다.!

먼저 1개가 end로 이동한다.
- > 2개의 탑이 bridge에 세워짐
- > 3개의 탑이 end에 세워짐
- > 4개의 탑이 bridge에 세워짐
- > 가장 큰 원판이 end로 이동

hanoi(num-1, bridge, end, start) 두번째 재귀
뜻 : 4개의 탑이 쌓여져있는 bridge에서 end로 탑을 다 이동시킨다.!

먼저 맨 위의 한개가 start로 이동
- > 2개의 탑이 end로 이동
- > 3개의 탑이 start로 이동
- > 4개의 탑이 end로 이동하며 end기둥에 5개의 탑이 세워지게 된다. 


재귀문제를 푸는 가장 좋은 방법??

재귀함수를 잘 사용하는 가장 좋은 방법은,, 재귀적으로 생각하지 않는것이라고 한다..!

재귀함수를 잘 사용하려면 수많~은 재귀의 실행순서를 머릿속으로 큰그림을 그리려하면 안되고,
함수에서 중요한 요소들에 초점을 맞춰서 어떻게 구현할지 문제를 풀어나가라고 한다.

 

 

모두 나름의 방법으로 재귀함수를 잘 풀어내셨으면 좋겠습니다.........

다른 문제를 봐도 척척 풀어낼 수 있을지...