在计算单词中的位数时,蛮力就是这样的:
int CountNumSetBits(unsigned long n)
{
unsigned short num_setbits = 0;
while (n)
{
num_setbits += n & 1;
n >>= 1;
}
return num_setbits;
}
大O速度为O(n),其中n是Word中的位数。
我想到了另一种编写算法的方法,利用了这样一个事实:我们使用y = x&〜(x-1)来获得第一次出现的设置位
int CountNumSetBitsMethod2(unsigned long n)
{
unsigned short num_setbits = 0;
int y = 0;
while (n)
{
y = n& ~(n - 1); // get first occurrence of '1'
if (y) // if we have a set bit inc our counter
++num_setbits;
n ^=y; // erase the first occurrence of '1'
}
return num_setbits;
}
如果我们假设输入是50%1和50%0,那么第二种算法似乎可以快两倍。但是,实际的复杂性更高:
在方法一中,我们对每个位执行以下操作: 1加 1和 1班
在方法二中,我们为每个设置位执行以下操作: 1和 1补充 1减法(减法的结果必须复制到另一个reg) 1比较 1个增量(如果比较为真) 1 XOR
现在,在实践中,可以通过执行一些分析来确定哪种算法更快。也就是说,使用秒表机制和一些测试数据并调用每个算法一百万次。
然而,我想先做的是看看我能通过眼球代码来估计速度差异(给定相同数量的设置和未设置位)。
如果我们假设减法采用与add(近似)相同的量周期,并且所有其他操作周期相等,那么可以得出结论:每个算法花费大约相同的时间吗?
注意:我在这里假设我们不能使用查找表。
答案 0 :(得分:2)
第二种算法可以大大简化:
int CountNumSetBitsMethod2(unsigned long n) {
unsigned short num_setbits = 0;
while (n) {
num_setbits++;
n &= n - 1;
}
return num_setbits;
}
有许多方法可以计算单词中设置的位数:
尝试根据经验确定通过计算周期哪个更快是不容易的,因为即使查看汇编输出,也很难评估指令并行化,流水线操作,分支预测,寄存器重命名和争用的影响......现代CPU是非常复杂!此外,生成的实际代码取决于编译器版本和配置,时间取决于CPU类型和版本...更不用说与所使用的特定值集相关的可变性(对于具有可变数量指令的算法)。 / p>
基准测试是一种必要的工具,但即使是仔细的基准测试也可能无法正确模拟实际使用情况。
这是一个很棒的网站,适合这种小小的游戏:
http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive
我建议您在系统上实施不同版本并执行比较基准测试。没有明确的答案,只有特定条件的局部最优。
一些惊人的发现:
// option 3, for at most 32-bit values in v:
c = ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) %
0x1f;
c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
更经典的一种,通常被认为是计算32位整数v中的位的最佳方法:
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
答案 1 :(得分:0)
首先,了解事物速度的唯一方法就是衡量它们。
第二 - 找到某些字节中的设置位数,为一个字节中的设置位数构建一个查找表
0->0
1->1
2->1
3->2
4->1
etc.
这是一种常用方法而且非常快
您可以手动编码或在启动时创建