Jacobsthal and Jacobsthal-Lucas Numbers


Jacobsthal Numbers

Lucas sequence 𝑈𝑛(𝑃,𝑄) where P = 1 and Q = -2 are called Jacobsthal numbers. The recurrence relation for Jacobsthal numbers is,

$$\mathrm{𝐽_𝑛 = 0\: 𝑓𝑜𝑟 \: 𝑛 = 0}$$

$$\mathrm{𝐽_𝑛 = 1\: 𝑓𝑜𝑟 \: 𝑛 = 1}$$

$$\mathrm{𝐽_𝑛 = 𝐽_𝑛−1 + 2𝐽_{𝑛−2}\: 𝑓𝑜𝑟 \: 𝑛 > 1}$$

Following are the Jacobsthal numbers −

0, 1, 1, 3, 5, 11, 21, 43, 85, 171, 341, 683, 1365, ….

Jacobsthal-Lucas Numbers

Complementary Lucas sequence $\mathrm{𝑉_𝑛(𝑃,𝑄)}$ where P = 1 and Q = -2 are called JacobsthalLucas numbers. The recurrence relation for Jacobsthal-Lucas numbers is,

$\mathrm{𝐽_𝑛}$ = 2 𝑓𝑜𝑟 𝑛 = 0

$\mathrm{𝐽_𝑛}$ = 1 𝑓𝑜𝑟 𝑛 = 1

$\mathrm{𝐽_𝑛 = 𝐽_{𝑛−1} + 2𝐽_{𝑛−2}\: 𝑓𝑜𝑟 \: 𝑛 > 1}$

Following are the Jacobsthal-Lucas numbers −

2, 1, 5, 7, 17, 31, 65, 127, 257, 511, 1025, 2047, 4097, ….

Problem Statement

Given an integer, N. Find the Nth Jacobsthal and Jacobsthal-Lucas number.

Example 1

Input: 2
Output:
Jacobsthal = 1
Jacobsthal-Lucas = 5

Explanation

Jacobsthal Numbers - 0, 1, 1

Jacobsthal-Lucas Numbers - 2, 1, 5

Example 2

Input: 12
Output:
Jacobsthal = 1365
Jacobsthal-Lucas = 4097

Explanation

Jacobsthal Numbers − 0, 1, 1, 3, 5, 11, 21, 43, 85, 171, 341, 683, 1365

Jacobsthal-Lucas Numbers − 2, 1, 5, 7, 17, 31, 65, 127, 257, 511, 1025, 2047, 4097

Approach 1: Recursive Approach

Using the recurrence relation of Jacobsthal and Jacobsthal-Lucas numbers, we can form a recursive relation as, $\mathrm{𝐽_𝑛 = 𝐽_{𝑛−1} + 2𝐽_{𝑛−2}}$

Pseudocode

procedure jacobsthalNumber (n)
   if n == 0
      ans = 0
   end if
   if n == 1
      ans = 1
   end if
   ans = jacobsthalNumber(n-1) + 2*jacobsthalNumber(n-2)
end procedure
procedure jacobsthalLucasNumber (n)
   if n == 0
      ans = 2
   end if
   if n == 1
      ans = 1
   end if
   ans = jacobsthalLucasNumber(n-1) + 2*jacobsthalLucasNumber(n-2)
end procedure

Example

In the following program, we have created separate functions for jacobsthal and jacobsthal-lucas numbers and used their recurrence relation to recursively calculate the nth number.

#include<bits/stdc++.h>
using namespace std;
// Recursive function for finding the nth Jacobsthal Number
int jacobsthalNumber (int n){

   // Defining the first case in recurrence relation
   if (n == 0) {
      return 0;
   }
   
   // Defining the second case in recurrence relation
   if (n == 1) {
      return 1;
   }
   
   // Defining the third case in recurrence relation when n>1
   return jacobsthalNumber(n-1) + 2*jacobsthalNumber(n-2);
}

// Recursive function for finding the nth Jacobsthal-Lucas Number
int jacobsthalLucasNumber (int n){

   // Defining the first case in recurrence relation
   if (n == 0) {
      return 2;
   }
   
   // Defining the second case in recurrence relation
   if (n == 1) {
      return 1;
   }
   
   // Defining the third case in recurrence relation when n>1
   return jacobsthalLucasNumber(n-1) + 2*jacobsthalLucasNumber(n-2);
}
int main(){
   int N = 7;
   cout << N << "th Jacobsthal number = " << jacobsthalNumber(N) << endl;
   cout << N << "th Jacobsthal-Lucas number = " << jacobsthalLucasNumber(N) << endl;
}

Output

7th Jacobsthal number = 43
7th Jacobsthal-Lucas number = 127

Time Complexity − O(2n) due to the recursive tree formed.

Space Complexity − O(1) + Recursive stack space.

Approach 2: Memoization

Memoization is helpful in reducing time complexity as it helps compute the function for one input only once by storing the results.

Pseudocode −

