获得尾随1位的数量

时间:2010-03-04 16:05:28

标签: c++ algorithm optimization bit-manipulation

我可以做一些有效的按位操作来获得整数结束的设置位数吗?例如,11 10 = 1011 2 将是两个尾随1位。 8 10 = 1000 2 将为0尾随1位。

是否有比线性搜索更好的算法?我正在实现一个随机跳过列表并使用随机数来确定插入元素时的最大级别。我在C ++中处理32位整数。

编辑:汇编程序是不可能的,我对纯C ++解决方案感兴趣。

12 个答案:

答案 0 :(得分:11)

计算~i & (i + 1)并将结果用作包含32个条目的表中的查找。 1表示零1,2表示1,4表示两个1,依此类推,但0表示32个1。

答案 1 :(得分:7)

Bit Twiddling Hacks页面有许多用于计算尾随零的算法。其中任何一个都可以通过简单地反转您的数字进行调整,并且可能有巧妙的方法来改变算法,而不是这样做。在具有廉价浮点运算的现代CPU上,最好的可能就是:

unsigned int v=~input;            // find the number of trailing ones in input
int r;                     // the result goes here
float f = (float)(v & -v); // cast the least significant bit in v to a float
r = (*(uint32_t *)&f >> 23) - 0x7f;
if(r==-127) r=32;

答案 2 :(得分:6)

从Ignacio Vazquez-Abrams那里得到答案并用计数而不是表格来完成:

b = ~i & (i+1);   // this gives a 1 to the left of the trailing 1's
b--;              // this gets us just the trailing 1's that need counting
b = (b & 0x55555555) + ((b>>1) & 0x55555555);  // 2 bit sums of 1 bit numbers
b = (b & 0x33333333) + ((b>>2) & 0x33333333);  // 4 bit sums of 2 bit numbers
b = (b & 0x0f0f0f0f) + ((b>>4) & 0x0f0f0f0f);  // 8 bit sums of 4 bit numbers
b = (b & 0x00ff00ff) + ((b>>8) & 0x00ff00ff);  // 16 bit sums of 8 bit numbers
b = (b & 0x0000ffff) + ((b>>16) & 0x0000ffff); // sum of 16 bit numbers

在结尾b将包含1的计数(掩码,添加和移位计数1)。 除非我当然是傻瓜。使用前测试。

答案 3 :(得分:4)

GCC有__builtin_ctz,其他编译器也有自己的内在函数。只需使用#ifdef

保护它
#ifdef __GNUC__
int trailingones( uint32_t in ) {
    return ~ in == 0? 32 : __builtin_ctz( ~ in );
}
#else
// portable implementation
#endif

在x86上,这个内置程序将编译为一个非常快速的指令。其他平台可能会慢一点,但大多数都有某种位计数功能,这些功能将超过纯C运算符的功能。

答案 4 :(得分:2)

可能有更好的答案,特别是如果汇编程序不是不可能的,但一个可行的解决方案是使用查找表。它将有256个条目,每个条目返回连续尾随1位的数量。将其应用于最低字节。如果是8,请申请下一个并继续计算。

答案 5 :(得分:2)

实施Steven Sudit的想法......

uint32_t n; // input value
uint8_t o;  // number of trailing one bits in n

uint8_t trailing_ones[256] = {
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8};

uint8_t t;
do {
  t=trailing_ones[n&255];
  o+=t;
} while(t==8 && (n>>=8))

1(最佳)到4(最差)(平均1.004)次(1次查找+ 1次比较+ 3次算术运算)减去一次算术运算。

答案 6 :(得分:1)

Hacker's Delight中给出了一个非常快速的方法来查找尾随0的数字。

您可以补充整数(或更一般地说,单词)来查找尾随1的数字。

答案 7 :(得分:0)

此代码计算从here获取的尾随零位数(还有一个版本依赖于IEEE 32位浮点表示,但我不相信它,模数/除法接近看起来很光滑 - 也值得一试):

int CountTrailingZeroBits(unsigned int v) // 32 bit
{
    unsigned int c = 32; // c will be the number of zero bits on the right

    static const unsigned int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};
    static const unsigned int S[] = {1, 2, 4, 8, 16}; // Our Magic Binary Numbers

    for (int i = 4; i >= 0; --i) // unroll for more speed
    {
        if (v & B[i])
        {
            v <<= S[i];
            c -= S[i];
        }
    }

    if (v)
    {
        c--;
    }

    return c;
}

然后计算尾随的数据:

int CountTrailingOneBits(unsigned int v)
{
    return CountTrailingZeroBits(~v);
}

答案 8 :(得分:0)

答案 9 :(得分:0)

基于Ignacio Vazquez-Abrams的答案实施

uint8_t trailing_ones(uint32_t i) {
 return log2(~i & (i + 1));
}

log2()的实现留给读者练习(参见here

答案 10 :(得分:0)

根据@ phkahler的回答,您可以定义以下预处理器语句:

#define trailing_ones(x) __builtin_ctz(~x & (x + 1))

当你向前所有的那个留下一个时,你可以简单地计算尾随的零。

答案 11 :(得分:-2)

我有这个样本:

#include <stdio.h>

int trailbits ( unsigned int bits, bool zero )
{
    int bitsize = sizeof(int) * 8;
    int len = 0;
    int trail = 0;
    unsigned int compbits = bits;

    if ( zero ) compbits = ~bits;

    for ( ; bitsize; bitsize-- )
    {
        if ( compbits & 0x01 ) trail++;
        else
        {
            if ( trail > 1 ) len++;
            trail = 0;
        }
        compbits = compbits >> 1;
    }

    if ( trail > 1 ) len++;

    return len;
}

void PrintBits ( unsigned int bits )
{
    unsigned int pbit = 0x80000000;
    for ( int len=0 ; len<32; len++ )
    {
        printf ( "%c ", pbit & bits ? '1' : '0' ); 
        pbit = pbit >> 1;
    }
    printf ( "\n" );
}

void main(void)
{
    unsigned int forbyte = 0x0CC00990;

    PrintBits ( forbyte );

    printf ( "Trailing ones is %d\n", trailbits ( forbyte, false ));
    printf ( "Trailing zeros is %d\n", trailbits ( forbyte, true ));
}