본문 바로가기
Python

파이썬 재귀함수

by 승환파크 2023. 5. 9.

재귀 호출

함수 안에서 자기 자신을 호출하는 방식이다.

알고리즘을 구현할 때 반복문으로 구현한 코드보다 재귀호출로 구현한 코드가 더 직관적이고 이해하기 쉬운 경우가 종종 있다.

 

재귀호출 사용

def hello():
    print("Hello, World!")
    hello()

# 결과값
# Hello World!
# Hello World!
# Hello World!
# ... 1000번 무한 반복

재귀호출이 종료되지 않으면 최대 재귀 깊이를 초과할 때 에러 발생

파이썬의 최대 재귀 깊이(maximum recursion depth) : 1000

 

재귀호출에 종료 조건 만들기

def hello(count):
    if count == 0: # 종료 조건, count가 0일 때 종료
        return
    print("Hello World!", count)
    count -= 1 # count를 1감소 시킨뒤 다시 hello에 넣음
    hello(count)

# 결과값
# Hello World! 5
# Hello World! 4
# Hello World! 3
# Hello World! 2
# Hello World! 1

실행 순서

hello(5) -> hello(4) -> hello(3) -> hello(2) -> hello(1) -> hello(0)

 

종료 순서

hello(0) -> hello(1) -> hello(2) -> hello(3) -> hello(4) -> hello(5)

 

팩토리얼 구하기

1부터 n까지 양의 정수를 차례대로 곱한 값을 팩토리얼 이라고 한다.

! 기호로 표기한다.

 

팩토리얼을 구하는 방식

1. 반복문

def factorial(n):
    output = 1
    
    # 반복문으로 숫자를 더하기
    for i in range(1, n + 1):
        output *= i
        
    return output

print(factorial(5))
# 결과값
# 120

 

2.재귀함수

def factorial(n):
    if n == 1: # n 이 1일때, 1을 반환하고 재귀호출 종료
        return 1
    
    return n * factorial(n - 1) # n과 factorial 함수에 n - 1을 넣어서 반환된 값을 곱하기

print(factorial(5))
# 결과값
# 120
# factorial 함수 연산 과정
# 5 * factorial(4)
    # 4 * factorial(3)
        # 3 * factorial(2)
            # 2 * factorial(1)
                # return 1
            # 2 * 1
        # 3 * 2
    # 4 * 6
# 5 * 24

 

재귀함수의 문제점

상황에 따라서 같은 것을 기하급수적으로 많이 반복하게 된다.

따라서 이 문제를 위해서 메모화(memorization)를 사용하는것이 좋다.

 

 

피보나치 수열

0과 1로 시작해서 다음번 숫자는 두 앞의 두 피보나치 수의 합이 나오는 수열이다.

# 피보나치 수열 : 0과 1로 시작해서 다음번 수는 바로 앞의 두 피보나치 수의 합
def fibonacci(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci(10)
# 결과값
# 55
counter = 0

def fibonacci(n):
    print(f"fibonacci({n})를 계산중")
    global counter
    counter += 1
    
    # 피보나치 구하기
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

fibonacci(10)
print(counter)
# 결과값
# 109
# fibonacci(10)를 계산중
# fibonacci(9)를 계산중
# fibonacci(8)를 계산중
# fibonacci(7)를 계산중
# fibonacci(6)를 계산중
# fibonacci(5)를 계산중
# fibonacci(4)를 계산중
# ...
# fibonacci(2)를 계산중
# fibonacci(1)를 계산중
# fibonacci(2)를 계산중
55

이처럼 재귀함수는 한번 구했던 값이라도 처음부터 다시 계산해야 하기때문에 계산 횟수가 기하급수적으로 늘어나게된다.

따라서 이와같은 상황을 만들지 않기 위해서 메모화를 사용한다.

memo_dict = {1 : 1, 2 : 1}

def fibonacci(n):
    if n in memo_dict:
        # 메모가 되어 있으면 메모된 값을 리턴
        return memo_dict[n]
    else:
        # 메모가 되어있지 않으면 값을 구함
        output = fibonacci(n - 1) + fibonacci(n - 2)
        memo_dict[n] = output
        return output

fibonacci(50)
# 결과값
# 12586269025

피보나치 수열을 구하는 방식을 처음 작성한 코드에서는 fibonacci(40) 을 구하는 시간이 상당히 오래 걸리지만 메모화를 만들면 fibonacci(1000)까지도 금방 값이 나오게 된다.

메모화를 위해서 딕셔너리를 사용해 한 번 계산한 값을 저장하였다.

딕셔너리에 값이 저장되어 있으면 처리를 수행하지 않고 곧바로 메모된 값을 반환하면서 코드의 실행 속도를 빠르게 하는 방식이다.

 

리스트를 평탄화 하는 재귀함수

리스트 평탄화는 중첩된 리스트가 있을 때 중첩을 모두 제거하고 풀어서 1차원 리스트로 만드는 것을 말한다.

def flatten(data):
    output = []
    
    for item in data:
        # 리스트가 아니라면 output리스트에 자료를 넣고
        # 리스트라면 리스트에 있는 요소들을 하나하나 output에 추가
        if type(item) == list:
            output.extend(item)
        else:
            output.append(item)
            
    return output

example = [[1, 2, 3,], [4, 5, 6], 7, [8, 9]]
flatten(example)
# 결과값
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 3차원이 되면 문제 발생
example = [[1, 2, 3,], [4, [5, 6]], 7, [8, 9]]
flatten(example)
# 결과값
# [1, 2, 3, 4, [5, 6], 7, 8, 9]

리스트를 평탄화 하는 코드를 사옹했을 때 3차원 이상의 리스트가 나오면 리스트 평탄화가 제대로 작동되지 않는다.

3차원을 평탄화 하려면 코드를 수정해야 하고 그 이상으로 n차원의 리스트가 나온다면 코드의 수정이 얼마나 많이 수정되야 할지 감도 안온다. 이러한 상황을 만들지 않기 위해서 재귀함수를 사용해서 코드를 수정해주는것이 좋다.

# 재귀함수 사용
def flatten(data):
    output = []
    
    for item in data:
        if type(item) == list:
            output.extend(flatten(item))
        else:
            output.append(item)
    return output

example = [[1, 2, 3], [4, 5, 6], 7, [8, 9]]
flatten(example)
# 결과값
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

example = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
flatten(example)
# 결과값
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

example = [[1, 2, 3], [4, [5, [6, 7]]], [8, 9]]
flatten(example)
# 결과값
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

'Python' 카테고리의 다른 글

파이썬 제너레이터  (0) 2023.05.09
파이썬 람다함수  (0) 2023.05.09
파이썬 이터레이터  (0) 2023.05.09
파이썬 예외처리  (0) 2023.05.09
파이썬 모듈  (1) 2023.05.08