计算整数二进制表示的1的个数

1. 利用与运算或者非运算

(1)把一个整数减去1之后再和原来的整数做与运算,得到的结果相当于是把整数的二进制表示中的最右边一个1变成0 。

int odd_ones(int x)
{
	int count = 0;
    while(x){
        x &= (x-1);
        count++;
    }
    return count;
}

(2)把一个整数加上1之后再和原来的整数做或运算,得到的结果相当于是把整数的二进制表示中的最右边一个0变成1 。 

int odd_ones(int x)
{
    int n = 0; 
    while((x+1)){  //将0全部替换为1时溢出
        n++;  
        x|=(x+1);
    }
    return 32-n;//长度-0的个数
}

2. 移位法

​int count(unsigned x){
    int count=0;
    while(x){
        if(x & 0x1)
            ++count;
        x=(x>>1);
    }
    return count;
}

3. 分冶法

这个方法是Hamming weight Wikipedia上面提出来的,很高效,比上面的两种方法都要高效。采用了分治法的思想,具体实现如下:

int Hamming_weight(uint32_t n ){  
    n = (n&0x55555555) + ((n>>1)&0x55555555);  
    n = (n&0x33333333) + ((n>>2)&0x33333333);  
    n = (n&0x0f0f0f0f) + ((n>>4)&0x0f0f0f0f);  
    n = (n&0x00ff00ff) + ((n>>8)&0x00ff00ff);  
    n = (n&0x0000ffff) + ((n>>16)&0x0000ffff);
    return n;  
}

代码解析: 乍一看,立马懵逼,很难看懂为何那么写。先将代码中用到的几个常数对比一下其特点,再联想到分治的思想,你可能就懂了。

0x5555……这个换成二进制之后就是01 01 01 01 01 01 01 01…… 
0x3333……这个换成二进制之后就是0011 0011 0011 0011…… 
0x0f0f………这个换成二进制之后就是00001111 00001111……

看出来点什么了吗? 如果把这些二进制序列看作一个循环的周期序列的话,那么第一个序列的周期是2,每个周期是01,第二个序列的周期是4,每个周期是0011,第三个的周期是8,每个是00001111,第四个和第五个以此类推。看出了这些数的特点,再回头看代码你会轻松的发现代码的意义。算法的实现原理是将32位无符号整数分成32个段,每个段即1bit,段的取值可表示当前段中1的个数,所以将32个段的数值累加在一起就是二进制中1的个数,如何累加呢?这就是代码做的事情。 (n&0x55555555)+((n>>1)&0x55555555) 将32位数中的32个段从左往右把相邻的两个段的值相加后放在2bits中,就变成了16个段,每段2位。同理(n&0x33333333)+((n>>2)&0x33333333)将16个段中相邻的两个段两两相加,存放在4bits中,就变成了8个段,每段4位。以此类推,最终求得数中1的个数就存放在一个段中,这个段32bits,就是最后的n。
4. 个人见到的一种方法,跟分冶法相似,但只能计算出1的个数为奇数个还是偶数个,因为每次计算后有效的只有最后一位

bool odd_ones(unsigned x)
{
	x = x ^ (x >> 1);
	x = x ^ (x >> 2);
	x = x ^ (x >> 4);
	x = x ^ (x >> 8);
	x = x ^ (x >> 16);
	return x & 1;
}

………………………………

本文只是在博主Dablelv的文章做了一些修改,仅为个人学习使用。原文内容更加全面翔实,特附上链接 
原文: https://blog.csdn.net/k346k346/article/details/53316953

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值