Skip to Content
독학사독학사 4단계알고리즘03. 점화식과 재귀 알고리즘 분석 기초

이번 문서의 목표: 이 문서를 다 읽으면 재귀 함수의 호출 과정을 손으로 추적해 변수 값 표를 그릴 수 있고, 간단한 점화식을 반복 대입과 재귀 트리 두 가지 방법으로 직접 풀 수 있으며, 마스터 정리로 그 결과를 빠르게 검산할 수 있다.

왜 점화식을 따로 떼어 배우는가

03편에서는 반복문의 실행 횟수를 시그마로 세어 복잡도를 유도했다. 하지만 07편의 퀵·병합 정렬이나 15편의 분할정복 기법처럼, 자기 자신을 더 작은 크기로 다시 호출하는 재귀(recursion) 알고리즘은 반복문만으로는 실행 횟수를 세기 어렵다. “크기 n짜리 문제를 풀려면 크기 n/2짜리 문제를 두 번 풀고, 거기에 추가로 n만큼 일을 더 한다”처럼 알고리즘의 실행 시간을 자기 자신의 더 작은 버전으로 정의하는 식이 필요한데, 이런 식을 점화식(recurrence relation)이라고 부른다.

07편(퀵·병합 정렬)과 15편(분할정복)에서 점화식 계산이 계속 반복해서 나오므로, 여기서 점화식을 세우고 푸는 절차를 한 번 확실히 익혀 두면 이후 편에서는 “이 알고리즘의 점화식은 이렇게 세워진다”까지만 짚고 계산 자체는 이 편에서 익힌 절차를 그대로 적용하면 된다.

쉽게 말하면: 이 편은 “재귀 알고리즘이 몇 번 일하는지”를 세는 방법을 배우는 편이다.

재귀 호출 추적 — 변수 값 표로 눈에 보이게 만들기

점화식을 세우기 전에, 재귀 함수가 실제로 어떻게 호출되고 되돌아오는지부터 손으로 따라가 보자. 다음은 n의 팩토리얼(02편에서 배운 n!)을 재귀로 계산하는 의사코드다.

function factorial(n): if n <= 1: return 1 return n * factorial(n - 1)

factorial(4)를 호출했을 때 어떤 일이 일어나는지 단계별로 추적해 보자.

호출 단계호출n <= 1 인가반환하는 식실제 반환값(재귀 결과가 나온 뒤)
1factorial(4)아니오4 × factorial(3)4 × 6 = 24
2factorial(3)아니오3 × factorial(2)3 × 2 = 6
3factorial(2)아니오2 × factorial(1)2 × 1 = 2
4factorial(1)1 (더 이상 재귀 호출 없음)1

이 표에서 1~3단계는 “아직 답을 모르니 더 작은 문제를 풀어 달라”며 호출을 계속 쌓아 나가는 과정이고, 4단계에서 n <= 1이라는 조건(기저 조건, base case, 재귀 호출을 멈추게 하는 조건)에 도달해 즉시 답을 반환한다. 이후에는 4단계 → 3단계 → 2단계 → 1단계 순서로 되돌아가며 곱셈이 실제로 계산된다.

비유로 이해하기: 재귀 호출은 “포장을 뜯기 전까지는 안의 내용물을 모르는 러시아 인형(마트료시카)“과 비슷하다. 가장 작은 인형(기저 조건)의 답을 먼저 확인한 다음, 그 답을 가지고 한 겹씩 바깥 인형으로 나오면서 최종 결과를 완성한다.

자주 틀리는 점: 기저 조건이 없거나 잘못 설정되면 재귀 호출이 끝없이 이어져 무한 재귀(스택 오버플로 오류의 원인)가 발생한다. 재귀 함수를 볼 때는 항상 “언제 호출을 멈추는가(기저 조건)“와 “그 조건에 얼마나 빨리 다가가는가(재귀 호출마다 문제 크기가 확실히 줄어드는가)“를 먼저 확인하는 습관이 필요하다.

점화식이란 무엇인가 — 알고리즘의 실행 시간을 자기 자신으로 정의하기

factorial(n)이 몇 번 곱셈을 하는지 점화식으로 표현해 보자. T(n)을 “크기 n일 때 필요한 곱셈 횟수”라고 정의하면 다음과 같이 쓸 수 있다.

