汉明重量(算法-数学)

引题

输入一个数N(10进制),输出N的二进制中“1”个数

这是一道谷歌、微软等大厂的一道经典面试题(原题给的二进制N)

输入格式:

7

输出格式:

3

解析:

7(10)==>0111(2)

含有三个1,故输出3;

虽然在cpp中我们有__builtin_popcount(n)函数,可以很快的帮助我们计算出这个结果,但是我们学习的思想应该是剖析这个问题。工具随便用,但我们也要想想没有工具的时候我们应该怎么办。

分析

解法一:我曾经的一位伟大的数学老师告诉我:“数学的最高境界就是一个一个数”,但问题是该如何在计算机中实现呢?

       其实计算机执行的是数学指令,其实这也是再问,在数学上如何实现?我们引进两个位操作符:

按位与(&)和shift right(>>)

相信各位在《数字电路》和《计算机组成原理》中都见过这两种运算符物理方面的硬件操作,当然本次我们不讲这两本书。

按位与(&)

应用范围:整数范围且二进制;

运算:同位当中,均为1结果为1,否则为0;

例如:

3==>0011

5==>0101

3&5==>0001

shift right(>>)

应用范围:必须在整数范围内进行;

运算:把二进制数向右移动一位,右边丢弃,左边补0;

如果给定一个x(一位),做x&1的运算,那么其运算的结果就是在数x中“1“的个数,再结合我们的shift right操作,我们就能从右向左,对每一位进行计算来去数“1”的个数

我们实际讲解一下

第一次cnt为0;cnt+=0;

x

1

1

1

0

&

0

0

0

1

运算结果

0

0

0

0

shift right操作;

第二次cnt为0;cnt+=1;

x

0

1

1

1

&

0

0

0

1

运算结果

0

0

0

1

shift right操作;

第三次cnt值为1;cnt+=1;cnt值为2

x

0

0

1

1

&

0

0

0

1

运算结果

0

0

0

1

shift right操作;

第四次cnt值为2;cnt+=1;cnt值为3

x

0

0

0

1

&

0

0

0

1

运算结果

0

0

0

1

这样我们就得到的最后的结果为3;

你也就成功入门了,当然这还是拿不到offer的。我们发现解法一中,对每一位都进行了运算,这大大增加了时间的成本,所以我们可以优化做到只数1,0直接跳过

解法二:给定一个x,让x进行x&(x-1)操作,然后x&(x-1)再与x&(x-1)-1进行按位与操作,直到最后的结果为0,那么执行的次数就是x中含有1的个数

举个例子:

第一次

X

1

0

0

0

1

x-1

1

0

0

0

0

运算结果

1

0

0

0

0

第二次

X

1

0

0

0

0

x-1

0

1

1

1

1

运算结果

0

0

0

0

0

共执行两次,所以“1”的个数为2;

我们不难看出,每次进行-1的操作,无非就是消除数据中最后一位的1,直到1消除完毕。

但是,这还是用到了循环,需要消耗的时间无法确定。

所以我们还有接下来的方法:

解法三:

a
b0 = ( a>>0 ) & 01 01 01 01 01 01 01 01
b1 = ( a>>1 ) & 01 01 01 01 01 01 01 01
c = b0 + b1
d0 = ( c>>0 ) & 0011 0011 0011 0011
d2 = ( c>>2 ) & 0011 0011 0011 0011
e = d0 + d2
f0 = ( e>>0 ) & 00001111 00001111
f4 = ( e>>4 ) & 00001111 00001111
g = f0 + f4
h0 = ( g>>0 ) & 0000000011111111
h5 = ( g>>8 ) & 0000000011111111
i = h0 + h8

这是一个16位数据的计算过程,无论你数据中含有多少个1,它都是固定的计算次数,时间复杂度也是O(logN),这个算法中涉及到了归并的思想

我们举个例子

x

1

0

1

1

0

0

0

1

相邻两位里1的个数

1

2

0

1

相邻四位里1的个数

3

1

相邻八位里1的个数

4

&01===>处理相邻的两位

&0011==>处理相邻的四位

&00001111===>处理相邻的八位

看到这里,你就离你的offer更近了,但还不行。在实际工作中,我们要有应对工作的方法。

方案四:空间换时间

我们可以使用最朴素的方式,把所需要范围内的所有数据都进行计算,把它储存在一张表中,需要的时候拿出来查表就行了。

解题

最后在让我们回归那道题:

讲了这么多,我们也需要程序上的操作;

这里我们使用解法二

int GetOneCount(int x){

       int count = 0;

       if(x == 0){

              return 0;

       }

       while(x != 0){

              count++;

              x = x & (x - 1);

       }

       return count;

}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值