位掩码

1. Introduction

位掩码提供了一种有效的方式来操作存储为32位(或64位)有符号整数的小布尔集合,但以短32位(或64位)字符字符串的形式解释。


通过使用位运算,可以轻松快速地检查整数的每个位,打开(或关闭)它们。它可以用于各种算法,例如旅行商问题的动态规划解决方案,以加速关键(小)集合操作。

2. 可视化

上面显示了位掩码数据结构及其操作的可视化。


顶部行描述了输入整数 S 的位索引,从最右边的第 0 位开始。请注意,我们使用 1 位十六进制表示法 A/B/../E 来表示第 10/11/../14 位。


第二行描述了输入整数 S 的位。在实际应用中,这通常是一个 32 位有符号整数(例如 C++/大多数其他编程语言中的 int)。有时,这是一个 64 位有符号整数(例如 C++ 中的 long long)。对于此可视化,我们将 S 限制为 16 位有符号整数。


第三行描述了将应用于 S(位)掩码 的位以及相关的按位操作。


最后一行描述了结果

3. 位掩码运算

在此可视化中,我们只是六种使用常数时间的位运算:
  1. 设置 S 的值
  2. 将第 j 位设为 on
  3. 判断第 j 位的值
  4. 将第 j 位设为 off
  5. 对第 j 位取反
  6. 最低有效位

3-1. 设置 S 的值

我们可以输入一个在 [0..32767 (215-1)] 区间内的小整数,然后可视化其二进制的形式。
或者我们也可以随机选一个在 [0..32767] 内的整数,或二进制为 '100...0' 的 2n,或二进制为 '11...1' 的 2n-1。

3-2. 将第 j 位设为 on

我们可以输入第 j 位的索引来将其设为 on。
索引从 0 开始,但是从最右边的位开始数的(见上)。
位运算比较简单:S OR (1 << j)
将一个已经是 on 的位设为 on 不改变任何东西。

3-3. 判断第 j 位的值

我们可以输入第 j 位的索引来判断其状态(on 或 off)。
索引从 0 开始,但是从最右边的位开始数的(见上)。
位运算比较简单:S AND (1 << j)

3-4. 将第 j 位设为 off

我们可以输入第 j 位的索引来将其设为 off。
索引从 0 开始,但是从最右边的位开始数的(见上)。
位运算比较简单:S AND ~(1 << j)
将一个已经是 off 的位设为 off 不改变任何东西。

3-5. 对第 j 位取反

我们可以输入要第 j 位的索引来对其取反(on → off 或 off → on)。
索引从 0 开始,但是从最右边的位开始数的(见上)。
位运算比较简单:S XOR (1 << j)

3-6. 最低有效位

这个运算不需要输入。它只是一个找到 S 中最右边的值为 on 的位的特殊运算。
位运算比较简单:S AND (-S)
注意在补码中,-S = NOT(S)+1

4. 挑战

有关位掩码/位操作的源代码示例,请查看:bit_manipulation.cpp | py | java | ml


要测试您对位操作的理解,请尝试我们的位掩码主题的在线测验


而对于涉及位掩码/位操作的更具挑战性的问题,请尝试以下在线评测问题:UVa 11933 - 分割数字Kattis - bitbybit


除了这些简单的应用之外,位掩码还经常用作一些高级算法中的低级优化,所以当您在更大的算法中遇到位掩码时,请做好准备。