반응형
기존 피보나치의 수열을 구하는 알고리즘은 다음과 같다
int Fibonacci(int n) { if(n==0) return 0; if(n==1 || n==2) return 1; return Fibonacci(n-1) + Fibonacci(n-2); }
하지만 이 알고리즘은 점점 재귀함수를 호출하는 횟수가 2배씩 늘어서
시간복잡도가 이기 때문에 매우 비효율적이다.
하지만 행렬과 분할정복 기법을 활용하면 수준으로 알고리즘을 개선할 수 있다.
우선 n번째 피보나치 수를 이라 하고 아래와 같은 2x2 정사각형 행렬을 만들 수 있다.
이 정사각행렬을 n번 제곱하면 다음과 같은 등식을 유도할 수 있다.
위의 행렬식을 이용해서 알고리즘을 만들면 의 알고리즘을 구현할 수 있지만
을 이용하면 좀 더 빠른 알고리즘을 설계할 수 있다.
위 식을 활용하여 n이 1이 될때까지 반으로 계속 분할한 다음 다시 합쳐서 원래 구하려던
원래의 을 구하여 n번째 피보나치 수열인 을 구할 수 있다.
저 위의 식은 n이 짝수일 때 적용되고 n이 홀수일 경우엔 n을 2로 나누면서 1이 버려지기 때문에
이렇게 을 한번 곱해야 한다.
이 알고리즘을 기반으로 c++로 구현해보았다.
#include<iostream> typedef unsigned long ULONG; //unsigned long을 LONG으로 사용 using namespace std; typedef struct Matrix_2x2 //2x2 정사각행렬 구조체 { ULONG f[2][2]; }Matrix2x2; Matrix2x2 mulitply(Matrix2x2 A, Matrix2x2 B) { //행렬의 곱하기 함수, A와 B를 곱해서 반환 Matrix_2x2 C; C.f[0][0] = A.f[0][0]*B.f[0][0] + A.f[0][1]*B.f[1][0]; C.f[0][1] = A.f[0][0]*B.f[0][1] + A.f[0][1]*B.f[1][1]; C.f[1][0] = A.f[1][0]*B.f[0][0] + A.f[1][1]*B.f[1][0]; C.f[1][1] = A.f[1][0]*B.f[0][1] + A.f[1][1]*B.f[1][1]; return C; } //분할정복 기법으로 구현한 피보나치 수열 구하는 함수 Matrix2x2 Matrix_Power(Matrix2x2 A, int n) { if (n > 1) //n이 1보다 클때 { //짝수일경우 반으로 나눠서 Matrix_Power 재귀 호출 A = Matrix_Power(A, n / 2); A = mulitply(A, A);// A^n = A^(n/2) * A^(n/2) if (n & 1) //n이 홀수일 경우 { Matrix2x2 F1 = { 1,1,1,0 }; /*행렬 {1,1} {1,0} 을 곱하여 나누기 연산으로 버려진 1을 보완 */ A = mulitply(A, F1); } } return A; } int main() { int n; cin >> n; Matrix2x2 F1 = { 1,1,1,0 }; F1 = Matrix_Power(F1, n); cout << n<<"번째 피보나치 수" << F1.f[0][1] << endl; }
반응형