T(n)=T(n1)+1,T(1)=0T(n) = T(n-1) + 1, \quad T(1) = 0
  • T(n)T(n): 크기 n일 때 걸리는 작업량(여기서는 곱셈 횟수). 앞으로 점화식에서 항상 이렇게 “구하고 싶은 함수”를 정의하고 시작한다.
  • T(n1)T(n-1): 크기를 하나 줄인 부분 문제(재귀 호출 한 번)를 푸는 데 걸리는 작업량.
  • +1+1: 재귀 호출을 제외하고 그 단계에서 추가로 하는 작업(여기서는 곱셈 한 번).
  • T(1)=0T(1) = 0: 기저 조건에서의 작업량. 재귀가 멈추는 지점의 값을 반드시 함께 정의해야 점화식을 끝까지 풀 수 있다.

이 점화식을 말로 풀면 “n짜리 문제를 풀려면, n-1짜리 문제를 풀고(T(n-1)), 거기에 곱셈 한 번(+1)을 더 한다”는 뜻이다.

등차형 점화식 풀기 — 반복 대입법

T(n) = T(n-1) + 1 같은 점화식은 반복 대입법(iteration method, 점화식을 계속 풀어서 대입해 나가며 규칙을 찾는 방법)으로 쉽게 풀 수 있다.

한 단계씩 전개하기

T(n)=T(n1)+1T(n) = T(n-1) + 1

T(n1)=T(n2)+1T(n-1) = T(n-2) + 1이므로 대입하면 T(n)=T(n2)+1+1=T(n2)+2T(n) = T(n-2) + 1 + 1 = T(n-2) + 2

T(n2)=T(n3)+1T(n-2) = T(n-3) + 1이므로 대입하면 T(n)=T(n3)+1+2=T(n3)+3T(n) = T(n-3) + 1 + 2 = T(n-3) + 3

규칙 찾기

k번 전개했을 때 T(n)=T(nk)+kT(n) = T(n-k) + k라는 규칙이 보인다.

기저 조건에 도달할 때까지 전개하기

nk=1n - k = 1이 되는 지점, 즉 k=n1k = n - 1일 때 기저 조건 T(1)=0T(1) = 0에 도달한다.

T(n)=T(1)+(n1)=0+(n1)=n1T(n) = T(1) + (n-1) = 0 + (n-1) = n-1

n = 4로 검산해 보면 factorial(4)4×3×2×1을 계산하는 과정에서 곱셈을 4×3, (12)×2, (24)×1 이렇게 3번(n-1 = 3번) 수행하므로 식과 정확히 일치한다. 이 점화식을 Big-O로 표현하면 T(n)=n1=O(n)T(n) = n - 1 = O(n)이다. “n-1씩 계속 줄어드는” 이런 형태의 점화식을 등차형 점화식이라고 부르며, 결과는 항상 n에 정비례하는 선형 시간이 된다.

등비형 점화식 풀기 — 재귀 트리로 그림 그려 보기

이번에는 07편의 병합 정렬, 15편의 분할정복에서 실제로 등장하는 형태의 점화식을 다뤄 보자.

T(n)=2T ⁣(n2)+n,T(1)=1T(n) = 2\,T\!\left(\frac{n}{2}\right) + n, \quad T(1) = 1
  • 2T(n/2)2\,T(n/2): 크기 n짜리 문제를 정확히 절반 크기인 n/2짜리 문제 두 개로 나눠서 푼다는 뜻(분할정복에서 “반으로 나누고 두 번 재귀 호출”하는 패턴을 그대로 옮긴 식이다).
  • +n+n: 두 부분 문제의 결과를 합치는 데 걸리는 추가 작업(예: 병합 정렬에서 두 정렬된 배열을 하나로 합치는 과정)이 n에 비례한다는 뜻.

이런 점화식은 반복 대입법보다 재귀 트리(recursion tree, 재귀 호출이 가지를 치며 뻗어나가는 모습을 나무 형태로 그린 그림)로 그려서 각 레벨(깊이)의 작업량을 더하는 방식이 훨씬 직관적이다.

각 레벨의 작업량 세기

레벨 0(맨 위, 처음 호출)은 노드 1개, 각 노드의 작업량 n → 레벨 합계 n.

레벨 1은 노드 2개, 각 노드의 작업량 n/2 → 레벨 합계 2 × (n/2) = n.

레벨 2는 노드 4개, 각 노드의 작업량 n/4 → 레벨 합계 4 × (n/4) = n.

이 패턴을 보면 모든 레벨의 작업량 합계가 항상 n으로 똑같다는 규칙이 보인다.

레벨의 개수(트리의 높이) 구하기

문제 크기가 n → n/2 → n/4 → ⋯ → 1로 줄어들며, 매 레벨마다 절반씩 나누므로 몇 레벨을 내려가야 크기가 1이 되는지는 02편에서 배운 로그의 정의 그대로다. n을 2로 몇 번 나눠야 1이 되는가는 log2n\log_2 n번이므로, 트리의 높이(레벨의 개수)는 log2n+1\log_2 n + 1이다(0번째 레벨부터 세므로 1을 더한다).

