想象一下,我们给出了一个整数排序序列,它作为一个位集存储在一个32位或64位整数中。例如:
sequence = 0, 2, 3, 5, 6, 8, 11, 14;
mask = 1011011010010010;
16-bit bitset = 0100100101101101b = 0x496D;
序列中的每个整数都有 index ,它等于它之前的整数,以及 position ,它只是整数的值。相同的术语可以应用于相应位集中的任何设置位。例如,在上面的示例中,设置位为 position = 2 和 index = 1 ,并且 position = 8 和指数= 5
使用popcnt
指令,我们可以在现代x86架构中轻松找到 position 中的设置位的 index ,如下所示:
uint32_t position_to_index(uint32_t bset, uint32_t pos) {
(a) return _mm_popcnt_u32(_bzhi_u32(bset, pos)); //BMI2 & POPCNT
(b) return _mm_popcnt_u32(bset & ((1 << pos) - 1))); //POPCNT only
}
显然,它也适用于任何其他支持硬件popcount的架构。
问题是:如何有效地计算反函数,它通过给定的索引k返回给定整数中第k个设置位的位置?
为简单起见,可以假设在给定的位集中确实存在具有指定索引的设置位,即不需要进行错误检查。期望随机输入比特集的快速性能,因此所谓的无分支解决方案将是好的。欢迎使用特定于硬件的指令。小型(但随机分布)指数的快速解决方案也很有意思。
这是一个简单的基本解决方案,适用于小型索引:
uint32_t index_to_position(uint32_t bset, uint32_t idx) {
for (uint32_t i = 0; i < idx; i++)
(a) bset = _blsr_u32(bset); //BMI1
(b) bset = bset & (bset - 1); //pure C
return _bit_scan_forward(bset); //bsf (>=386)
}