컴퓨터는 잘못이 없다..

[이것이코딩테스트다]5장_탐색 알고리즘 DFS/BFS(3)_DFS,BFS에 필요한 자료구조 - 재귀 함수 본문

공부/알고리즘(파이썬)

[이것이코딩테스트다]5장_탐색 알고리즘 DFS/BFS(3)_DFS,BFS에 필요한 자료구조 - 재귀 함수

도토리까꿍v 2021. 5. 24. 14:36
Contents 접기

#재귀 함수 

재귀 함수란 : 자기 자신을 다시 호출하는 함수를 의미한다.

 

#간단한 재귀 함수 예 

def recursive_function() :
    print('재귀 함수를 호출합니다.')
    #재귀함수 호출
    recursive_function()

#재귀함수 호출
recursive_function()

▲설명

이 코드를 실행하면 '재귀 함수를 호출합니다.' 라는 문자열을 무한히 출력한다. 여기서 정의한 recursive_functino()이 자기 자신을 계속해서 추가로 불러오기 때문이다. 물론 어느 정도 출력하다가 다음과 같은 오류 메시지를 출력하고 멈출 것이다.

RecursionError: maximum recursion depth exceeded while pickling an object

이 오류 메시지는 재귀이 최대 깊이를 초과했다는 내용이다. 보통 파이썬 인터프리터는 호출횟수 제한이 있는데 이 한계를 벗어났기 때문이다. 따라서 무한대로 재귀 호출을 진행할 수는 없다. 

따라서 재귀 함수를 문제풀이에서 사용할 때는 재귀함수가 언제 끝날지, 종료 조건을 꼭 명시해야한다. 

 

#재귀 함수의 종료 조건

재귀 함수를 문제풀이에서 사용할 때에는 재귀함수가 언제 끝날지, 종료 조건을 꼭 명시해야한다. 

 

┌재귀 함수 종료 조건을 사용한 재귀 함수 예제

def recursive_function(i) :
    #10번째 출력했을 때 종료되도록 종료 조건 명시
    if i==10 :
        return

    print(i,'번째 재귀 함수에서', i+1, '번째 재귀 함수를 호출합니다.')
    recursive_function(i+1)
    print(i,'번째 재귀 함수를 종료합니다.')


#재귀함수 호출
recursive_function(1)

'''
결과
1 번째 재귀 함수에서 2 번째 재귀 함수를 호출합니다.
2 번째 재귀 함수에서 3 번째 재귀 함수를 호출합니다.
3 번째 재귀 함수에서 4 번째 재귀 함수를 호출합니다.
4 번째 재귀 함수에서 5 번째 재귀 함수를 호출합니다.
5 번째 재귀 함수에서 6 번째 재귀 함수를 호출합니다.
6 번째 재귀 함수에서 7 번째 재귀 함수를 호출합니다.
7 번째 재귀 함수에서 8 번째 재귀 함수를 호출합니다.
8 번째 재귀 함수에서 9 번째 재귀 함수를 호출합니다.
9 번째 재귀 함수에서 10 번째 재귀 함수를 호출합니다.
9 번째 재귀 함수를 종료합니다.
8 번째 재귀 함수를 종료합니다.
7 번째 재귀 함수를 종료합니다.
6 번째 재귀 함수를 종료합니다.
5 번째 재귀 함수를 종료합니다.
4 번째 재귀 함수를 종료합니다.
3 번째 재귀 함수를 종료합니다.
2 번째 재귀 함수를 종료합니다.
1 번째 재귀 함수를 종료합니다.
'''

▲설명

위 코드의 출력결과를 보면 컴퓨터 내부에서 재귀 함수의 수행은 스택 자료구조를 이용한다는 것을 알 수 있다. 

함수를 계속 출력했을 때 가장 마지막에 호출한 함수가 먼저 수행을 끝내야 그 앞의 함수 호출이 종료되기 때문이다. 컴퓨터의 구조 측면에서 보자면 연속해서 호출되는 함수는 메인 메모리의 스택 공간에 적재되므로 재귀 함수는 스택 자료구조와 같다는 말은 틀린 말이 아니다. 즉, 재귀 함수는 내부적으로 스택 자료구조와 동일하다. 

 

🌟🌟🌟즉, 재귀 함수는 내부적으로 스택 자료구조와 동일하기 때문에 스택 자료구조를 활용해야 하는 상당수 알고리즘은 재귀 함수를 이용해서 간편하게 구현될 수 있다. DFS가 대표적인 예이다.

 

#팩토리얼 문제

팩토리얼을 1. 반복문으로 구현하고 2. 재귀적으로도 구현해보자.

#반복적으로 구현한 팩토리얼 예제
def factorial_iterative(n) :
    result=1
    #1부터 n까지의 수를 차례대로 곱하기
    for i in range(1,n+1) :
        result*=i
    return result

def factorial_recursive(n) :

    if n<=1 :
        return 1
    #n! = n*(n-1)!를 그대로 코드로 작성하기
    return n*factorial_recursive(n-1)

#각각의 방식으로 구현한 n! (출력 n=5)
print('반복적으로 구현:',factorial_iterative(5))
print('재귀적으로 구현:',factorial_recursive(5))

'''
결과
반복적으로 구현: 120
재귀적으로 구현: 120
'''

▲반복문 대신 재귀함수를 사용했을 때 얻을 수 있는 장점?

위의 코드를 비교했을 때 재귀 함수의 코드가 더 간결한 것을 알 수 있다. 이렇게 간결해진 이유는 재귀 함수가 수학의 점화식(재귀식)을 그대로 소스코드로 옮겼기 때문이다. 수학에서 점화식은 특정한 함수를 자신보다 더 작은 변수에 대한 함수와의 관계로 표현한 것을 의미한다. 

 

 

 

Comments