簡體   English   中英

只有某些位可以改變的二進制數的所有組合

[英]All combinations of a binary number where only certain bits can change

我想知道是否有一種算法可以生成二進制數的所有可能組合,其中只有某些位置的位可以改變,例如,我們有以下位流,但只有 position x中標記的位可以改變(這個例子有8個地方可以改變做不同的組合,一共2^8):

x00x0000x000000x00x00x000x000x

一種解決方案是首先將數字視為 8 位數字,然后計算xxxxxxxx的所有組合。 但是,這並不能完全滿足我的需求,因為我想稍后在線性移位寄存器 (LFSR) 中使用該數字,目前,我正在尋找一個使用std::bitset的答案。

滿足該模式的整數可以通過使用“掩碼增量”進行迭代來枚舉,這會增加可變位但使固定位保持不變。 為方便起見,我將假設“固定位”為零,但事實並非如此,它仍然可以進行微小的更改。 固定位mask為 1,可變位掩碼為 0。

uint32_t x = 0;
do {
    // use x
    ...
    // masked increment
    x = (x | mask) + 1 & ~mask;
} while (x != 0);

x | mask x | mask設置固定位,以便進位將“通過”固定位。 +1 增加變量位。 &~mask清除設置的額外位,將固定位變回零。

std::bitset不能遞增,因此很難直接使用,但如果需要,可以將整數轉換為std::bitset

所以,也已經有了答案。 但只是轉儲代碼,沒有任何解釋。 不好。 不知道,你為什么接受。 反正...

我想用不同的方法添加一個答案,並解釋這些步驟。

基本上,如果你想要一個二進制數的所有組合,那么你可以簡單地“計數”或“加一”。 3 位值的示例。 這將是十進制 0、1、2、3、4、5、6、7 和二進制 000、001、010、011、100、101、110、111。您會看到這是簡單的計數。

如果我們回想起學生時代,在那里我們學習了 boolean 代數和一點點自動機理論,那么我們會記得這個計數操作是如何在低級別完成的。 我們總是翻轉最低有效位,如果有從 1 到 0 的轉換,那么我們基本上發生了溢出,也必須翻轉下一位。 這就是二進制加法器的原理。 我們希望在我們的示例中始終添加 1。 因此,將 1 加到 0,結果為 1,則不會溢出。 但是加 1 到 1,結果是 0,那么我們有一個過低,必須加 1 到下一位。 這將有效地翻轉下一位,依此類推。

這種方法的優點是,我們並不總是需要對所有位進行操作。 因此,復雜度不是 O(n),而是 O(log n)。

附加優勢:它非常適合您使用std::bitset的要求。

第三個優勢,也許不是那么明顯:您可以將計算下一個組合的任務與程序的 rest 分離。 無需將您的實際任務代碼集成到這樣的 function 中。 這也是為什么std::next_permutation是這樣實現的原因。

而且,上面描述的算法適用於所有值,不需要排序或一些必要的東西。

那部分是針對您要求的算法的。


下一部分是針對您的要求,即只有某些位可以更改。 當然,我們需要指定這些位。 而且因為您正在使用std::bitset掩碼,所以這里沒有解決方案。 更好的方法是使用索引。 含義,給出允許更改的位的位位置。

然后我們可以使用上述算法,只需要一個額外的間接。 所以,我們不使用bits[pos] ,而是bits[index[pos]]

索引可以使用初始化列表輕松存儲在std::vector中。 我們還可以從字符串或其他任何東西中導出索引向量。 我以std::string為例。


以上所有將導致一些簡短/緊湊的代碼,只有幾行並且易於理解。 我還添加了一些使用此 function 的驅動程序代碼。

請參見:

#include <iostream>
#include <vector>
#include <string>
#include <bitset>
#include <algorithm>
#include <cassert>

constexpr size_t BitSetSize = 32U;

void nextCombination(std::bitset<BitSetSize>& bits, const std::vector<size_t>& indices) {

    for (size_t i{}; i < indices.size(); ++i) {

        // Get the current index, and check, if it is valid
        if (const size_t pos = indices[i]; pos < BitSetSize) {

            // Flip bit at lowest positions
            bits[pos].flip();

            // If there is no transition of the just flipped bit, then stop
            // If there is a transition from high to low, then we need to flip the next bit
            if (bits.test(pos))
                break;
        }
    }
}

// Some driver code
int main() {
    // Use any kind of mechanism to indicate which index should be changed or not
    std::string mask{ "x00x0000x000000x00x00x000x000x" };

    // Here, we will store the indices
    std::vector<size_t> index{};
    // Populated the indices vector from the string
    std::for_each(mask.crbegin(), mask.crend(), [&, i = 0U](const char c) mutable {if ('x' == c) index.push_back(i); ++i; });

    // The bitset, for which we want to calculate the combinations
    std::bitset<BitSetSize> bits(0);

    // Play around
    for (size_t combination{}; combination < (1 << (index.size())); ++combination) {

        // This is the do something
        std::cout << bits.to_string() << '\n';

        // calculate the next permutation
        nextCombination(bits, index);
    }
    return 0;
}

此軟件已使用 C++17 與 MSVC 19 社區版一起編譯

如果您還有其他問題或需要更多說明,那么我很樂意回答

像這樣的東西

// sample indexes
static const int indexes[8] = { 0, 4, 8, 11, 13, 16, 22, 25 };

std::bitset<32> clear_bit_n(std::bitset<32> number, int n)
{
    return number.reset(indexes[n]);
}

std::bitset<32> set_bit_n(std::bitset<32> number, int n)
{
    return number.set(indexes[n]);
}

void all_combinations(std::bitset<32> number, int n)
{
    if (n == 8)
    {
        // do something with number
    }
    else
    {
        all_combinations(clear_bit_n(number, n), n + 1);
        all_combinations(set_bit_n(number, n), n + 1);
    }
}

all_combinations(std::bitset<32>(), 0);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM