Moser-de Bruijn Sequence


The problem statement includes printing the first N terms of the Moser−de Bruijn Sequence where N will be given in the user input.

The Moser−de Bruijn sequence is a sequence consisting of integers which are nothing but the sum of the different powers of 4 i.e. 1, 4, 16, 64 and so on.

The first few numbers of the sequence include 0, 1, 4, 5, 16, 17, 20, 21, 64.......

The sequence always starts with zero followed by the sum of different powers of 4 such as $\mathrm{4^{0}}$ i.e $\mathrm{4^{1}\:i.e\:4,}$ then sum of $\mathrm{4^{0}\:and\:4^{1}\:i.e\:5}$ and so on.

In this problem, we will be given any positive integer N and our task will be to print the Moser−de Bruijn sequence upto N terms.

Let’s understand the problem with the examples given below.

Input

N=6

Output

0  1  4  5  16  17

Explanation − The input given is 6 which means we need to print the first 6 terms of the Moser−de Bruijn sequence which is our required output.

Input

N=12

Output

0  1  4  5  16  17  20  21  64  65  68  69

Explanation − The first 12 terms of the Moser−de Bruijn sequence is our required output. We can calculate the Nth term of the sequence by adding the different powers of 4.

Let’s understand the algorithm to print the first N terms of the Moser−de Bruijn sequence in accordance with the value of N in the input.

Algorithm

By observing the numbers in the Moser−de Bruijn sequence, we can see that the numbers in the sequence follow the mathematical relation among them.

The first few numbers in the sequence are 0, 1, 4, 5, 16, 17, 20, 21, 64, 65, 68, 69, 80, 81, 84……..

The numbers in the sequence only includes distinct powers of 4 and sum of distinct powers of 4.

If we consider the position of the numbers in the sequence from 0, we can observe that M(0)=0 and M(1)=1.

And every Nth term where N is even can be given by,

$$\mathrm{M(N)=4*M(N/2)}$$

Similarly, every Nth term where N is odd can be given by,

$$\mathrm{M(N)=4*M(N/2)+1}$$

Let’s try to find out the few numbers of the Moser−de Bruijn sequence using the above relations.

$$\mathrm{M(0)=0\:\:\:M(1)=1}$$

M(2) will be equal to 4*M(N/2) since N is even in this case. So M(2)=4*1=4.

M(3) will be equal to 4*M(N/2)+1 since N is even in this case. So, M(3)=5.

Similarly,

$$\mathrm{M(4)=4*M(4/2)=4*4=16}$$

$$\mathrm{M(5)=4*M(5/2)+1=4*4+1=17}$$

We can calculate all the terms of the sequence until N using this relation. We will use the above relation among the numbers in the sequence to print the first N terms of the Moser−de Bruijn sequence.

Approach−1 (using recursion)

We will use recursion to find the ith term of the sequence using the formula discussed in the algorithm. The steps to follow to implement the recursion in our approach to print first N terms of Moser−de Bruijn sequence are:

  • To find the ith number of the sequence, we will create a recursive function.

  • In the function, we will return 0 if i=0 and 1 if i=1. For all other values of i, we will check if i is an even number or odd. If i is even, return 4*rec(i/2) whereas if i is odd, return 4*rec(i/2)+1.

  • We can get the value of ith term by calculating values of the i/2 th term using recursion in order to solve the sub problems included.

  • Iterate in a for loop from i=0 to i>N to print the first N terms of the Moser−de Bruijn sequence.

  • For each iteration, we will call the recursive function to get the value of ith term in the sequence and keep printing them.

Example

//C++ code to print the N terms of Moser-de Bruijn Sequence
#include <bits/stdc++.h>

using namespace std;

//recursive function to get the Nth term of the sequence
int rec(int N){
    
    if(N==0){ //as M(0)=0
        return 0;
    }
    
    else if(N==1){  //as M(1)=1
        return 1;
    }
    
    else if(N&1){ //for the case when N is odd
        return 4*rec(N/2)+1;
    }
    
    else{  //for the case when N is even
        return 4*rec(N/2);
    }
}

//to print the first N terms of the sequence
void print_sequence(int N){
    //for printing each term upto N using the rec() function
    for(int i=0;i<N;i++){
        cout<<rec(i)<<"  ";
    }
}

int main()
{
    int N; //for input value
    N=22;
    
    print_sequence(N);  //calling the function
    
    return 0;
}

Output

0  1  4  5  16  17  20  21  64  65  68  69  80  81  84  85  256  257  260  261  272  273

Approach−2 (Dynamic Programming)

As we all know that dynamic programming is an optimised solution to the problems solved using recursion. Therefore, in order to optimise the above approach we will use the concept of dynamic programming to print the first N terms of the Moser−de Bruijn sequence.

Here, we will keep storing the values of ith term in an array of N so that we don’t need to calculate the values of repeating sub problems for ith number in the sequence.

The steps to implement dynamic programming in the approach to print first N terms of Moser−de Bruijn sequence are given below:

  • We will make a vector of size N to store the first N terms of the sequence.

  • In order to store the numbers in the array, we will make a function where we will find the ith number of the sequence using the formula discussed in the algorithm section.

  • Once the vector is being updated up to N terms of the sequence we will print the numbers stored in the vector by iterating in the for loop from i=0 to i<N which will be the first N terms of the Moser−de Bruijn sequence.

Example

//C++ code to print the first N terms of Moser-de Bruijn Sequence

#include <bits/stdc++.h>

using namespace std;

//function to update the vector using dp
void calculate_numbers(vector<int>& dp, int N){
    
    dp[0]=0; //as M(0)=0
    dp[1]=1; //M(1)=1
    
    for(int i=2;i<N;i++){
        if((i&1) == 0){ //if i is even
            dp[i]=4*dp[i/2];
        }
        else{ //if i is odd
            dp[i]=4*dp[i/2]+1;
        }
    }
}

//function to print the first N terms of the Moser-de Bruijn sequence
void print_sequence(vector<int>& dp, int N){
    
    for(int i=0;i<N;i++){
        cout<<dp[i]<<"  ";
    }
}

int main()
{
    int N; //for taking input
    N=35;
    
    vector<int> dp(N,0); //to store first N terms of the sequence
    
    //calling the function to update the array up to N
    calculate_numbers(dp,N);
    
    //to print the sequence
    print_sequence(dp,N);
    

    return 0;
}

Output

0  1  4  5  16  17  20  21  64  65  68  69  80  81  84  85  256  257  260  261  272  273  276  277  320  321  324  325  336  337  340  341  1024  1025  1028

Time complexity: O(N) because we iterate in a for loop for N times to update N terms of the sequence.

Space complexity: O(N) as we used a vector of size N to store the numbers of the sequence.

Conclusion

The concept of the Moser−de Bruijn sequence was discussed in the article. We discussed the algorithm to find any Nth term of the sequence using the mathematical relation among the numbers which we implement in our approach in C++ using recursion and dynamic programming both to make you understand better.

I hope you find this article helpful in clearing all your concepts regarding the topic.

Updated on: 28-Aug-2023

95 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements