【问题标题】:All combinations of a binary number where only certain bits can change只有某些位可以改变的二进制数的所有组合
【发布时间】:2020-07-20 08:44:46
【问题描述】:

我想知道是否有一种算法可以生成二进制数的所有可能组合,其中只有某些位置的位可以改变,例如,我们有以下比特流,但只有标记在位置x 的位可以变化(这个例子有8个地方可以变化做出不同的组合,一共2^8个):

x00x0000x000000x00x00x000x000x

一种解决方案是首先将数字视为 8 位数字,然后计算 xxxxxxxx 的所有组合。 但是,这并不能完全满足我的需求,因为我想稍后在线性移位寄存器 (LFSR) 中使用该数字,目前,我正在寻找使用 std::bitset 的答案。

【问题讨论】:

  • 使用递归可以轻松完成所有组合的迭代。
  • @john 对于代码中的这个特定问题,您介意详细说明吗?

标签: c++ algorithm bit-manipulation


【解决方案1】:

可以通过使用“掩码增量”进行迭代来枚举满足模式的整数,这会增加可变位但使固定位保持不变。为方便起见,我将假设“固定位”为零,但事实并非如此,它仍然可以进行微小的更改。 mask 固定位为 1,可变位​​为 0。

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

x | mask 设置固定位,因此进位将“通过”固定位。 +1 增加变量位。 &~mask 清除设置的额外位,将固定位变回零。

std::bitset 不能自增,所以很难直接使用,但如果需要,可以将整数转换为std::bitset

【讨论】:

    【解决方案2】:

    所以,已经有答案了。但只是转储代码,没有任何解释。不好。不确定,你为什么接受。总之……

    我想添加一个不同方法的答案,并解释步骤。

    基本上,如果您想要一个二进制数的所有组合,那么您可以简单地“计数”或“加一”。 3 位值的示例。这将是十进制 0, 1, 2, 3, 4, 5, 6, 7 和二进制 000, 001, 010, 011, 100, 101, 110, 111。你看这是简单的计数。

    如果我们回想起学生时代,我们学习了布尔代数和一点自动机理论,那么我们会记得这种计数操作是如何在低级别完成的。我们总是翻转最低有效位,如果有从 1 到 0 的转换,那么我们基本上发生了溢出,也必须翻转下一位。这就是二进制加法器的原理。我们希望在我们的示例中始终添加 1。因此,将 1 加到 0,结果为 1,则不会溢出。但是加 1 到 1,结果是 0,那么我们有一个过低,必须加 1 到下一位。这将有效地翻转下一位,依此类推。

    这种方法的优点是,我们并不总是需要对所有位进行操作。所以,复杂度不是 O(n),而是 O(log n)。

    附加优势:非常适合您使用std::bitset 的要求。

    第三个优势,也许不是那么明显:您可以将计算下一个组合的任务与程序的其余部分分离。无需将您的实际任务代码集成到这样的功能中。这也是为什么std::next_permutation是这样实现的原因。

    而且,上面描述的算法适用于所有值,不需要排序或其他必要的东西。

    那部分是你要求的算法。


    下一部分是针对您的要求,即只有某些位可以更改。当然,我们需要指定这些位。而且因为您正在使用std::bitset 掩蔽在这里没有解决方案。更好的方法是使用索引。含义,给出允许更改的位的位位置。

    然后我们可以使用上述算法,只需要一个额外的间接。所以,我们不使用bits[pos],而是使用bits[index[pos]]

    索引可以使用初始化列表轻松存储在std::vector 中。我们还可以从字符串或其他任何东西中导出索引向量。我以std::string 为例。


    以上所有将产生一些简短/紧凑的代码,只有几行并且易于理解。我还添加了一些使用此功能的驱动程序代码。

    请看:

    #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 社区版

    如果您还有其他问题或需要更多说明,我很乐意为您解答

    【讨论】:

      【解决方案3】:

      类似的东西

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

      【讨论】:

      • 不知道为什么这会被否决,我是不是误会了什么?
      • 据我了解,您的答案会生成 8 位二进制数组的所有组合,这不是我的问题的重点,重点是生成所有二进制数,其中只有位x的地方在变化。
      • @A.R.H 这就是使用clear_bit_nset_bit_n 函数的地方。以您为例,似乎位 0 是最右边的位,位 1 是第五位,位 2 是第九位,依此类推。
      • @A.R.H 我只是想说明如何使用递归来生成组合。我认为这是你没有得到的部分,但也许我错了。
      • 我了解递归部分,但是,我不了解我的问题的重点部分,clear_bit_nset_bit_n
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-01
      • 2015-03-01
      • 1970-01-01
      相关资源
      最近更新 更多