计算一个整数的二进制表示有多少个1(别人的最快算法)

最近看了这篇文章 的第一道题

题目本意是怎么判断这段代码的输出

int func(x)
{
    int countx =0;
    while(x)
    {
          countx ++;
          x = x&(x-1);
     }
    return countx;
}

但深入一想,这不就是求任意整数的二进制表示里有多少个1吗?

要想解决上述问题,我能想到的最快方法是按位右移,累计每个移出的数,直到原数为0

int func2(int x)
{
    int countx =0;
    while (x)
    {
        if (x & 1)
            countx ++;
        x = x >> 1;
    }
    return countx;
}

但是很明显,我的算法在二进制表示含有较多中间0时,效率很低,而别人的算法就不存在该问题,而且没有if分支判断,避免CPU指令Cache miss,方便并行化

总之,该算法求解1个数,类似于辗转相除法求最大公约数,都是基于某种数学原理,性能自然比愚蠢的枚举不知道高到哪里去了


该算法的原理,我晚上思考了下,觉得是对【判断一个整数是否为2的幂】算法的扩展应用

【判断一个整数是否为2的幂】算法

bool power_of_2(int x)
{
    if (0 == (x & x-1))
        return true;
    else
        return false;
}

该算法的思路是,如果整数i为2的幂,则其二进制表示必然只有1位为1,假设是 第N位为1,则 0到N-1位必然为0,且N+1到31位(假设是32位CPU)全零

令j = i-1,则j的二进制表示就是第N位为00到N-1位全1,N+1到31位还是全零,

因为减法需要从高位借位,对于2的幂整数,能借位的只有唯一的那个1,所以N位必然变为0

又因为减数为1,所以0到N-1位必然全为1

这样i和j的【按位与】运算,必然等于0


将上述思路拓展

对于任意一个整数,都可以看成一系列幂的和,比如

520,就是512+8

1111,就是1024+64+16+4+2+1

当对该整数进行【减一】和【按位与】操作时,最低等级的幂先变为零

然后再次执行【减一】和【按位与】操作,次低等级的幂也变为零

以此类推,每次循环都将一个非零幂项清除(为零的幂项不发生借位,所以自动忽略)

最后变为全零,循环退出

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值