전체 작업량 = 레벨당 작업량 × 레벨 수

T(n)=n×(log2n+1)=nlog2n+nT(n) = n \times (\log_2 n + 1) = n\log_2 n + n

최고차항만 남기면 T(n)=O(nlogn)T(n) = O(n \log n)이다.

n = 8로 검산해 보자. 레벨 0(크기 8, 작업량 8) → 레벨 1(크기 4씩 2개, 합 8) → 레벨 2(크기 2씩 4개, 합 8) → 레벨 3(크기 1씩 8개, 합 8). 레벨은 0부터 3까지 총 4개이고, log₂8 + 1 = 3 + 1 = 4로 정확히 일치한다. 전체 작업량은 8 × 4 = 32이며 이는 n log₂n + n = 8×3 + 8 = 32와도 일치한다. 이 점화식이 바로 07편에서 다루는 병합 정렬의 복잡도가 O(nlogn)O(n \log n)이 되는 근거다.

자주 틀리는 점: 재귀 트리에서 “레벨마다 노드 수는 늘어나지만 각 노드의 작업량은 줄어든다”는 것을 놓치고 레벨 수만 세거나 노드 수만 세는 실수를 한다. 반드시 레벨의 노드 수와 노드 하나의 작업량을 곱해 그 레벨의 합계를 구한 다음, 레벨별 합계를 모두 더해야 한다.

마스터 정리 — 검산용 공식

재귀 트리를 매번 그리지 않고도 T(n)=aT(n/b)+f(n)T(n) = a\,T(n/b) + f(n) 형태의 점화식을 빠르게 판정할 수 있는 공식이 마스터 정리(master theorem)다. 여기서는 시험에서 요구하는 개요 수준으로만 정리한다(엄밀한 증명은 이 과목의 범위를 벗어난다).

T(n)=aT ⁣(nb)+f(n)T(n) = a\,T\!\left(\frac{n}{b}\right) + f(n)
  • aa: 한 번 재귀 호출할 때 몇 개의 부분 문제로 나뉘는가(위 병합 정렬 예시에서는 2).
  • bb: 부분 문제의 크기가 원래의 몇 분의 1로 줄어드는가(위 예시에서는 2, 즉 절반).
  • f(n)f(n): 재귀 호출을 제외하고 그 레벨에서 추가로 하는 작업량(위 예시에서는 n).

마스터 정리는 f(n)f(n)nlogban^{\log_b a}(재귀로 생기는 부분 문제 개수가 총 몇 개인지를 나타내는 기준값)을 비교해 세 가지 경우로 나눈다.

비교 결과의미결론
f(n)f(n)nlogban^{\log_b a}보다 차수가 낮다트리의 아래쪽(잎에 가까운 레벨)에서 하는 일이 더 많다T(n)=Θ(nlogba)T(n) = \Theta(n^{\log_b a})
f(n)f(n)nlogban^{\log_b a}과 같은 차수다모든 레벨에서 하는 일의 총량이 비슷하다(위 병합 정렬 예시가 이 경우)T(n)=Θ(nlogbalogn)T(n) = \Theta(n^{\log_b a} \log n)
f(n)f(n)nlogban^{\log_b a}보다 차수가 높다트리의 위쪽(처음 호출에 가까운 레벨)에서 하는 일이 더 많다T(n)=Θ(f(n))T(n) = \Theta(f(n))

병합 정렬 예시로 검산해 보면 a=2a=2, b=2b=2이므로 nlogba=nlog22=n1=nn^{\log_b a} = n^{\log_2 2} = n^1 = n이고, f(n)=nf(n) = n으로 nlogban^{\log_b a}와 정확히 같은 차수다. 따라서 두 번째 경우에 해당해 T(n)=Θ(n1logn)=Θ(nlogn)T(n) = \Theta(n^1 \log n) = \Theta(n \log n)이 되며, 앞서 재귀 트리로 직접 유도한 결과와 정확히 일치한다.

자주 틀리는 점: 마스터 정리는 T(n)=aT(n/b)+f(n)T(n) = a\,T(n/b) + f(n) 형태의 점화식에만 적용할 수 있다. 앞서 다룬 T(n) = T(n-1) + 1처럼 부분 문제의 크기가 n/b가 아니라 n-1, n-2처럼 한 개씩 줄어드는 형태에는 마스터 정리를 적용할 수 없고, 반복 대입법으로 풀어야 한다. 두 형태를 구분하는 것이 마스터 정리를 오용하지 않는 핵심이다.

