본문 바로가기

알고리즘

01. Recursion 재귀

728x90

재귀 함수란, 하나의 함수에서 자신을 다시 호출하여 작업을 수행하는 방식으로 주어진 문제를 푸는 방법이다.

재귀함수는 매 호출시마다 입력값이 변한다. 입력값의 변화가 없는 호출은 무한히 반복된다.

입력값의 변화가 없거나 입력값의 변화가 특정 패턴을 반복하게 되면 그 재귀함수는 영원히 반복되다가 콜스택 초과로 종료된다.

따라서 재귀함수 설계 시에는 입력값이 종료 조건으로 수렴하는지를 꼭 검증해야 한다.

재귀 함수의 구조는 크게 2가지로 나눌 수 있다.


종료 조건 (base case)

재귀 함수가 무한 호출에 빠지지 않게 해주는 조건을 명시하는 것이다.

이는 곧 이미 문제가 충분히 작아서, 더 작은 부분 문제로 나누지 않고도 바로 답을 알 수 있는 경우를 뜻한다.


재귀 조건 (recursive case)

종료 조건을 만족하지 못한 나머지 경우를 뜻한다.

즉, 현 문제가 너무 커서, 같은 형태의 더 작은 부분 문제를 재귀적으로 푸는 경우이다.


1부터 N까지의 합 계산

1. 반복문 활용

def sum(n):
    s = 0
    for i in range(1, n + 1):
        s += i
    return s
  • 수행 시간

2. 재귀적 방법 1

  • 논리
def sum(n):
    if n <= 1: return n
    return n + sum(n-1)
  • 수행 시간

3. 재귀적 방법 2

  • 논리
def sum(a, b): # return a + (a+1) + ... + b
    if a > b: return 0
    if a == b: return a
    m = (a+b)//2
    return sum(a, m) + sum(m+1, b)
  • 수행 시간
  • 재귀적 방법 1과의 차이점

리스트 거꾸로 배치하기

재귀적 방법1

  • 논리
def reverse(A):
    if len(A) == 1:
        return A
    return reverse(A[1:]) + A[0]
  • 수행시간

재귀적 방법2

  • 논리
def reverse(A, start, stop):
    if start <= stop:
        A[start], A[stop] = A[stop], A[start]    # 양 쪽 값 바꾸기
        reverse(A, start + 1, stop - 1)    # 나머지 가운데 부분 재귀
  • 수행시간

Tail Recursion

기본적인 팩토리얼 재귀함수를 생각해보자.

def factorial(n):
    if n == 1:
        return n
    return n * factorial(n - 1)

base case(factorial(1))까지 계산이 된 후에 return 이 되어 다시 계산되어 올라오면서 전체 값이 완성이 된다.

base case에 도달했을 때, 결과를 내놓을 순 없을까? 라는 아이디어에서 출발.

def factorial(n, value = 1):
    if n == 1:
        return value
    return factorial(n - 1, value * n)

factorial(n - 1, **value * n**)과 같이 중간에 계산된 값을 직접 전달base case에서 이미 최종 값을 전달 받음 ⇒ return

이는 recursion stack의 내용을 overwrite하는 방식으로 메모리 사용을 크게 줄일 수 있음.

TRO(Tail Recursion optimization)이라고 한다.

728x90

'알고리즘' 카테고리의 다른 글

03. Divide & Conquer 분할정복  (0) 2021.02.17
02. Selection  (0) 2021.02.17