计算一个数字中的零组位数

时间:2010-12-21 12:18:14

标签: c

如何计算数字中零组位的数量?组位 是任何连续的零或一位,例如,2表示 as .... 0000000000000000010最少有两个零位组 重要的一点,小组从一开始。 此外,如果有的话,我对位操作算法的需求很大 一个有参考,请分享

6 个答案:

答案 0 :(得分:3)

以下是一些提示:

  • if (x & 1) {...}检查x的最低有效位是否已设置;
  • x >>= 1x的值向右移动一位;
  • 当你进行位操作时,请注意负数。

答案 1 :(得分:2)

最简单的方法是使用位掩码的移位以及按位“和”操作来计算循环中从1到0的转换次数。如果为0,还需要检查第一位并将获得的数量增加一。

答案 2 :(得分:2)

以下是一些有趣的方法。开头的#define-s仅用于以二进制表示法表示函数输入。执行该工作的两个函数是主题的变体:第一个使用de Bruijn序列和查找表来计算参数中有多少尾随零,并相应地进行其余操作。第二个使用Mod37表来做同样的事情,这在概念上非常相似,但涉及模运算而不是乘法和位移。其中一个更快。懒得找出哪一个。

这比明显的解决方案要多得多。但是如果在输入中主要有零,这可能非常有效,因为它只需要每个1位进行一次循环迭代(实际上只需要一个分支),而不是每个位进行一次循环迭代。

#define HEX__(n) 0x##n##LU

#define B8__(x)  ((x&0x0000000FLU)?  1:0)   \
                +((x&0x000000F0LU)?  2:0)   \
                +((x&0x00000F00LU)?  4:0)   \
                +((x&0x0000F000LU)?  8:0)   \
                +((x&0x000F0000LU)? 16:0)   \
                +((x&0x00F00000LU)? 32:0)   \
                +((x&0x0F000000LU)? 64:0)   \
                +((x&0xF0000000LU)?128:0)

#define B8(d) ((unsigned char)B8__(HEX__(d)))

#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))

#define B32(dmsb,db2,db3,dlsb) (((unsigned long)B8(dmsb)<<24) \
                              + ((unsigned long)B8(db2)<<16)  \
                              + ((unsigned long)B8(db3)<<8)   \
                              + B8(dlsb))

unsigned int count_zero_groups_debruijn(unsigned int v)
{
    // number of zero-bit groups (set to 1 if high-bit is zero)
    unsigned int g = (~(v & 0x80000000)) >> 31; 

    // lookup table for deBruijn 
    static const int _DeBruijnTable[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
    };

    do {
        // get number of trailing zeros in v
        int tz = _DeBruijnTable[((v & -v) * 0x077CB531U) >> 27];
        // increment zero group count if more than 1 trailing zero
        g += (tz > 0) * 1;
        // shift out trailing zeros and the preceding 1
        v = v >> (tz+1);
    } while (v);

    return g;
}

unsigned int count_zero_groups_mod37(unsigned int v)
{
    // number of zero-bit groups (set to 1 if high-bit is zero)
    unsigned int g = (~(v & 0x80000000)) >> 31; 

    // lookup table for mod37
    static const int _Mod37Table[] = 
    {
         0,  0,  1, 26,  2, 23, 27,  0,  3, 16, 24, 30, 28, 11,  0, 13,  4,
         7, 17,  0, 25, 22, 31, 15, 29, 10, 12,  6,  0, 21, 14,  9,  5, 20,
         8, 19, 18
    };

    do {
        // get number of trailing zeros in v
        int tz = _Mod37Table[(v & -v) % 37];
        // increment zero group count if more than 1 trailing zero
        g += (tz > 0) * 1;
        // shift out trailing zeros and the preceding 1
        v = v >> (tz+1);
    } while (v);

    return g;
}

int main(int argc, char* argv[])
{
    printf("zero groups: %d (should be 6)\n", count_zero_groups_debruijn(B32(10011001,10000000,00001001,00010011)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_debruijn(B32(10011001,10000000,00001001,00010000)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_debruijn(B32(00011001,10000000,00001001,00010001)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_debruijn(B32(00011001,10000000,00001001,00010000)));
    printf("zero groups: %d (should be 0)\n", count_zero_groups_debruijn(B32(11111111,11111111,11111111,11111111)));
    printf("zero groups: %d (should be 1)\n", count_zero_groups_debruijn(B32(00000000,00000000,00000000,00000000)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_mod37   (B32(10011001,10000000,00001001,00010011)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_mod37   (B32(10011001,10000000,00001001,00010000)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_mod37   (B32(00011001,10000000,00001001,00010001)));
    printf("zero groups: %d (should be 6)\n", count_zero_groups_mod37   (B32(00011001,10000000,00001001,00010000)));
    printf("zero groups: %d (should be 0)\n", count_zero_groups_mod37   (B32(11111111,11111111,11111111,11111111)));
    printf("zero groups: %d (should be 1)\n", count_zero_groups_mod37   (B32(00000000,00000000,00000000,00000000)));
    return 0;
}

答案 3 :(得分:1)

安德鲁的解决方案毫无疑问是最简单的设计和实现,但我不禁想知道是否有更快的解决方案使用更大的位掩码。

在求职面试中,我被要求编写一些代码来识别最重要的设置位。在花了几分钟时间使用缩小的位掩码进行超薄超快速二进制搜索后,我突然意识到可以进一步优化,并且进一步导致大量纸张上的大量涂鸦,审查员看着我茫然地问我是否知道如何使用for循环。

也许他应该让我使用for循环来解决问题。

无论如何,这里存在类似的解决方案并非不可能。

答案 4 :(得分:0)

您可以重复使用整数除法和模运算符来提取位,并跟踪循环中的组。这听起来像是一个家庭作业问题,所以我不确定你能为你提供一个完整的解决方案吗?考虑一下你可以使用这个算法来获得一个正整数的base-2表示(实际上它适用于任何基数&gt; = 2):

int example = 40;
while (example > 0) {
    printf("%d\n", example % 2);
    example /= 2;
}

这将以相反的顺序打印出位(即从最不重要的位置开始)。从这里开始计算想要计算的群体并没有太多工作要做。我应该走得更远,还是可以从这里拿走它?

答案 5 :(得分:0)

试试这段代码 没有测试..如果你发现了一些错误,请告诉我

数字是输入。

int main()
{
    int count = 0;

    int num = 0xF0000000, mask = 1; /*size of mask and num should be same. */

    int i;
    int flag= 1;


    i = sizeof(num) * 8;

    while(--i) {
         if(flag && !(num & mask)) {
            count++;
            flag = 0;
        }
        else if(num & mask)
            flag = 1;

    mask = mask<<1;
 }

 printf("\n%d\n",count);
 }

Thanks
cexpert
http://learnwithtechies.com