이 편의 절차가 뒤에서 어떻게 쓰이는가

이후 편에서는 이 편에서 익힌 절차를 다음과 같이 재사용한다.

  • 07편(퀵·병합 정렬): 병합 정렬은 이 편의 예시와 똑같은 T(n)=2T(n/2)+nT(n) = 2T(n/2) + n을, 퀵 정렬은 피벗 선택에 따라 균형 분할(T(n) = 2T(n/2) + n, 최선)과 불균형 분할(T(n) = T(n-1) + n, 최악)이 어떻게 다른 결과를 내는지를 이 편의 재귀 트리·반복 대입 절차로 유도한다.
  • 15편(분할정복): 분할정복의 일반 패턴에서 점화식을 세우는 방법과 마스터 정리 적용 사례를 더 다양한 예제로 확장한다.
  • 16편(동적계획법): 재귀 호출이 같은 부분 문제를 중복해서 계산하는 비효율(예: 피보나치수를 단순 재귀로 계산하면 T(n)=T(n1)+T(n2)+1T(n) = T(n-1) + T(n-2) + 1로 지수 시간이 걸린다)을 이 편의 재귀 트리 사고로 확인한 뒤, 그 중복을 없애는 것이 동적계획법의 핵심 아이디어임을 배운다.

자주 틀리는 점 (종합)

  • 기저 조건을 빠뜨리는 실수: 점화식은 반드시 재귀 부분(T(n)=T(n) = \cdots)과 기저 조건(T(1)=T(1) = \cdots)을 함께 정의해야 끝까지 풀 수 있다.
  • 반복 대입법에서 전개 횟수와 남은 크기를 헷갈리는 실수: k번 전개했을 때 크기가 n-k인지, n/2^k인지 형태에 따라 다르므로 매번 직접 대입해 규칙을 확인해야 한다.
  • 재귀 트리에서 레벨 수를 세지 않고 노드 하나의 작업량만 보는 실수: 반드시 (레벨의 노드 수) × (노드 하나의 작업량)을 레벨마다 계산해야 한다.
  • 마스터 정리를 아무 점화식에나 적용하는 실수: T(n)=aT(n/b)+f(n)T(n) = a\,T(n/b) + f(n) 형태가 아니면(예: n-1씩 줄어드는 형태) 마스터 정리를 적용할 수 없다.

핵심 정리

  • 재귀 호출은 기저 조건에 도달할 때까지 호출을 쌓았다가, 기저 조건에서부터 거꾸로 계산 결과를 되돌려 받는 구조이며 변수 값 표나 mermaid 그림으로 추적하면 이해하기 쉽다.
  • 점화식은 “크기 n짜리 문제의 작업량을 더 작은 크기의 작업량으로 정의하는 식”이며, 재귀 부분과 기저 조건을 함께 정의해야 한다.
  • 등차형 점화식(T(n) = T(n-1) + c)은 반복 대입법으로 풀며 결과는 보통 선형 시간이 된다.
  • 등비형 점화식(T(n) = a T(n/b) + f(n))은 재귀 트리를 그려 레벨별 작업량을 더하는 방식이 직관적이며, T(n) = 2T(n/2) + nO(nlogn)O(n \log n)이 된다.
  • 마스터 정리는 f(n)f(n)nlogban^{\log_b a}의 차수를 비교해 세 가지 결론 중 하나로 빠르게 검산하는 공식이며, T(n) = a T(n/b) + f(n) 형태에만 적용된다.

마무리 복습

문제 14지선다
재귀 함수에서 기저 조건(base case)이 하는 역할로 가장 적절한 것은?
문제 24지선다
점화식 T(n) = T(n-1) + 1, T(1) = 0을 반복 대입법으로 풀었을 때 결과로 옳은 것은?
문제 34지선다
점화식 T(n) = 2T(n/2) + n을 재귀 트리로 분석할 때, 각 레벨(깊이)의 작업량 합계에 대한 설명으로 옳은 것은?
문제 44지선다
위 3번 문항의 점화식 T(n) = 2T(n/2) + n의 시간복잡도로 옳은 것은?
문제 54지선다
마스터 정리 T(n) = aT(n/b) + f(n)를 적용할 수 있는 점화식으로 옳은 것은?
문제 64지선다
병합 정렬의 점화식 T(n) = 2T(n/2) + n에 마스터 정리를 적용할 때 a=2, b=2이므로 n^(log_b a) = n이 되고, f(n) = n으로 이와 같은 차수다. 이때 마스터 정리의 결론으로 옳은 것은?

참고 자료

Last updated on