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
Input: N = 6
Output: 8
Approach:
- The Matrix Exponentiation Method is already discussed before. The Doubling Method can be seen as an improvement to the matrix exponentiation method to find the N-th Fibonacci number although it doesn’t use matrix multiplication itself.
- The Fibonacci recursive sequence is given by
F(n+1) = F(n) + F(n-1)
- The Matrix Exponentiation method uses the following formula
![Rendered by QuickLaTeX.com \[ \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n = \begin{bmatrix} F_{n+1} & F_n \\ F_n & F_{n-1} \end{bmatrix} \]](https://www.geeksforgeeks.org/wp-content/ql-cache/quicklatex.com-186069d23e4f57ec324673203ac3f1f1_l3.png)
- The method involves costly matrix multiplication and moreover Fn is redundantly computed twice.
On the other hand, Fast Doubling Method is based on two basic formulas:
F(2n) = F(n)[2F(n+1) – F(n)]
F(2n + 1) = F(n)2+F(n+1)2
- Here is a short explanation of the above results:
Start with:
F(n+1) = F(n) + F(n-1) &
F(n) = F(n)
It can be rewritten in the matrix form as:
![Rendered by QuickLaTeX.com \[ \begin{bmatrix} F(n+1) \\ F(n) \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix} \begin{bmatrix} F(n) \\ F(n-1) \end{bmatrix} \] \[\quad\enspace= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^2 \begin{bmatrix} F(n-1) \\ F(n-2) \end{bmatrix} \] \quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\enspace\enspace\thinspace......\\ \[= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n \begin{bmatrix} F(1) \\ F(0) \end{bmatrix} \]](https://www.geeksforgeeks.org/wp-content/ql-cache/quicklatex.com-002cb7e127a7ba7841e8be9e4450729b_l3.png)
For doubling, we just plug in “2n” into the formula:
![Rendered by QuickLaTeX.com \[ \begin{bmatrix} F(2n+1) \\ F(2n) \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{2n} \begin{bmatrix} F(1) \\ F(0) \end{bmatrix} \] \[\quad\enspace= \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n \begin{bmatrix} F(1) \\ F(0) \end{bmatrix} \] \[\quad\enspace= \begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix} \begin{bmatrix} F(n+1) & F(n) \\ F(n) & F(n-1) \end{bmatrix} \begin{bmatrix} F(1) \\ F(0) \end{bmatrix} \] \[\quad\enspace= \begin{bmatrix} F(n+1)^2 + F(n)^2 \\ F(n)F(n+1) + F(n)F(n-1) \end{bmatrix} \]](https://www.geeksforgeeks.org/wp-content/ql-cache/quicklatex.com-b4dd6b196b753fc449bd1a844e9c7f81_l3.png)
Substituting F(n-1) = F(n+1)- F(n) and after simplification we get,
![Rendered by QuickLaTeX.com \[ \begin{bmatrix} F(2n+1) \\ F(2n) \end{bmatrix} = \begin{bmatrix} F(n+1)^2 + F(n)^2 \\ 2F(n+1)F(n) - F(n)^2 \end{bmatrix} \]](https://www.geeksforgeeks.org/wp-content/ql-cache/quicklatex.com-f52ffc4370e47ab14b6eb72781a0c90f_l3.png)
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
int a, b, c, d;
#define MOD 1000000007
void FastDoubling( int n, int res[])
{
if (n == 0) {
res[0] = 0;
res[1] = 1;
return ;
}
FastDoubling((n / 2), res);
a = res[0];
b = res[1];
c = 2 * b - a;
if (c < 0)
c += MOD;
c = (a * c) % MOD;
d = (a * a + b * b) % MOD;
if (n % 2 == 0) {
res[0] = c;
res[1] = d;
}
else {
res[0] = d;
res[1] = c + d;
}
}
int main()
{
int N = 6;
int res[2] = { 0 };
FastDoubling(N, res);
cout << res[0] << "\n" ;
return 0;
}
|
Java
class GFG{
static void FastDoubling( int n, int []res)
{
int a, b, c, d;
int MOD = 1000000007 ;
if (n == 0 )
{
res[ 0 ] = 0 ;
res[ 1 ] = 1 ;
return ;
}
FastDoubling((n / 2 ), res);
a = res[ 0 ];
b = res[ 1 ];
c = 2 * b - a;
if (c < 0 )
c += MOD;
c = (a * c) % MOD;
d = (a * a + b * b) % MOD;
if (n % 2 == 0 )
{
res[ 0 ] = c;
res[ 1 ] = d;
}
else
{
res[ 0 ] = d;
res[ 1 ] = c + d;
}
}
public static void main(String []args)
{
int N = 6 ;
int res[] = new int [ 2 ];
FastDoubling(N, res);
System.out.print(res[ 0 ]);
}
}
|
Python3
MOD = 1000000007
def FastDoubling(n, res):
if (n = = 0 ):
res[ 0 ] = 0
res[ 1 ] = 1
return
FastDoubling((n / / 2 ), res)
a = res[ 0 ]
b = res[ 1 ]
c = 2 * b - a
if (c < 0 ):
c + = MOD
c = (a * c) % MOD
d = (a * a + b * b) % MOD
if (n % 2 = = 0 ):
res[ 0 ] = c
res[ 1 ] = d
else :
res[ 0 ] = d
res[ 1 ] = c + d
N = 6
res = [ 0 ] * 2
FastDoubling(N, res)
print (res[ 0 ])
|
C#
using System;
class GFG{
static void FastDoubling( int n, int []res)
{
int a, b, c, d;
int MOD = 1000000007;
if (n == 0)
{
res[0] = 0;
res[1] = 1;
return ;
}
FastDoubling((n / 2), res);
a = res[0];
b = res[1];
c = 2 * b - a;
if (c < 0)
c += MOD;
c = (a * c) % MOD;
d = (a * a + b * b) % MOD;
if (n % 2 == 0)
{
res[0] = c;
res[1] = d;
}
else
{
res[0] = d;
res[1] = c + d;
}
}
public static void Main()
{
int N = 6;
int []res = new int [2];
FastDoubling(N, res);
Console.Write(res[0]);
}
}
|
Javascript
<script>
let a, b, c, d;
let MOD = 1000000007;
function FastDoubling(n, res)
{
if (n == 0) {
res[0] = 0;
res[1] = 1;
return ;
}
FastDoubling(parseInt(n / 2, 10), res);
a = res[0];
b = res[1];
c = 2 * b - a;
if (c < 0)
c += MOD;
c = (a * c) % MOD;
d = (a * a + b * b) % MOD;
if (n % 2 == 0) {
res[0] = c;
res[1] = d;
}
else {
res[0] = d;
res[1] = c + d;
}
}
let N = 6;
let res = new Array(2);
res.fill(0);
FastDoubling(N, res);
document.write(res[0]);
</script>
|
Time Complexity: Repeated squaring reduces time from linear to logarithmic . Hence, with constant time arithmetic, the time complexity is O(log n).
Auxiliary Space: O(n).
Iterative Version
We can implement iterative version of above method, by initializing array with two elements f = [F(0), F(1)] = [0, 1] and iteratively constructing F(n), on every step we will transform f into [F(2i), F(2i+1)] or [F(2i+1), F(2i+2)] , where i corresponds to the current value of i stored in f = [F(i), F(i+1)].
Approach:
- Create array with two elements f = [0, 1] , which represents [F(0), F(1)] .
- For finding F(n), iterate over binary representation of n from left to right, let kth bit from left be bk .
- Iteratively apply the below steps for all bits in n .
- Using bk we will decide whether to transform f = [F(i), F(i+1)] into [F(2i), F(2i+1)] or [F(2i+1), F(2i+2)] .
if bk == 0:
f = [F(2i), F(2i+1)] = [F(i){2F(i+1)-F(i)}, F(i+1)2+F(i)2]
if bk == 1:
f = [F(2i+1), F(2i+2)] = [F(i+1)2+F(i)2, F(i+1){2F(i)+F(i+1)}]
where,
F(i) and F(i+1) are current values stored in f.
Example:
for n = 13 = (1101)2
b = 1 1 0 1
[F(0), F(1)] -> [F(1), F(2)] -> [F(3), F(4)] -> [F(6), F(7)] -> [F(13), F(14)]
[0, 1] -> [1, 1] -> [2, 3] -> [8, 13] -> [233, 377]
Below is the implementation of the above approach:
C++
#include <bitset>
#include <iostream>
#include <string>
using namespace std;
string decimal_to_bin( int n)
{
string bin = bitset< sizeof ( int ) * 8>(n).to_string();
auto loc = bin.find( '1' );
if (loc != string::npos)
return bin.substr(loc);
return "0" ;
}
long long fastfib( int n)
{
string bin_of_n
= decimal_to_bin(n);
long long f[] = { 0, 1 };
for ( auto b : bin_of_n) {
long long f2i1
= f[1] * f[1] + f[0] * f[0];
long long f2i = f[0] * (2 * f[1] - f[0]);
if (b == '0' ) {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i1 + f2i;
}
}
return f[0];
}
int main()
{
int n = 13;
long long fib = fastfib(n);
cout << "F(" << n << ") = " << fib << "\n" ;
}
|
Java
import java.io.*;
class GFG {
static String convertToBinary( int x)
{
int bin = 0 ;
int rem, i = 1 , step = 1 ;
while (x != 0 ) {
rem = x % 2 ;
x = x / 2 ;
bin = bin + rem * i;
i = i * 10 ;
}
return Integer.toString(bin);
}
static String decimal_to_bin( int n)
{
String bin = convertToBinary(n);
int loc = bin.indexOf( "1" );
if (loc != - 1 ) {
return bin.substring(loc);
}
return "0" ;
}
static int fastfib( int n)
{
String bin_of_n
= decimal_to_bin(n);
int [] f = { 0 , 1 };
for ( int i = 0 ; i < bin_of_n.length(); i++) {
int b = bin_of_n.charAt(i);
int f2i1 = f[ 1 ] * f[ 1 ] + f[ 0 ] * f[ 0 ];
int f2i = f[ 0 ] * ( 2 * f[ 1 ] - f[ 0 ]);
if (b == '0' ) {
f[ 0 ] = f2i;
f[ 1 ] = f2i1;
}
else {
f[ 0 ] = f2i1;
f[ 1 ] = f2i1 + f2i;
}
}
return f[ 0 ];
}
public static void main(String[] args)
{
int n = 13 ;
int fib = fastfib(n);
System.out.print( "F(" + n + ") = " + fib);
}
}
|
Python3
def fastfib(n):
bin_of_n = bin (n)[ 2 :]
f = [ 0 , 1 ]
for b in bin_of_n:
f2i1 = f[ 1 ] * * 2 + f[ 0 ] * * 2
f2i = f[ 0 ] * ( 2 * f[ 1 ] - f[ 0 ])
if b = = '0' :
f[ 0 ], f[ 1 ] = f2i, f2i1
else :
f[ 0 ], f[ 1 ] = f2i1, f2i1 + f2i
return f[ 0 ]
n = 13
fib = fastfib(n)
print (f 'F({n}) =' , fib)
|
C#
using System;
using System.Collections.Generic;
public class GFG {
public static string convertToBinary( int x)
{
int bin = 0;
int rem, i = 1, step = 1;
while (x != 0) {
rem = x % 2;
x = x / 2;
bin = bin + rem * i;
i = i * 10;
}
return bin.ToString();
}
public static string decimal_to_bin( int n)
{
string bin = convertToBinary(n);
int loc = bin.IndexOf( '1' );
if (loc != -1) {
return bin.Substring(loc);
}
return "0" ;
}
public static int fastfib( int n)
{
string bin_of_n
= decimal_to_bin(n);
int [] f = { 0, 1 };
for ( int i = 0; i < bin_of_n.Length; i++) {
int b = bin_of_n[i];
int f2i1 = f[1] * f[1] + f[0] * f[0];
int f2i = f[0] * (2 * f[1] - f[0]);
if (b == '0' ) {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i1 + f2i;
}
}
return f[0];
}
static public void Main()
{
int n = 13;
int fib = fastfib(n);
Console.WriteLine( "F(" + n + ") = " + fib);
}
}
|
Javascript
function convertToBinary(x) {
let bin = 0;
let rem, i = 1, step = 1;
while (x != 0) {
rem = x % 2;
x = parseInt(x / 2);
bin = bin + rem * i;
i = i * 10;
}
return bin.toString();
}
function decimal_to_bin(n)
{
let bin = convertToBinary(n);
let loc = bin.indexOf( '1' );
if (loc != -1)
return bin.substring(loc);
return "0" ;
}
function fastfib(n)
{
let bin_of_n = decimal_to_bin(n);
let f = [0, 1];
for (let i = 0; i < bin_of_n.length; i++){
let b = bin_of_n[i];
let f2i1 = f[1] * f[1] + f[0] * f[0];
let f2i = f[0] * (2 * f[1] - f[0]);
if (b == '0' ) {
f[0] = f2i;
f[1] = f2i1;
}
else {
f[0] = f2i1;
f[1] = f2i1 + f2i;
}
}
return f[0];
}
let n = 13;
let fib = fastfib(n);
console.log( "F(" ,n, ") =" , fib);
|
Time Complexity: We are iterating over a binary string of length n and doing constant time arithmetic operations for each digit, so the time complexity is O(n).
Auxiliary Space: We are storing two elements in f (which is a constant cost), and the binary representation of the number (which has a cost of O(n)) so space complexity is O(n). We could reduce this down to O(1) if we didn’t convert the number to a string, but instead used the bits of the number to iterate through .