'C++' Divide and Conquer (Fibonacci)

토스트·2025년 5월 9일
0

Algorithms in C++

목록 보기
3/6

분할 정복(Divide and Conquer)

큰 문제를 작은 하위 문제로 나누고(Divide), 이 하위 문제를 재귀적으로 해결(Conquer) 한 후, 그 결과를 합쳐서(Combine) 원래 문제의 해답을 얻는 알고리즘 설계 기법입니다.

제곱 분할법(Exponentiation by Squaring)

거듭제곱을 계산할 때 bnb^{n}(b(n/2))2(b^{(n/2)})^{2}이 될 수 있음을 이용하여 작은 문제로 나누고, 이 하위 문제를 재귀적으로 해결한 후, 그 결과를 합쳐서 bnb^{n}의 해답을 얻을 수 있습니다.

2102^{10}을 구해봅시다.

#include <iostream>

using namespace std;

int pow(int base, int exp) {
	if (exp == 0) return 1;

	int half = pow(base, exp / 2);

	return exp % 2 == 0 ? half * half : base * half * half;
}

int main() {
	cout << pow(2, 10);

	return 0;
}

결과

pow(2, 10) = pow(2, 5) * pow(2, 5) --> 2102^{10} = (25)2(2^{5})^{2}

pow(2, 5) = 2 * pow(2, 2) * pow(2, 2) --> 252^{5} = 2(22)22 * (2^{2})^{2}

pow(2, 2) = pow(2, 1) * pow(2, 1); --> 222^{2} = (21)2(2^{1})^{2}

pow(2, 1) = 2 * pow(2, 0) * pow(2, 0) --> 212^{1} = 2(20)22* (2^{0})^{2}

pow(2, 0) = 1
pow(2, 1) = 2 * 1 * 1 = 2
pow(2, 2) = 2 * 2 = 4
pow(2, 5) = 2 * 4 * 4 = 32
pow(2, 10) = 32 * 32 = 1024

피보나치 행렬(Fibonacci matrix)

Fi+1=Fi+Fi1F_{i + 1} = F_{i} + F_{i - 1}
Fi=Fi+0F_{i} = F_{i} + 0

이 식을 행렬로 바꿔보면,
F0=0F_{0} = 0
F1=1F_{1} = 1

[Fi+1Fi]=[1110][FiFi1]=[Fi+Fi1Fi+0]\begin{bmatrix} F_{i+1} \\ F_{i} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \cdot \begin{bmatrix} F_{i} \\ F_{i-1} \end{bmatrix} = \begin{bmatrix} F_{i} + F_{i -1} \\ F_{i} + 0 \end{bmatrix}
[FiFi1]=[1110][Fi1Fi2]=[Fi1+Fi2Fi1+0]\begin{bmatrix} F_{i} \\ F_{i-1} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \cdot \begin{bmatrix} F_{i-1} \\ F_{i-2} \end{bmatrix} = \begin{bmatrix} F_{i-1} + F_{i -2} \\ F_{i-1} + 0 \end{bmatrix}
\cdot
\cdot
\cdot
[F2F1]=[1110][F1F0]=[1110][10]\begin{bmatrix} F_{2} \\ F_{1} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \cdot \begin{bmatrix} F_{1} \\ F_{0} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix}

즉,

[Fi+1Fi]=[1110]i[10]\begin{bmatrix} F_{i+1} \\ F_{i} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^i \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix}

제곱 분할법과 피보나치 행렬을 이용한 피보나치 수열

#include <iostream>

using namespace std;

const int M[2][2] = { {1, 1}, {1, 0} };

void multiply(int X[2][2], const int Y[2][2]) {
    int a = X[0][0] * Y[0][0] + X[0][1] * Y[1][0];
    int b = X[0][0] * Y[0][1] + X[0][1] * Y[1][1];
    int c = X[1][0] * Y[0][0] + X[1][1] * Y[1][0];
    int d = X[1][0] * Y[0][1] + X[1][1] * Y[1][1];

    X[0][0] = a;
    X[0][1] = b;
    X[1][0] = c;
    X[1][1] = d;
}

void pow(int F[2][2], int n) {
    if (n <= 1) return;

    pow(F, n / 2); 
    multiply(F, F);

    if (n % 2 != 0) multiply(F, M);
}

int fibo(int n) {
	if (n <= 1) return n;

	int F[2][2] = { {1, 1}, {1, 0} };

    pow(F, n - 1);

	return F[0][0];
}

int main() {
	cout << fibo(10);

	return 0;
}

결과

[F10F9]=[1110]9[10]=(([1110]2)2)2[1110][10]=[55343421][10]=[5434]\begin{bmatrix} F_{10} \\ F_{9} \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^9 \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \left( \left( \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^2 \right)^2 \right)^2 \cdot \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 55 & 34 \\ 34 & 21 \end{bmatrix} \cdot \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 54 \\ 34 \end{bmatrix}

0개의 댓글