【问题标题】:Bit manipulation, permutate bits位操作,置换位
【发布时间】:2010-05-19 12:23:21
【问题描述】:

我正在尝试创建一个循环,该循环遍历所有不同的整数,其中最后 40 位中的 10 位设置为高,其余设置为低。原因是我有一张包含 40 个不同值的地图,我想将其中 10 个值相乘的所有不同方式相加。 (这只是出于好奇,所以真正感兴趣的是“bitmanip”循环,而不是总和。)

如果我要这样做,例如4位中的2位,手动设置很容易,

0011 = 3,
0101 = 5,
1001 = 9,
0110 = 6,
1010 = 10,
1100 = 12,

但是 40 人中有 10 人我似乎无法找到有效生成这些的方法。我尝试从 1023 开始(= 1111111111 二进制),找到了一种操作它的好方法,但没有成功。我一直在尝试在 C++ 中做到这一点,但它确实是令人感兴趣的通用方法(如果有的话)。我做了一些谷歌搜索,但收效甚微,如果有人有一个好的链接,那当然也会受到赞赏。 :)

【问题讨论】:

    标签: c++ bit-manipulation


    【解决方案1】:

    您可以使用选择/组合算法的任何标准实现。基本上,您想从 40 位中选择 10 位,将其设置为 1

    也就是说,40 choose 10 is 847,660,528。并且该数字将乘以许多可能不在前 40 位中的“尾”位。大概尾位不受任何规则的约束,因此如果有 k 位,那将是另一个 2k 因素。

    这个算法,即使你实现了它,也会很慢。想出一种更好的方法来解决您遇到的任何问题可能是个好主意。

    相关问题

    【讨论】:

    • 我发现运行时间会相当长,所以我不会这样做,但我对此有点好奇并想弄清楚。感谢您的链接,那里有很多好帖子。 :)
    【解决方案2】:

    有点复杂,但纯粹是通过位操作完成的。你的例子:

    #define WIDTH 4
    #define BITS 2
    
    void printbits(long pattern) {
      long bit;
      for (bit = 1L << WIDTH - 1; bit; bit >>= 1)
        putchar(pattern & bit ? 49 : 48);
      putchar('\n');
    }
    
    void movebits(pattern, bit) {
      long mask = 3L << bit;
      while (((pattern ^= mask) & mask) && (mask < 1L << WIDTH)) {
        mask <<= 1;
        printbits(pattern);
        if (bit)
          movebits(pattern, bit - 1);
      }
    }
    
    int main() {
      long pattern = (1L << BITS) - 1L, mask;
      printbits(pattern);
      movebits(pattern, BITS - 1);
    }
    

    你的真实应用:

    #define WIDTH 40
    #define BITS 10
    

    并且,正如 polygenelubricants 所说,准备好等待位 :) 当然,您会将 printbits 替换为对您更有用的东西...

    (因测试不足而编辑:/该死的错别字...)

    【讨论】:

    • 完美。或者,至少非常接近。(只需要制作更长的整数。)现在我只需要坐下来真正理解 while 循环中的条件。它实际上看起来很酷,看着那些 1 向左飞行。 :D
    • 嗯,这个想法是这样的:我想将 1 向左移动,直到它碰到另一个 1 或左边缘。因此,我假设有一个模式“...01...”,我将其翻译为“...10...”。最简单的方法是在适当的位置使用“11”掩码,我将其与模式 (pattern ^= mask) 进行异或。如果我碰到另一个 1,则模式将是“...11...”,并且 XORing 会给出“...00...”。因此,如果异或位(通过 AND 忽略任何其他位)都为零,这是我的结束条件之一 ((pattern ^= mask) &amp; mask))。
    • 另一个条件 (mask &lt; 1 &lt;&lt; WIDTH) 只是设置最高 1 的边界,因为它没有任何东西可以碰到,所以我检查掩码是否超过第 4 位(或第 40 位) .我希望这会有所帮助...
    • 确实有帮助。我会自己弄清楚(我希望),但看到解释有助于加快这个过程。所以感谢您抽出宝贵的时间。
    【解决方案3】:

    您可以简单地使用next_permutation。这是一个重现您的 4 个案例中的 2 个的示例(顺序略有不同):

    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main () {
     int bits[] = {0,0,1,1};
    
     do {
      for (int i = 0; i < 4; ++i) cout << bits[i] << " ";
      cout << endl;
     } while ( next_permutation (bits,bits+4) );
     return 0;
    }
    

    【讨论】:

    • 这意味着每个“位”实际上都是一个 int,所以实际上有四个 int 正在被打乱,而不是一个 int 中的四个位。这可能是一个很好的解决方案,但不完全是我的意思。
    • 难道不是bitset会更好地代替bit[]?
    【解决方案4】:

    有一种非常不明显的方法可以有效地做到这一点:Gosper 的方法从 HAKMEM 项目 175 中找到具有相同数量 1 位的下一个更高整数。

    lowest_1_bit = prev_combo & -prev_combo;
    tmp = prev_combo + lowest_1_bit;
    new_combo = (((prev_combo ^ tmp) >> 2) / lowest_1_bit) | tmp;
    
    • 第一行找到最右边的1位;
    • 第二个将最右边的1 位转换为0,并将位于左侧的0 转换为1
    • 第三行替换了单词底部第二行丢失的1 位。

    现在(假设您使用的是 64 位整数类型)您可以从 1023 开始并重复应用它(直到结果超过 1&lt;&lt;40)。

    【讨论】:

    • 这就像一个魅力。我尝试阅读 HAKMEM 论文,但对我来说,至少 C++ 语法更容易,所以感谢您的精彩翻译。
    【解决方案5】:

    如果将其重写为一组指示位位置的嵌套循环会更容易:

    0011 = 0 1
    0101 = 0 2
    1001 = 0 3
    0110 = 1 2
    1010 = 1 3
    1100 = 2 3
    

    也就是说,第一个位的位置P1从0到3-1,第二个位P2的位置从P1+1到3重复运行。把这个变成一个通用的递归函数就剩下了一个练习。

    【讨论】:

    • 谢谢。运动总是好的。感谢您的编辑,我犯了愚蠢的错误。
    猜你喜欢
    • 2017-01-12
    • 2013-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    相关资源
    最近更新 更多