Nth Fibonacci Number

Nth Fibonacci Number

Fibonacci Numbers represent a series of numbers where the nth number is the sum of the previous two elements starting from 0 and 1 as the first two elements. Since ancient times mathematicians around the world have mentioned this series in their work. Whether it is the spiral of a spider web or the arrangement of celestial bodies, hints of the Fibonacci Series can be found everywhere.

No alt text provided for this image

In programming, there are many ways to find nth number of the Fibonacci Series and in this article, we will go through some of the cool ones. Mainly

  1. Iterative Method
  2. Recursive Method
  3. Memoized Recursive Method
  4. Matrix Multiplication Method
  5. Matrix Exponentiation Method
  6. Formula for Nth Fibonacci Number

Iterative Method

The simplest approach to find the Nth Fibonacci Number would be to run a loop from 0 to n and keep track of the last and the second last numbers as we can obtain the next element in the series by adding the last two elements.

No alt text provided for this image

Following is an implementation of this approach in JavaScript:

function fibonacci(n) 
? if (n === 0 || n === 1) return n;? ? // handling edge cases
? let prevNum = 0;? ??
? let currNum = 1;
? for (let i = 0; i <= n - 2; i++) {
? ? let temp = prevNum;? ??
? ? prevNum = currNum;
? ? currNum = currNum + temp;
? ? //? OR
? ? // currNum = currNum + prevNum;
? ? // prevNum = currNum - prevNum;
? ? // OR
? ? // [prevNum, currNum] = [currNum, currNum + prevNum]
? }
? return currNum;
}        

Time Complexity: O(n)

Space Complexity: O(1)

Recursive Method

We can find the Nth Fibonacci Number by recursively adding the last two numbers in series. Recursion is used to break down the code into smaller and repetitive problems, making the code easier to understand. This is also the reason why recursion is emphasized by?Functional Programming.

Let's solve this recursive problem step by step. The?first step?would be to identify the?base cases. In this scenario, our base cases would be?F(0)?and?F(1). If 0 or 1 is passed as an argument to our function, then we can simply return the number itself as?F(0) = 0?and?F(1) = 1. Now the?second step?is to write a recursive statement. We know that?F(n) = F(n-1) + F(n-2), so for any F(n) function call we would return F(n-1) + F(n-2) as the recursive statement.

Let's see the code before moving forward,

function fibonacci(n) 
? if (n === 0 || n === 1) return n;
? return fibonacci(n - 1) + fibonacci(n - 2);
}
OR
// function fibonacci(n){
//? ?return n === 0 || n === 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
// }

        

Time Complexity: O(n)

Space Complexity: O(1)

Calling?fibonacci(5)?would generate the following call stack:

No alt text provided for this image

And the corresponding return values would be:

No alt text provided for this image

We can improve on the time complexity of this approach by using the memoization technique.

Memoization Method

It is an improvement on the previous method. As you might have noticed in the recursion call stack some of the function calls were made more than once. For Example, the function call F(3) is made twice which means that F(3) is calculated at two separate occasions.

No alt text provided for this image

To prevent these redundant calls, we can store their return values in our code. This process of storing return values is called?memoization?and it is a cornerstone of?Dynamic Programming.

Memoized code is as follows:

function fibonacci(n, memo = {}) {? 
? // initialize memo as empty object
? if (n in memo) return memo[n];? ?
? // if key 'n' exists in the memo, then return its value
? if (n === 0 || n === 1) return n;
? // store return values in memo object and pass it down to function calls?
? return (memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo));? ??
? // OR
? // return n === memo
? //? ?? memo[n]
? //? ?: n === 0 || n === 1
? //? ?? n
? //? ?: (memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo));
}
?        

Time Complexity:?O(n)

Space Complexity:?O(n)

By maintaining a memo object we are able to bring down the time complexity to O(n). Here is the call stack and memo object for the above code:

No alt text provided for this image

Matrix Multiplication Method

Let's start by establishing the equation we will be using in this method.

No alt text provided for this image

Here is a?YouTube video?deriving this equation.

