피보나치 수란 무엇일까요?
피보나치 수(Fibonacci numbers)는 보통 Fn으로 표기하며, 0과 1에서 시작해 각 항이 바로 앞의 두 항의 합이 되는 수열, 즉 피보나치 수열을 이룹니다. 수식으로 나타내면 다음과 같습니다.
F0 = 0, F1 = 1
Fn = Fn-1 + Fn-2 (n > 1)
단순 반복문으로 피보나치 수를 구하면 O(n)의 시간이 필요하지만, 행렬 거듭제곱(Matrix Exponentiation) 기법을 활용하면 분할 정복 방식으로 거듭제곱을 빠르게 계산하여 시간 복잡도를 O(log n)까지 줄일 수 있습니다.
핵심 원리
피보나치 수열은 다음과 같이 2×2 행렬의 거듭제곱 형태로 표현할 수 있습니다.
| F(n+1) F(n) | | 1 1 | ^n | F(n) F(n-1) | = | 1 0 |
따라서 기본 행렬 M = {{1,1},{1,0}}의 (n-1)제곱을 구한 뒤, 결과 행렬의 첫 번째 요소 F[0][0]을 반환하면 n번째 피보나치 수를 얻을 수 있습니다.
알고리즘
시작
2×2 크기의 배열 두 개를 준비한다
행렬 곱셈을 수행하는 함수(multiply)를 만든다
행렬의 거듭제곱을 구하는 함수(power)를 만든다
피보나치 수를 반환하는 함수(fibonacci_matrix)를 만든다
multiply(arr1[2][2], arr2[2][2])
변수 a, b, c, d 넷을 선언하고 다음을 계산한다
a = arr1[0][0]*arr2[0][0] + arr1[0][1]*arr2[1][0]
b = arr1[0][0]*arr2[0][1] + arr1[0][1]*arr2[1][1]
c = arr1[1][0]*arr2[0][0] + arr1[1][1]*arr2[1][0]
d = arr1[1][0]*arr2[0][1] + arr1[1][1]*arr2[1][1]
계산 결과를 arr1에 다시 저장한다
power(arr1[2][2], 정수 n)
n이 0 또는 1이면 그대로 종료한다
M = {{1,1},{1,0}}로 초기화한다
power(arr1, n/2)로 절반씩 재귀 호출한다
multiply(arr1, arr1)로 스스로를 제곱한다
n이 홀수이면 multiply(arr1, M)을 한 번 더 수행한다
fibonacci_matrix(n)
F = {{1,1},{1,0}}로 초기화한다
n이 0이면 0을 반환한다
power(F, n-1)을 호출한다
F[0][0]을 반환한다
끝
C++ 예제 코드
#include <iostream>
using namespace std;
// 두 2×2 행렬을 곱한 결과를 F에 저장합니다.
void multiply(int F[2][2], int M[2][2]) {
int a = F[0][0] * M[0][0] + F[0][1] * M[1][0];
int b = F[0][0] * M[0][1] + F[0][1] * M[1][1];
int c = F[1][0] * M[0][0] + F[1][1] * M[1][0];
int d = F[1][0] * M[0][1] + F[1][1] * M[1][1];
F[0][0] = a;
F[0][1] = b;
F[1][0] = c;
F[1][1] = d;
}
// 분할 정복으로 행렬 F의 n제곱을 구합니다.
void power(int F[2][2], int n) {
if (n == 0 || n == 1)
return;
int M[2][2] = {{1,1},{1,0}};
power(F, n / 2);
multiply(F, F);
if (n % 2 != 0)
multiply(F, M);
}
int fibonacci_matrix(int n) {
int F[2][2] = {{1,1},{1,0}};
if (n == 0)
return 0;
power(F, n - 1);
return F[0][0];
}
int main() {
int n;
while (1) {
cout << "Enter the integer n to find nth fibonacci no. (enter 0 to exit):";
cin >> n;
if (n == 0)
break;
cout << fibonacci_matrix(n) << endl;
}
return 0;
}
실행 결과
Enter the integer n to find nth fibonacci no. (enter 0 to exit): 2 1 Enter the integer n to find nth fibonacci no. (enter 0 to exit): 6 8 Enter the integer n to find nth fibonacci no. (enter 0 to exit): 7 13 Enter the integer n to find nth fibonacci no. (enter 0 to exit): 0
마무리
이 프로그램은 사용자가 0을 입력할 때까지 반복적으로 정수를 입력받아, 해당 번째의 피보나치 수를 화면에 출력합니다. 행렬 거듭제곱 기법 덕분에 n이 커져도 로그 시간 안에 결과를 얻을 수 있어, 지수 시간이 걸리는 단순 재귀 구현이나 O(n)의 반복문 구현보다 훨씬 효율적입니다. 다만 n이 매우 커질 경우 int 타입의 오버플로우가 발생할 수 있으므로, 필요에 따라 long long 같은 더 넓은 범위의 자료형을 사용하는 것이 좋습니다.