使用位逻辑运算实现位向量

使用位逻辑运算实现位向量

解答《编程珠玑》中相关习题时的一些思考。

假定,我们需要使用X个类型为type的整数来创建一个至少包含N个比特位的位向量,那么,计算出的X就是:

int X = (int)ceil(N * 1.0 / (sizeof(type) * 8))

通过这些整数,形成一个数组,就能构建出了我们需要的位向量:

type bit_vector[X]

如果要对索引位置为i的比特位进行操作,就需要知道该比特位是哪个整数中的哪个比特位,因此需要处理两个小任务:

  1. 查找整数
  2. 查找整数中的比特位

查找整数

查找整数,也即定位到该比特位所在的整数。

很明显,该整数在bit_vector中的索引N为:i / st,也即i >> log2(st)

这里,我们将log2(st)记为shift

shift = log2(st)

因此,N = i >> shift

使用不同的整数类型时,所对应的shift如下:

整数类型 shift
uint8_t 3
uint16_t 4
uint32_t 5
uint64_t 6

查找整数中的比特位

该比特位在整数bit_vector[N]中的索引是i % st,也即i - (i / st * st),使用位逻辑运算就是i - ((i >> log2(st)) << log2(st)),也即i - ((i >> shift) << shift)

其中的(i >> shift) << shift,也就是将i的低shift位,置为0。示例:

所以,i - ((i >> shift) << shift)就表示保留i的低shift位的值。示例:

使用位逻辑运算实现低shift位的保留时,我们需要用到一个低shift位均为1而其他位均为0的整数,让其与i进行&运算。

shift位均为1而其他位均为0的整数,也即((1 << shift) - 1),我们将其记为mask

mask = ((1 << shift) - 1)

使用mask实现低位保留:

前面提到shift = log2(st),因此((1 << shift) - 1)就是st - 1

使用不同的整数类型时,所对应的mask如下:

整数类型 mask
uint8_t 7(0x07)
uint16_t 15(0x0F)
uint32_t 31(0x1F)
uint64_t 63(0x3F)

最终,该比特位在整数bit_vector[i >> shift]中的索引i - ((i >> shift) << shift)可以用i & mask表示:

当对位向量中索引为i的比特位进行设置、清除、测试时,就可以用bit_vector[i >> shift](1 << (i & mask))进行位逻辑运算实现这些操作。

设置:

1
bit_vector[i >> shift] |= (1 << (i & mask))

清除:

1
bit_vector[i >> shift] &= ~(1 << (i & mask))

测试:

1
bit_vector[i >> shift] & (1 << (i & mask))

使用位逻辑运算实现位向量
https://daniate.github.io/2022/03/12/使用位逻辑运算实现位向量/
作者
Daniate
发布于
2022年3月12日
许可协议