From the above equation, it is clear that if we multiply the matrix?[[1, 1], [1, 0]],?n-1?times then the element at index?[0][0]?will be the?nth?Fibonacci number. Check this code:

// returns product of two 2x2 square matrice
function matrixMultiply(a, b) {
? const len = a.length;? ??
? let res = [
? ? [0, 0],
? ? [0, 0]
? ];
? for (let i = 0; i < len; i++) {
? ? for (let j = 0; j < len; j++) {
? ? ? for (let k = 0; k < len; k++) {
? ? ? ? res[i][j] += a[i][k] * b[k][j];
? ? ? }
? ? }
? }
? return res;
}
// returns the matrix [[1, 1], [1, 0]] raised to nth power
// note- we will improve this part with exponentiation
function matrixExponent(n) {
? const k = [
? ? [1, 1],
? ? [1, 0]
? ];
? let res = k;
? for (let i = 0; i < n; i++) {
? ? res = matrixMultiply(res, k);
? }
? return res;
}
function fibonacci(n) {
? if (n === 0 || n === 1) return 1;
? let res = matrixExponent(n - 1);
? return res[0][0];? ? // [0][0] index is our answer
}        

Time Complexity:?O(n)

Space Complexity:?O(1)

Currently, our code finds power of the matrix in O(n) time, we will improve it in the next section.

Matrix Exponentiation Method

Our target is to find the power of a matrix under O(logn) time. This can be achieved using matrix exponentiation. I would recommend you learn binary exponentiation before going through this section.

Let's jump into the code first!

function matrixMultiply(a, b) 
? const len = a.length;
? let res = [
? ? [0, 0],
? ? [0, 0]
? ];
? for (let i = 0; i < len; i++) {
? ? for (let j = 0; j < len; j++) {
? ? ? for (let k = 0; k < len; k++) {
? ? ? ? res[i][j] += a[i][k] * b[k][j];
? ? ? }
? ? }
? }
? return res;
}
// finds nth power of matrix under logn time
function matrixExponent(n) {
? if (n === 0) {
? ? return [
? ? ? [1, 0],
? ? ? [0, 1]
? ? ];
? }
? const k = [
? ? [1, 1],
? ? [1, 0]
? ];
? if (n === 1) return k;
? let temp = matrixExponent(Math.floor(n / 2));
? let res = matrixMultiply(temp, temp);
? if (n % 2 === 1) res = matrixMultiply(res, k);
? return res;
}
function fibonacci(n) {
? if (n === 0 || n === 1) return n;
? let res = matrixExponent(n - 1);
? return res[0][0];
}        

Time Complexity:?O(logn)

Space Complexity:?O(logn)

Matrix exponentiation is easy to understand if you compare it with corresponding binary exponentiation. Note that in this case the value of the matrix was constant, so it wasn't passed as an argument.

No alt text provided for this image

Now that we have gone through all sorts of implementations, let's check out the direct formula to find the nth Fibonacci number.

Formula for Nth Fibonacci Number

We can find the nth Fibonacci number directly using the following formula:

No alt text provided for this image

Here is the code for this method:

function fibonacci(n) 
? const phi = (1 + Math.sqrt(5)) / 2;? ??
? return Math.round(Math.pow(phi, n) / Math.sqrt(5));
}
        

Time Complexity:?O(logn)

Space Complexity:?O(1)

Note- This formula involves working with fractional numbers, which is something coding languages are not very good at (try executing 0.1 + 0.2 in your console).

Congratulations! you have made it to the end of this article. Do let me know what was your favorite approach in the comments below.

要查看或添加评论,请登录

Divyansh Sareen的更多文章

  • Matrix Traversal Methods

    Matrix Traversal Methods

    Introduction Matrix is defined as a group of similar or different data values arranged in rows and columns. In…

  • JavaScript Objects and Prototypes

    JavaScript Objects and Prototypes

    JavaScript is an Object Oriented Programming Language. But in a world where OOPs and classes go hand in hand…

    2 条评论

社区洞察

其他会员也浏览了