Initialize two arrays j[N+1] and jL[N+1] with -1
procedure jacobsthalNumber (n)
   if n == 0
      ans = 0
   end if
   if n == 1
      ans = 1
   end if
   if j[n] == -1
      j[n] = jacobsthalNumber(n-1) + 2*jacobsthalNumber(n-2)
   end if
   ans = j[n]
end procedure
procedure jacobsthalLucasNumber (n)
   if n == 0
      ans = 2
   end if
   if n == 1
      ans = 1
   end if
   if jL[n] == -1
      jL[n] = jacobsthalLucasNumber(n-1) + 2*jacobsthalLucasNumber(n-2)
   end if
   ans = jL[n]
end procedure

Example

In this program, we use memoization to reduce the time complexity of the previous approach.

#include<bits/stdc++.h>
using namespace std;
int N = 12;
// initializing memoization table
vector<int> j(N+1, -1), jL(N+1, -1);

// Memoized function for finding the nth Jacobsthal Number
int jacobsthalNumber (int n){

   // Defining the first case in recurrence relation
   if (n == 0) {
      return 0;
   }
   
   // Defining the second case in recurrence relation
   if (n == 1) {
      return 1;
   }
   
   // Checking if nth jacobsthal number is already calculated or not
   if(j[n] != -1) {
      return j[n];
   } else {
      return j[n] = jacobsthalNumber(n-1) + 2*jacobsthalNumber(n-2);
   }
}

// Memoized function for finding the nth Jacobsthal-Lucas Number
int jacobsthalLucasNumber (int n){

   // Defining the first case in recurrence relation
   if (n == 0) {
      return 2;
   }
   
   // Defining the second case in recurrence relation
   if (n == 1) {
      return 1;
   }
   
   // Checking if nth jacobsthal-lucas number is already calculated or not
   if(jL[n] != -1) {
      return jL[n];
   } else {
      return jL[n] = jacobsthalLucasNumber(n-1) + 2*jacobsthalLucasNumber(n-2);
   }
}

// Driver code
int main(){
   cout << N << "th Jacobsthal number = " << jacobsthalNumber(N) << endl;
   cout << N << "th Jacobsthal-Lucas number = " << jacobsthalLucasNumber(N) << endl;
}

Output

12th Jacobsthal number = 1365
12th Jacobsthal-Lucas number = 4097

Time Complexity − O(N) as repetitive recursive calls are not calculated.

Space Complexity − O(N) (because of the space used for the memoization table) + recursive stack space.

Approach 3: Dynamic Programming

Pseudocode −

procedure jacobsthal (n)
   initialize dp table jacobsthalDP[n+1]
   jacobsthalDP[0] = 0
   jacobsthalDP[1] = 1
   for i = 2 to n
      jacobsthalDP[i] = jacobsthalDP[i-1] + 2*jacobsthalDP[i-2]
   end for
   ans = jacobsthalDP[n]
end procedure
procedure jacobsthalLucas (n)
   initialize dp table jacobsthalLucasDP[n+1]
   jacobsthalLucasDP[0] = 2
   jacobsthalLucasDP[1] = 1
   for i = 2 to n
      jacobsthalLucasDP[i] = jacobsthalLucasDP[i-1] + 2*jacobsthalLucasDP[i-2]
   end for
   ans = jacobsthalLucasDP[n]
end procedure

Example

In the following program, dynamic programming approach is used.

#include<bits/stdc++.h>
using namespace std;

// Function for finding the nth Jacobsthal Number
int jacobsthalNumber (int n){
   int jacobsthalDP[n+1];
   
   // Defining base condition
   jacobsthalDP[0] = 0;
   jacobsthalDP[1] = 1;
   for (int i = 2 ; i <= n ; i++){
      jacobsthalDP[i] = jacobsthalDP[i-1] + 2*jacobsthalDP[i-2];
   }
   return jacobsthalDP[n];
}

// Function for finding the nth Jacobsthal-Lucas Number
int jacobsthalLucasNumber (int n){
   int jacobsthalLucasDP[n+1];
   
   // Defining base condition
   jacobsthalLucasDP[0] = 2;
   jacobsthalLucasDP[1] = 1;
   for (int i = 2 ; i <= n ; i++){
      jacobsthalLucasDP[i] = jacobsthalLucasDP[i-1] + 2*jacobsthalLucasDP[i-2];
   }
   return jacobsthalLucasDP[n];
}
int main(){
   int N = 5;
   cout << N << "th Jacobsthal number = " << jacobsthalNumber(N) << endl;
   cout << N << "th Jacobsthal-Lucas number = " << jacobsthalLucasNumber(N) << endl;
}

Output

5th Jacobsthal number = 11
5th Jacobsthal-Lucas number = 31

Time Complexity − O(n) as for loop is only executed once in each function

Space Complexity − O(n) for the dp table. In this approach, no extra stack recursive space is used.

Conclusion

In conclusion, Jacobsthal numbers are obtained from the Lucas sequence and Jacobsthal-Lucas numbers are obtained from the complementary Lucas sequence. In order to find the nth Jacobsthal and Jacobsthal-Lucas number, we can move from a recursive to a dynamic programming approach with minimum time and space complexity.

Updated on: 28-Sep-2023

160 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements