提问人:Tyker 提问时间:11/14/2023 最后编辑:Tyker 更新时间:11/14/2023 访问量:244
有没有一种非迭代的方法来查找第 N 个集合位的索引?[复制]
is there a non-iterative way of finding the index of the Nth set bit? [duplicate]
问:
#include <cassert>
#include <cstdint>
uint64_t pos_of_nth_bit(uint64_t X, uint64_t bit) {
while (X) {
if (!bit--)
return __builtin_ctzll(X);
X = X & (X - 1);
}
assert(false && "no such bit");
}
有没有更快的方法来做逻辑?pos_of_nth_bit
例如:
pos_of_nth_bit(0b101001000, 0) = 3
pos_of_nth_bit(0b101001000, 1) = 6
pos_of_nth_bit(0b101001000, 2) = 8
答:
0赞
Jerry Coffin
11/14/2023
#1
至少如果我们把答案限制在普通的硬件和相当实用的解决方案上,你几乎肯定会被困在一些迭代中。
不切实际的明显 O(1) 解决方案是查找表。它不切实际的原因是,对于 M 位输入,第 N 个设置位位置的查找表将需要一个包含 N * 2 M 个条目的查找表(如果每个条目的大小需要为log 2(M) 位)。对于 64 位输入,我很确定这比历史上产生的内存还要多。
但是,在保持查找表大小相当合理的同时,大幅减少最大迭代次数是很容易的。例如,我们可以查找一个字节中设置的位数,只有一个 256 个条目的表。这样,我们最多执行 8 次表查找以隔离一个包含第 i 个设置位的字节,然后在该字节内进行 8 次迭代以找到正确的位。这会将最坏情况从 64 次迭代减少到 16 次迭代(预期平均值从 32 次减少到 8 次左右)。查找表足够小,几乎可以容纳任何具有缓存的 CPU 的 L1 缓存,因此访问它通常非常快。
评论
0赞
harold
11/14/2023
如果您使用查找表只是为了计算位数,您不妨使用std::popcount
评论
log2
bit
__builtin_ctzll
X & (X-1)
X & (X - 1)
bit-manipulation
select