분할 정복(Divide and conquer)
문제를 반으로 나눠 각각 풀고 답을 합치는 방법. 병합 정렬(merge sort), 빠른 곱셈, 고속 푸리에 변환(fast Fourier transform)이 모두 이 틀이다.
분할 정복은 재귀(recursion)의 한 형태입니다. 크기 n인 문제를 크기 n/2인 문제 몇 개로 나누고(분할), 각각을 같은 방법으로 푼 뒤(정복), 답을 합칩니다. 가장 잘 알려진 예가 병합 정렬입니다. 목록을 반으로 나눠 각각 정렬하고, 정렬된 두 목록을 앞에서부터 하나씩 비교하며 합칩니다. 길이 8인 두 목록을 합칠 때는 원소(element) 16개를 하나씩 비교해 옮기므로 16번쯤 일합니다.
병합 정렬 전체의 일을 세어 봅시다. n = 16이면 반씩 나누기를 16 → 8 → 4 → 2 → 1로 4번 하므로, 합치는 일이 일어나는 층이 4개(크기 16, 8, 4, 2인 층) 생깁니다. 일반적으로 n을 1이 될 때까지 반으로 나누는 횟수가
이제 일반화합니다. 한 문제를 반 크기 문제 a개로 나누고, 나누고 합치는 데
입니다. 병합 정렬이면
하위 문제 수
비율
정리하면, 가장 무거운 층이 전체의 크기를 정합니다. r ≠ 1이면 전체는 가장 무거운 층(맨 위층이나 잎(leaf) 층)의 상수배이고, r = 1이면 모든 층이 똑같이 나눠 가져 한 층의 일에 층 수
r > 1일 때 전체를 정하는 잎 층을 세어 봅시다. 잎 하나의 일은 상수이므로 잎 층의 일은 잎의 개수에 비례합니다. 맨 아래층에는 잎이
곱셈이 좋은 예입니다. n자리 수 두 개를 초등학교 방식으로 곱하면 한 자리 곱셈을
입니다. 그대로 계산하면 반 크기 곱셈
1960년 모스크바 대학의 학생이던 아나톨리 카라추바는 곱셈을 세 번으로 줄였습니다.
- 첫째 곱:
- 둘째 곱:
- 셋째 곱:
, 가운데 항은 (실제로 ) - 합치기:
, 그리고 실제로 12 × 34 = 408입니다.
더하고 빼는 일은 n에 비례하니(a = 3, d = 1) 그 결과는
같은 요령은 행렬(matrix)에도 통합니다. 1969년 독일의 수학자 폴커 슈트라센은 행렬을 2×2 블록으로 나눈 행렬의 곱(matrix multiplication)을 여덟 번이 아닌 일곱 번의 블록 곱으로 해냈습니다. 블록 곱 하나는 반 크기 행렬의 곱이고, 블록을 더하는 일은
이어지는 곳.
- 고속 푸리에 변환: 신호를 여러 주파수의 파동의 합으로 나누는 계산(이산 푸리에 변환, discrete Fourier transform)을
이 아닌 에 해내는 분할 정복입니다. n이 2의 거듭제곱일 때 값을 짝수 번째와 홀수 번째로 나누면, 1의 거듭제곱근(거듭제곱하면 1이 되는 복소수(complex number))의 대칭 덕분에 반 크기 문제 두 개가 됩니다(a = 2, d = 1). 1965년 쿨리와 튜키가 발표해 널리 퍼졌는데, 가우스가 1805년 무렵 소행성 궤도(orbit)를 계산하며 같은 방법을 써 두었다는 사실이 뒤에 밝혀졌습니다. - 고속 푸리에 변환 덕분에 다항식(polynomial)의 곱셈, 푸리에 급수(Fourier series)의 계수를 수치로 어림하는 일, JPEG 압축에 쓰이는 이산 코사인 변환(discrete cosine transform)을 빠르게 계산할 수 있습니다.
- 이진 탐색(binary search): 반쪽 하나만 남기고 나머지를 버리는, a = 1, d = 0인 경우입니다.
- 동적 계획법(dynamic programming): 하위 문제들이 서로 겹치면 분할 정복은 같은 일을 되풀이하게 됩니다. 그때는 한 번 푼 답을 적어 두고 다시 쓰는 이 방법이 낫습니다.
- 병렬 스캔(parallel scan): 매 걸음 '앞의 값에 한 수를 곱하고 다른 수를 더하는' 되풀이
(여기서 는 걸음마다 주어진 수로, 위의 a와는 다릅니다)도 나눠 풀 수 있습니다. 두 걸음을 이으면 다시 '곱하고 더하기' 한 걸음이 됩니다(×2 하고 +1, 이어서 ×3 하고 +4는 ×6 하고 +7). 이 잇기는 어떻게 묶어도 결과가 같으므로(모노이드, monoid), 앞뒤 반을 따로 계산해 잇기를 되풀이하면 일꾼이 충분할 때 약 단계에 끝납니다. 상태 공간 모형(state space model)의 하나인 Mamba가 학습 때 이 방법을 씁니다. - 점화식(recurrence relation)과 수학적 귀납법(mathematical induction):
같은 식을 일반적으로 풀고 그 답을 증명하는 도구입니다.
이 개념이 나오는 긴 글
이 개념을 언급하는 페이지
- 행렬의 곱
… 덧셈을 더 쓰는 대신 이를 7번으로 줄이는 식을 찾았습니다. 조각 안의 곱셈에도 같은 방법을 되풀이하면(분할 정복) 곱셈 횟수는 n^{\log_2 7} \approx n^{2.81} 으로 줄어듭니다.
- 푸리에 급수
… 약 n^2 번 필요하지만, 1의 거듭제곱근의 성질을 이용해 문제를 절반 크기 둘로 쪼개기를 되풀이하면(분할 정복) n\log n 번 정도로 줄어듭니다. 1965년 제임스 쿨리와 존 튜키가 발표해 널리 퍼졌습니다. …
- 1의 거듭제곱근
… 되풀이하는 것이 고속 푸리에 변환(FFT)으로, 계산량을 n^2 에서 n\log n 정도로 줄입니다(분할 정복). 1965년 미국 IBM의 제임스 쿨리와 통계학자 존 튜키가 발표해 널리 퍼졌고, 가우스가 …
- 점화식
… 두 개로 나눠 풀고, 두 답을 합치는 데 n걸음이 든다는 뜻입니다. 그 답이 n\log n 정도라는 것이분할 정복과 점근 표기법의 기본 계산입니다. 규칙이 일차식이 아니면 사정이 전혀 다릅니다. x_{n+1} = r …
- 알고리즘
… 몇 가지 있습니다. 문제를 같은 모양의 더 작은 문제로 줄이는 재귀, 반으로 나눠 각각 풀고 합치는분할 정복, 겹쳐 나오는 작은 문제의 답을 표에 적어 두고 다시 쓰는 동적 계획법, 매 순간 가장 좋아 보이는 …
- 이진 탐색
… 가까워진 뒤로 맞는 자릿수를 매번 두 배쯤으로 늘립니다. 문제를 반으로 줄여 한쪽만 푸는 이진 탐색은분할 정복의 가장 단순한 경우입니다. 이 생각을 자료 구조로 굳힌 것이 이진 탐색 트리로, 각 점의 왼쪽 …
- 재귀
… a \bmod b) 은 자기를 한 번만 부르는 재귀이고, 문제를 반으로 나눠 두 번 부르는 것이분할 정복입니다. 문장 안에 문장이 들어가는 언어의 구조는 문맥 자유 문법의 재귀 규칙으로 적고, ⟦람다 …
- 정렬 알고리즘
… 몇 개 없는 거의 정렬된 입력은 n번 남짓에 끝냅니다. 병합 정렬 은 반으로 나눠 각각 정렬한 뒤 합치는분할 정복입니다. 어떤 입력에서든 비교가 n \log_2 n 번을 넘지 않고, 가장 운이 좋아도 그 절반쯤은 …
- 동적 계획법
… 정수이면 물건 수 × 무게 한도 크기의 표로 풉니다), 분할수를 세는 표도 모두 동적 계획법입니다.분할 정복과 달리 하위 문제가 겹칠 때 쓰고, 매 순간 하나만 고르는 욕심쟁이 알고리즘이 틀리는 문제도 모든 …
- 이산 코사인 변환
… 제임스 쿨리와 존 튜키가 널리 알린 고속 푸리에 변환(FFT)처럼, 문제를 절반 크기 둘로 쪼개 푸는분할 정복으로 N^2 번 대신 O(N \log N) 번의 계산으로 끝낼 수 있습니다. 이어지는 곳. 양 끝이 …
- 모노이드
… 기다려야 하는 단계 수입니다. n개라면 n − 1단계 대 \lceil\log_2 n\rceil 단계입니다(분할 정복, 점근 표기법). 수백만 개의 수를 더할 때 여러 기계가 조각을 나눠 더한 뒤 그 결과를 다시 더해도 …
- 상태 공간 모형과 선형 순환
… 나눠 합치는 병렬 스캔으로, 프로세서가 충분하면 길이 n을 약 \log_2 n 단계에 계산할 수 있습니다(분할 정복). 결합법칙이 있어야 어느 이웃끼리 먼저 합쳐도 답이 같기 때문입니다. Mamba는 여기에 상태를 …
- 이산 푸리에 변환과 고속 푸리에 변환
… \log_2 N 은 1,024 × 10으로 1만 남짓입니다. 문제를 반으로 나누어 풀고 합치는 이런 방법이분할 정복이고, 계산량을 이렇게 N의 함수로 어림하는 말이 점근 표기법입니다. N이 2의 거듭제곱이 아니어도 …