我的问题与Fastest way of computing the power that a "power of 2" number used?非常相似:
将x=2^y
作为输入,我想输出y
。
区别在于我用C语言编码,而不是C ++编码,我确信输入中只有一个位,所以我想知道是否有更有效的方法来解决这个问题。
这是我的尝试:
unsigned int get_power_of_two(unsigned int x)
{
unsigned int y=0;
for(unsigned int input=x; input>0; input=input>>1)
{
if(input & 1U)
{
break;
}
y++;
}
return y;
}
与@ Dave的回答中提出的查找表相比,这个效率是多少?
(再次,我用C编码所以我没有像lower_bound
这样的迭代器函数)
答案 0 :(得分:2)
算法的效率为O(log x)
,而Dave(对2的幂进行二进制搜索)的效率为O(log log x)
。所以他渐渐变速了。
当然,最快的方法是使用BSF
指令。
答案 1 :(得分:2)
作为旁注,您应该考虑将函数get_power_of_two
重命名为get_log_two
。
如果您经常调用此函数,则可以初始化一个相对较小的查找表。
使用此表,您可以逐字节检查每个输入编号,如下所示:
#include <limits.h>
static unsigned int table[1<<CHAR_BIT];
void init_table() // should be called once
{
for (unsigned int n=0; n<CHAR_BIT; n++)
table[1<<n] = n;
}
unsigned int get_log_two(unsigned int x)
{
for (unsigned int n=0; x>0; n+=CHAR_BIT, x>>=CHAR_BIT)
{
unsigned int y = x & ((1<<CHAR_BIT)-1);
if (y > 0)
return n+table[y];
}
return ~0; // will never be reached during runtime
}
就“纯学术复杂性”而言,这不一定是最有效的方法,因为函数get_log_two
不执行二元搜索。
尽管如此,考虑到任何平台(通常为4)上sizeof(unsigned int)
的价值相对较小,对于平均和最差情况,它几乎会产生相同的性能。< / p>
答案 2 :(得分:2)
除了先前其他人所说的方式,例如严格依赖于底层ISA的BSF或CLZ指令,还有其他一些方法,例如:
http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
事实上,你可以在那里找到很多'有点笨拙的黑客'。答案 3 :(得分:1)
这是一个有趣的想法......
如果您知道只设置了1位,那么为什么不使用开关?
unsigned int get_log_two(unsigned int x)
{
switch(x)
{
case 1<<0: return 0;
case 1<<1: return 1;
case 1<<2: return 2;
case 1<<3: return 3;
case 1<<4: return 4;
case 1<<5: return 5;
case 1<<6: return 6;
case 1<<7: return 7;
case 1<<8: return 8;
case 1<<9: return 9;
case 1<<10: return 10;
case 1<<11: return 11;
case 1<<12: return 12;
case 1<<13: return 13;
case 1<<14: return 14;
case 1<<15: return 15;
}
return 0;
}
将此扩展到31,你将有一个很好的功能。 这应该很快;但只有在设置了一个位时才会起作用。
答案 4 :(得分:1)
在你的情况下,因为你知道只设置了一个位,所以它足以计算尾随零。这可以在没有硬件指令的情况下快速完成。看看这个answer,这就是下面的代码来自的地方(我不是一个篡改完美......有时候)。
unsigned v; // this is the number with one bit set
unsigned r; // this becomes the exponent in v == pow(2, r)
static const unsigned MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[((unsigned)((v & -v) * 0x077CB531U)) >> 27];
在你的情况下,因为v
只设置了一个位,所以你不需要找到最低位集;因此,您可以跳过v & -v
。你的代码版本就变成了这个:
unsigned v; // this is the number with one bit set
unsigned r; // this becomes the exponent in v == pow(2, r)
static const unsigned MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[(v * 0x077CB531U) >> 27];
有关详细信息,请参阅链接,然后链接到其源信息。