Moser-de Bruijn 序列


題目要求列印 Moser-de Bruijn 序列的前 N 項,其中 N 將作為使用者輸入。

Moser-de Bruijn 序列是一個由整數構成的序列,這些整數不過是 4 的不同次冪的和,例如 1、4、16、64 等等。

該序列的前幾項包括 0、1、4、5、16、17、20、21、64……

該序列總是以零開頭,然後是 4 的不同次冪的和,例如 4⁰,即 1;4¹,即 4;然後是 4⁰ 和 4¹ 的和,即 5,依此類推。

在這個問題中,我們將得到任意正整數 N,我們的任務是列印 Moser-de Bruijn 序列直到 N 項。

讓我們透過以下示例瞭解這個問題。

輸入

N=6

輸出

0  1  4  5  16  17

解釋 - 給定的輸入是 6,這意味著我們需要列印 Moser-de Bruijn 序列的前 6 項,這就是我們所需的輸出。

輸入

N=12

輸出

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

解釋 - Moser-de Bruijn 序列的前 12 項是我們所需的輸出。我們可以透過將 4 的不同次冪相加來計算序列的第 N 項。

讓我們瞭解根據輸入中 N 的值列印 Moser-de Bruijn 序列前 N 項的演算法。

演算法

透過觀察 Moser-de Bruijn 序列中的數字,我們可以看到序列中的數字遵循它們之間的數學關係。

序列中的前幾個數字是 0、1、4、5、16、17、20、21、64、65、68、69、80、81、84……

序列中的數字只包含 4 的不同次冪和 4 的不同次冪的和。

如果我們從 0 開始考慮序列中數字的位置,我們可以觀察到 M(0)=0 且 M(1)=1。

並且每個第 N 項(其中 N 為偶數)可以由下式給出:

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

類似地,每個第 N 項(其中 N 為奇數)可以由下式給出:

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

讓我們嘗試使用上述關係找出 Moser-de Bruijn 序列的幾項。

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

M(2) 將等於 4*M(N/2),因為在這種情況下 N 是偶數。所以 M(2)=4*1=4。

M(3) 將等於 4*M(N/2)+1,因為在這種情況下 N 是奇數。所以 M(3)=5。

同樣地,

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

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

我們可以使用此關係計算直到 N 的序列的所有項。我們將使用序列中數字之間的上述關係來列印 Moser-de Bruijn 序列的前 N 項。

方法 1(使用遞迴)

我們將使用遞迴來根據演算法中討論的公式查詢序列的第 i 項。為了實現遞迴以列印 Moser-de Bruijn 序列的前 N 項,需要遵循以下步驟:

  • 為了找到序列的第 i 個數,我們將建立一個遞迴函式。

  • 在函式中,如果 i=0,我們將返回 0;如果 i=1,我們將返回 1。對於 i 的所有其他值,我們將檢查 i 是偶數還是奇數。如果 i 是偶數,則返回 4*rec(i/2);如果 i 是奇數,則返回 4*rec(i/2)+1。

  • 為了解決所包含的子問題,我們可以透過使用遞迴計算第 i/2 項的值來獲得第 i 項的值。

  • 在 for 迴圈中從 i=0 迭代到 i>N,以列印 Moser-de Bruijn 序列的前 N 項。

  • 對於每次迭代,我們將呼叫遞迴函式以獲取序列中第 i 項的值並繼續列印它們。

示例

//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;
}

輸出

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

方法 2(動態規劃)

眾所周知,動態規劃是對使用遞迴解決的問題的最佳化解決方案。因此,為了最佳化上述方法,我們將使用動態規劃的概念來列印 Moser-de Bruijn 序列的前 N 項。

在這裡,我們將把第 i 項的值儲存在一個大小為 N 的陣列中,這樣我們就無需為序列中的第 i 個數重複計算重複子問題的值。

下面介紹在方法中實現動態規劃以列印 Moser-de Bruijn 序列前 N 項的步驟:

  • 我們將建立一個大小為 N 的向量來儲存序列的前 N 項。

  • 為了將數字儲存在陣列中,我們將建立一個函式,在該函式中我們將使用演算法部分中討論的公式查詢序列的第 i 個數。

  • 一旦向量被更新到序列的 N 項,我們將透過在 for 迴圈中從 i=0 迭代到 i

示例

//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;
}

輸出

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

時間複雜度:O(N) 因為我們用 for 迴圈迭代 N 次來更新序列的 N 項。

空間複雜度:O(N) 因為我們使用了大小為 N 的向量來儲存序列的數字。

結論

本文討論了 Moser-de Bruijn 序列的概念。我們討論了使用數字之間數學關係查詢序列中任何第 N 項的演算法,我們在 C++ 中使用遞迴和動態規劃這兩種方法來實現我們的方法,以便更好地讓您理解。

我希望您覺得這篇文章有助於澄清您關於該主題的所有概念。

更新於:2023年8月28日

瀏覽量:164

開啟您的職業生涯

完成課程獲得認證

開始學習
廣告