Given an integer n, the task is to find the n-th Fibonacci numbers.
Examples:Â
Input: n = 3Â
Output: 2Â
Explanation:Â
F(1) = 1, F(2) = 1
F(3) = F(1) + F(2) = 2
Hence, the 3rd Fibonacci number is 2.Input: n = 6
Output: 8
Explanation:
F(1) = 1, F(2) = 1
F(3) = F(1) + F(2) = 2
F(4) = F(2) + F(3) = 3
F(5) = F(3) + F(4) = 5
F(6) = F(4) + F(5) = 8
Hence, the 6th Fibonacci number is 8.
The Fibonacci sequence follows the recurrence relation: F(n)=F(n−1)+F(n−2). A straightforward recursive solution repeatedly solves the same subproblems, resulting in exponential time complexity. Dynamic programming improves this to O(n) time. The Fast Doubling Method further reduces the time complexity to O(log n).
Table of Content
Using Recursive Fast Doubling - O(log n) Time and O(log n) Space
The idea is to recursively compute F(k) and F(k + 1), where k = n / 2, and use the Fast Doubling formulas to obtain F(n) directly.
Why does this approach work?
The approach is based on the following Fast Doubling formulas:
- Even index: F(2n) = F(n) * (2F(n + 1) - F(n))
- Odd index: F(2n + 1) = F(n) ^ 2 + F(n + 1) ^ 2
These identities allow us to compute the Fibonacci number for a larger index directly from two smaller Fibonacci numbers, reducing the problem size by half in each recursive call. This is why the Fast Doubling Method runs in O(log n) time.
Working of Approach:
- If n = 0, return F(0) = 0 and F(1) = 1.
- Recursively compute F(k) and F(k + 1), where k = n / 2.
- Use the Fast Doubling formulas to compute F(2k) and F(2k + 1).
- If n is even, return F(2k); otherwise, return F(2k + 1).
Let us consider n = 6.
- Firstly, fastDoubling(6) calls fastDoubling(3) to compute F(3) and F(4).
- Then, fastDoubling(3) calls fastDoubling(1), which further calls fastDoubling(0). The base case returns (0, 1).
- Using (0, 1), fastDoubling(1) computes (1, 1), representing (F(1), F(2)).
- Using (1, 1), fastDoubling(3) computes (2, 3), representing (F(3), F(4)).
- Using (2, 3), fastDoubling(6) computes (8, 13), representing (F(6), F(7)).
- Finally, the first value of the pair, 8, is returned as the 6th Fibonacci number.
#include <iostream>
using namespace std;
const int MOD = 1000000007;
// Function returns {F(n), F(n + 1)}
pair<int, int> fastDoubling(int n)
{
// Base Case
if (n == 0)
return {0, 1};
// Recursively find F(k) and F(k + 1)
auto p = fastDoubling(n / 2);
int a = p.first;
int b = p.second;
// Compute F(2k)
int c = (1LL * a * ((2LL * b % MOD - a + MOD) % MOD)) % MOD;
// Compute F(2k + 1)
int d = (1LL * a * a + 1LL * b * b) % MOD;
// If n is even
if (n % 2 == 0)
return {c, d};
// If n is odd
return {d, (c + d) % MOD};
}
int nthFibonacci(int n)
{
// Return the n-th Fibonacci number
return fastDoubling(n).first;
}
int main()
{
int n = 6;
cout << "F(" << n << ") = " << nthFibonacci(n) << "\n";
return 0;
}
import java.util.Arrays;
public class GFG {
static final int MOD = 1000000007;
// Function returns {F(n), F(n + 1)}
static int[] fastDoubling(int n)
{
// Base Case
if (n == 0)
return new int[] { 0, 1 };
// Recursively find F(k) and F(k + 1)
int[] p = fastDoubling(n / 2);
int a = p[0];
int b = p[1];
// Compute F(2k)
int c = (int)((1L * a
* ((2L * b % MOD - a + MOD) % MOD))
% MOD);
// Compute F(2k + 1)
int d = (int)((1L * a * a + 1L * b * b) % MOD);
// If n is even
if (n % 2 == 0)
return new int[] { c, d };
// If n is odd
return new int[] { d, (c + d) % MOD };
}
static int nthFibonacci(int n)
{
// Return the n-th Fibonacci number
return fastDoubling(n)[0];
}
public static void main(String[] args)
{
int n = 6;
System.out.println("F(" + n
+ ") = " + nthFibonacci(n));
}
}
MOD = 1000000007
# Function returns {F(n), F(n + 1)}
def fastDoubling(n):
# Base Case
if n == 0:
return (0, 1)
# Recursively find F(k) and F(k + 1)
p = fastDoubling(n // 2)
a = p[0]
b = p[1]
# Compute F(2k)
c = (a * ((2 * b % MOD - a + MOD) % MOD)) % MOD
# Compute F(2k + 1)
d = (a * a + b * b) % MOD
# If n is even
if n % 2 == 0:
return (c, d)
# If n is odd
return (d, (c + d) % MOD)
def nthFibonacci(n):
# Return the n-th Fibonacci number
return fastDoubling(n)[0]
if __name__ == "__main__":
n = 6
print(f"F({n}) = {nthFibonacci(n)}")
using System;
public class GFG {
const int MOD = 1000000007;
// Function returns {F(n), F(n + 1)}
static(int, int) FastDoubling(int n)
{
// Base Case
if (n == 0)
return (0, 1);
// Recursively find F(k) and F(k + 1)
var p = FastDoubling(n / 2);
int a = p.Item1;
int b = p.Item2;
// Compute F(2k)
int c = (int)((1L * a
* ((2L * b % MOD - a + MOD) % MOD))
% MOD);
// Compute F(2k + 1)
int d = (int)((1L * a * a + 1L * b * b) % MOD);
// If n is even
if (n % 2 == 0)
return (c, d);
// If n is odd
return (d, (c + d) % MOD);
}
static int NthFibonacci(int n)
{
// Return the n-th Fibonacci number
return FastDoubling(n).Item1;
}
public static void Main()
{
int n = 6;
Console.WriteLine("F(" + n
+ ") = " + NthFibonacci(n));
}
}
const MOD = 1000000007;
// Function returns {F(n), F(n + 1)}
function fastDoubling(n)
{
// Base Case
if (n === 0)
return [ 0, 1 ];
// Recursively find F(k) and F(k + 1)
const p = fastDoubling(Math.floor(n / 2));
let a = p[0];
let b = p[1];
// Compute F(2k)
let c = (BigInt(a)
* ((BigInt(2) * BigInt(b) % BigInt(MOD)
- BigInt(a) + BigInt(MOD))
% BigInt(MOD)))
% BigInt(MOD);
// Compute F(2k + 1)
let d = (BigInt(a) * BigInt(a) + BigInt(b) * BigInt(b))
% BigInt(MOD);
// If n is even
if (n % 2 === 0)
return [ Number(c), Number(d) ];
// If n is odd
return [ Number(d), (Number(c) + Number(d)) % MOD ];
}
function nthFibonacci(n)
{
// Return the n-th Fibonacci number
return fastDoubling(n)[0];
}
// Driver Code
const n = 6;
console.log(`F(${n}) = ${nthFibonacci(n)}`);
Output
F(6) = 8
Using Iterative Fast Doubling - O(log n) Time and O(log n) Space
The idea is to process the binary representation of N from left to right and use the Fast Doubling formulas to iteratively update two consecutive Fibonacci numbers. Since each bit is processed once, the algorithm computes the N-th Fibonacci number efficiently.
Why does this approach work?
The approach maintains the invariant that the array f = [F(i), F(i + 1)] always stores two consecutive Fibonacci numbers for the current index i.
For every bit of n:
- If the current bit is 0, we update f to [F(2i), F(2i + 1)].
- If the current bit is 1, we update f to [F(2i + 1), F(2i + 2)].
Since every binary digit of n is processed exactly once, after processing all bits, f[0] becomes F(n).
Working of Approach:
- Initialize f = [0, 1], representing F(0) and F(1).
- Convert N into its binary representation.
- Traverse the bits from left to right.
- Apply the Fast Doubling formulas to update f based on the current bit.
- After all bits are processed, return f[0].
Let us consider n = 6.
Binary representation of 6 is 110.
- Initial: f = [0, 1] -> (F(0), F(1))
- Bit = 1: f = [1, 1] -> (F(1), F(2))
- Bit = 1: f = [2, 3] -> (F(3), F(4))
- Bit = 0: f = [8, 13] -> (F(6), F(7))
Hence, the 6th Fibonacci number is 8.
#include <bitset>
#include <iostream>
#include <string>
using namespace std;
// Function to convert decimal number to binary string
string decimalToBinary(int n)
{
string bin = bitset<32>(n).to_string();
int pos = bin.find('1');
if (pos != string::npos)
return bin.substr(pos);
return "0";
}
// Function to find the N-th Fibonacci number
int nthFibonacci(int n)
{
string bits = decimalToBinary(n);
// f[0] = F(i), f[1] = F(i + 1)
int f[2] = {0, 1};
for (char bit : bits)
{
// Compute F(2i)
int f2i = 1LL * f[0] * (2 * f[1] - f[0]);
// Compute F(2i + 1)
int f2i1 = 1LL * f[0] * f[0] + 1LL * f[1] * f[1];
if (bit == '0')
{
f[0] = f2i;
f[1] = f2i1;
}
else
{
f[0] = f2i1;
f[1] = f2i + f2i1;
}
}
return f[0];
}
int main()
{
int n = 6;
cout << "F(" << n << ") = " << nthFibonacci(n);
return 0;
}
import java.util.Arrays;
public class GFG {
// Function to convert decimal number to binary string
public static String decimalToBinary(int n)
{
String bin = Integer.toBinaryString(n);
int pos = bin.indexOf('1');
if (pos != -1) {
return bin.substring(pos);
}
return "0";
}
// Function to find the N-th Fibonacci number
public static int nthFibonacci(int n)
{
String bits = decimalToBinary(n);
// f[0] = F(i), f[1] = F(i + 1)
int[] f = { 0, 1 };
for (char bit : bits.toCharArray()) {
// Compute F(2i)
int f2i = f[0] * (2 * f[1] - f[0]);
// Compute F(2i + 1)
int f2i1 = f[0] * f[0] + f[1] * f[1];
if (bit == '0') {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i + f2i1;
}
}
return f[0];
}
public static void main(String[] args)
{
int n = 6;
System.out.println("F(" + n
+ ") = " + nthFibonacci(n));
}
}
def decimalToBinary(n):
bin = bin(n)[2:]
pos = bin.find('1')
if pos != -1:
return bin[pos:]
return '0'
def nthFibonacci(n):
bits = decimalToBinary(n)
# f[0] = F(i), f[1] = F(i + 1)
f = [0, 1]
for bit in bits:
# Compute F(2i)
f2i = f[0] * (2 * f[1] - f[0])
# Compute F(2i + 1)
f2i1 = f[0] * f[0] + f[1] * f[1]
if bit == '0':
f[0] = f2i
f[1] = f2i1
else:
f[0] = f2i1
f[1] = f2i + f2i1
return f[0]
if __name__ == '__main__':
n = 6
print(f'F({n}) = {nthFibonacci(n)}')
using System;
public class GFG {
// Function to convert decimal number to binary string
public static string DecimalToBinary(int n)
{
string bin = Convert.ToString(n, 2);
int pos = bin.IndexOf('1');
if (pos != -1)
return bin.Substring(pos);
return "0";
}
// Function to find the N-th Fibonacci number
public static int NthFibonacci(int n)
{
string bits = DecimalToBinary(n);
// f[0] = F(i), f[1] = F(i + 1)
int[] f = { 0, 1 };
foreach(char bit in bits)
{
// Compute F(2i)
int f2i = f[0] * (2 * f[1] - f[0]);
// Compute F(2i + 1)
int f2i1 = f[0] * f[0] + f[1] * f[1];
if (bit == '0') {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i + f2i1;
}
}
return f[0];
}
public static void Main()
{
int n = 6;
Console.WriteLine("F(" + n
+ ") = " + NthFibonacci(n));
}
}
function decimalToBinary(n)
{
let bin = n.toString(2);
let pos = bin.indexOf("1");
if (pos !== -1) {
return bin.substring(pos);
}
return "0";
}
function nthFibonacci(n)
{
let bits = decimalToBinary(n);
// f[0] = F(i), f[1] = F(i + 1)
let f = [ 0, 1 ];
for (let bit of bits) {
// Compute F(2i)
let f2i = f[0] * (2 * f[1] - f[0]);
// Compute F(2i + 1)
let f2i1 = f[0] * f[0] + f[1] * f[1];
if (bit === "0") {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i + f2i1;
}
}
return f[0];
}
// Driver Code
let n = 6;
console.log(`F(${n}) = ${nthFibonacci(n)}`);
Output
F(6) = 8
Note: The auxiliary space can be reduced to O(1) by processing the bits of n directly instead of first converting it into a binary string.