The Fibonacci series is a popular mathematical sequence that appears in various fields, from computer science to nature. The sequence starts with two numbers, 0 and 1, and every subsequent number is the sum of the previous two numbers.

In this article, we will explore three different ways to compute the Fibonacci series in C#: using Recursion, memorization, and the Optimal Programming approach.

Fibonacci Using Recursion

The recursive approach is a simple and intuitive way to compute the Fibonacci sequence. However, it’s not the most efficient, as we’ll see in the complexity analysis.

public static int FibonacciRecursive(int n)
{
    if (n <= 1) return n; // Base case
    return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2); //Sub problem
}

Output

fibonacci number

Fibonacci Using Memoization

Memoization is a technique that optimizes the recursive solution by storing previously computed results. This avoids the redundant calculations seen in the basic recursive approach.

public static int FibonacciMemoization(int n, Dictionary<int, int> memo = null)
{
    if (memo == null) memo = new Dictionary<int, int>(); //Used to store older results to avoid redundant recursion calls
    if (n <= 1) return n; //Base case   
    if (!memo.ContainsKey(n)) // check if already result is present. If not, then only compute
        memo[n] = FibonacciMemoization(n - 1, memo) + FibonacciMemoization(n - 2, memo); //Sub problem    
    return memo[n];
}

Output

fibonacci number

Fibonacci Using an Optimal Approach

In this approach for Fibonacci, as we only need the last two Fibonacci values to calculate the next one so by storing just these two values (instead of the entire sequence), we iteratively compute the result in a loop. This reduces both time complexity to O(n) and space complexity to O(1).

public static int FibonacciOptimal(int n)
{
    if (n <= 1) return n;
    int prev1 = 0, prev2 = 1; //Variables to store last two results
    for (int i = 2; i <= n; i++)
    {
        int current = prev1 + prev2;
        prev1 = prev2;
        prev2 = current;
    }
    return prev2;
}

Output

fibonacci number

Comparing the Approaches

Let’s summarize the time and space complexities of the three approaches.

Implementation Approach Time Complexity Space Complexity
Recursion O(2^n) O(n)
Memoization O(n) O(n)
Optimal O(n) O(1)

Complete Code

using System;
using System.Collections.Generic;
namespace FibonacciSeries
{
    internal class Program
    {
        static void Main(string[] args)
        {
            int n = 10; // calculate nth fibonnacci 
          
            Console.WriteLine("Fibonacci number using recursion for n = " + n + ":");
            Console.Write(FibonacciRecursive(n) + " "); 

            Console.WriteLine("\n\nFibonacci number using memoization for n = " + n + ":");
            Console.Write(FibonacciMemoization(n) + " ");  

            Console.WriteLine("\n\nFibonacci number using the optimal approach for n = " + n + ":");
            Console.Write(FibonacciOptimal(n) + " ");  
            Console.ReadLine();         
        }

        public static int FibonacciRecursive(int n)
        {
            if (n <= 1) return n;
            return FibonacciRecursive(n - 1) + FibonacciRecursive(n - 2);
        }

        public static int FibonacciMemoization(int n, Dictionary<int, int> memo = null)
        {
            if (memo == null) memo = new Dictionary<int, int>();
            if (n <= 1) return n;
            if (!memo.ContainsKey(n))
                memo[n] = FibonacciMemoization(n - 1, memo) + FibonacciMemoization(n - 2, memo);
            return memo[n];
        }

        public static int FibonacciOptimal(int n)
        {
            if (n <= 1) return n;
            int prev1 = 0, prev2 = 1;
            for (int i = 2; i <= n; i++)
            {
                int current = prev1 + prev2;
                prev1 = prev2;
                prev2 = current;
            }
            return prev2;
        }
    }   
}

Output