피보나치 수열

2024. 10. 30. 03:25Algorithms

피보나치 수열(Fibonacci Sequence)은 수학과 알고리즘에서 자주 등장하는 유명한 수열로, 각 항이 그 이전 두 항의 합으로 이루어진 수열입니다. 이 수열은 재귀적인 특성과 수학적 규칙성을 가지고 있어, 알고리즘 기초에서 필수적으로 다루어지며 효율적인 코드 작성을 배우는 데에도 큰 도움이 됩니다.

이 포스팅에서는 피보나치 수열의 기본 개념, 수학적 정의, 다양한 구현 방식과 그 차이점에 대해 알아보겠습니다.


1. 피보나치 수열의 개념

피보나치 수열은 0과 1에서 시작하여, 이전 두 수의 합으로 다음 수를 구하는 수열입니다. 수열은 다음과 같은 규칙을 따릅니다.

  • 수열의 첫 번째 항은 0, 두 번째 항은 1로 시작합니다.
  • 이후 각 항은 이전 두 항의 합으로 정의됩니다.

수열 예시

피보나치 수열의 처음 몇 항은 다음과 같습니다:

  • 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

피보나치 수열의 n번째 항은 다음과 같이 정의할 수 있습니다.


2. 피보나치 수열의 활용 사례

피보나치 수열은 수학적 흥미 외에도 다양한 실용적인 용도로 사용됩니다.

  • 자연의 패턴: 나선형 패턴이나 꽃잎의 배치 등에서 피보나치 수가 나타납니다.
  • 프로그래밍 : 알고리즘 최적화, 점화식 활용, 메모이제이션 기법 등에 활용됩니다.
  • 금융 분야: 피보나치 비율은 주식 시장 분석에서 트렌드와 가격의 변화를 예측하는 데 사용됩니다.

3. 피보나치 수열의 구현 방법

피보나치 수열을 코드로 구현하는 다양한 방법을 살펴보겠습니다.

3.1 재귀 방식

가장 기본적인 방법은 재귀를 사용하여 구현하는 것입니다. 각 함수 호출마다 피보나치 수를 계산하기 위해 다시 두 개의 하위 문제를 호출합니다.

using System;

public class Fibonacci
{
    public static int FibonacciRecursive(int n)
    {
        if (n <= 1)
            return n; // n이 0 또는 1일 때 n 반환
        return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
    }

    public static void Main()
    {
        int n = 10; // 예시: 10번째 피보나치 수 계산
        Console.WriteLine($"Fibonacci({n}) = {FibonacciRecursive(n)}");
    }
}

장단점

  • 장점: 코드가 직관적이고 이해하기 쉽습니다.
  • 단점: 각 호출마다 중복 계산이 발생하여, 시간이 지남에 따라 지수적으로 계산량이 증가합니다(O(2^n)).

3.2 동적 프로그래밍 방식 (메모이제이션)

재귀 방식의 비효율성을 줄이기 위해, 이전에 계산한 값을 저장해두고 다시 계산하지 않도록 메모이제이션 기법을 사용합니다.

using System;
using System.Collections.Generic;

public class Fibonacci
{
    private static Dictionary<int, int> memo = new Dictionary<int, int>();

    public static int FibonacciMemo(int n)
    {
        if (n <= 1)
            return n;
        if (memo.ContainsKey(n))
            return memo[n];
        
        memo[n] = FibonacciMemo(n - 1) + FibonacciMemo(n - 2); // 메모에 저장
        return memo[n];
    }

    public static void Main()
    {
        int n = 10;
        Console.WriteLine($"Fibonacci({n}) = {FibonacciMemo(n)}");
    }
}

장단점

  • 장점: 중복 계산을 줄여 시간 복잡도가 O(n)로 감소합니다.
  • 단점: 추가적인 메모리 공간이 필요합니다.

3.3 반복문 방식

반복문을 사용하여 구현하면 재귀 방식보다 메모리 사용량이 줄어들며, 시간 복잡도도 O(n)으로 효율적입니다.

using System;

public class Fibonacci
{
    public static int FibonacciIterative(int n)
    {
        if (n <= 1)
            return n;

        int a = 0, b = 1, c = 0;
        for (int i = 2; i <= n; i++)
        {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }

    public static void Main()
    {
        int n = 10;
        Console.WriteLine($"Fibonacci({n}) = {FibonacciIterative(n)}");
    }
}

장단점

  • 장점: 메모리와 시간이 모두 효율적입니다.
  • 단점: 코드가 재귀보다 덜 직관적일 수 있습니다.

3.4 행렬을 이용한 방법

피보나치 수열을 행렬 연산으로 표현할 수 있으며, 이를 통해 O(log n)의 시간 복잡도로 계산할 수 있습니다. 이 방법은 매우 큰 피보나치 수를 빠르게 구해야 할 때 유용합니다.

using System;

public class Fibonacci
{
    public static int FibonacciMatrix(int n)
    {
        if (n <= 1)
            return n;

        int[,] matrix = { { 1, 1 }, { 1, 0 } };
        Power(matrix, n - 1);

        return matrix[0, 0];
    }

    private static void Power(int[,] matrix, int n)
    {
        if (n <= 1)
            return;

        Power(matrix, n / 2);
        Multiply(matrix, matrix);

        if (n % 2 != 0)
        {
            int[,] fibMatrix = { { 1, 1 }, { 1, 0 } };
            Multiply(matrix, fibMatrix);
        }
    }

    private static void Multiply(int[,] matrix, int[,] other)
    {
        int a = matrix[0, 0] * other[0, 0] + matrix[0, 1] * other[1, 0];
        int b = matrix[0, 0] * other[0, 1] + matrix[0, 1] * other[1, 1];
        int c = matrix[1, 0] * other[0, 0] + matrix[1, 1] * other[1, 0];
        int d = matrix[1, 0] * other[0, 1] + matrix[1, 1] * other[1, 1];

        matrix[0, 0] = a;
        matrix[0, 1] = b;
        matrix[1, 0] = c;
        matrix[1, 1] = d;
    }

    public static void Main()
    {
        int n = 10;
        Console.WriteLine($"Fibonacci({n}) = {FibonacciMatrix(n)}");
    }
}

장단점

  • 장점: 빠른 계산 속도 (O(log n))로 매우 큰 수를 다룰 때 효과적입니다.
  • 단점: 구현이 복잡하여 이해하기 어려울 수 있습니다.

피보나치 수열은 단순해 보이지만, 여러 가지로 구현할 수 있으며 각 방법은 속도와 메모리 효율성에서 차이를 보입니다. 기본적인 재귀 방식에서부터 메모이제이션, 반복문, 그리고 행렬 방법까지 다양한 구현을 통해 시간 복잡도와 메모리 사용량을 최적화할 수 있습니다.

피보나치 수열을 구현해 보며 알고리즘의 효율성을 고려하는 연습을 해보세요. 효율적인 알고리즘 작성은 문제 해결 능력을 향상시키는 데 큰 도움이 될 것입니다.

'Algorithms' 카테고리의 다른 글

문자열 알고리즘  (2) 2024.10.30
최장 공통 부분 수열 알고리즘  (1) 2024.10.30
탐욕 알고리즘  (1) 2024.10.27
크루스칼 알고리즘  (2) 2024.10.24
프림 알고리즘  (0) 2024.10.24