快速求一个32位无符号整数二进制中为1的个数

来源:互联网 发布:淘宝代销退货 编辑:程序博客网 时间:2024/05/22 08:02

一个数5,二进制表示法,为101,则有2个1,如何快速求出一个32位无符号整数中1的个数,如果要我们来做,我们可能会进行一个32次的循环,从1至32,依次测试相应的位;这个算法不是最优的。以下算法效率的要快很多:http://aggregate.ee.engr.uky.edu/MAGIC/。源代码如下

 

unsigned int
ones32(register unsigned int x)
{
        /* 32-bit recursive reduction using SWAR...
       but first step is mapping 2-bit values
       into sum of 2 1-bit values in sneaky way
    */
        x -= ((x >> 1) & 0x55555555);
        x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
        x = (((x >> 4) + x) & 0x0f0f0f0f);
        x += (x >> 8);
        x += (x >> 16);
        return(x & 0x0000003f);
}

 

刚开始看这段代码,有一点晕,仔细分析一下,还是有点意思的。下面我就逐行分析这个函数

 

先看第一行代码,x -= ((x >> 1) & 0x55555555);

这行代码的作用是,将相邻两位中1的个数统计出来,我们举个例子,一个数x=abcdefgh

则x>>1=abcdefg,再将上一步的结果与0x55555555相与,0x55555555,这个数有一个特点,从低住高数所有偶数位都为0

,所以与这个数想与的作用是把一个数中所有的偶数位上的1清掉,最后再从X减掉这个数,我们看例子,x=abcdefgh

,((x>>1)&0x55555555)=0a0c0e0g,然后abcdefgh-0a0c0e0g,这里看前两位:gh-0g,这里有4个可能,

第一种可能:g种h都为1,则gh-0g=11-01=10,10正好为十进制的2,表示原数abcdefgh,最后两位gh中有2个1;

第二、三种可能:g种h有一个为1,则gh-0g=10-01,或者是01-00,不管怎么样,最后结果都是01,01为十进制的1,表示原数abcdefgh,最后两位gh中有1个1;

第四种可能:两个都为0,结果也是0,表示原数最后两位gh有0个1。

 

再来看第二行代码

x = (((x >> 2) & 0x33333333) + (x & 0x33333333));

这行代码的作用是将相邻4位的数加起来,也就是统计原数中相邻4位中1的个数,前面我们说过,第一步得到的结果是原数中相邻两位中1的个数,第一步得到的数连续两位的组合只有三种情况:10,01,00;

第一个表达式:((x >> 2) & 0x33333333)

其作用是将上一步的结果右移两位再与0x33333333相与,作用是将一个数中从低住高数,位置是偶数的连续两位清0,即从低往高数,将第3,4,有点念绕口令的感觉了。我们举个例子, x=abcdefgh,经过((x >> 2) & 0x33333333),则结果是00ab00ef,后面 (x & 0x33333333)),得到00cd00gh,两数相加:00ab00ef+00cd00gh,后四位的结果等于ef+gh,头4位的结果是ab+cd,其作用是第一步的结果以4位为一个单位,两位两位的相加,经过这一步后,每4位就是原数相应4位中1的个数。举个例子01001010,经过这一步,为00010010,头4位0001表示原数头4位有1个1,后4位0010,表示原数后4位有2个1。

 

第二、三、四行的代码功能差不多,只不过中将相邻4(8、16)位中的数进行两两相加。

 

最后一步(x & 0x0000003f),因为最多只有32个1,即最大数是32,与3f相与,可把其余位屏蔽掉!

 

原创粉丝点击