浅谈lowbit运算 关于lowbit运算的相关知识

本篇随笔简单讲解一下计算机中位运算的一类重要运算方式——(lowbit)运算。

lowbit的概念

我们知道,任何一个正整数都可以被表示成一个二进制数。如:

[(2)_{10}=(10)_2 ]

[(4)_{10}=(100)_2 ]

[cdots ]

那么定义一个函数(f=lowbit(x)),这个函数的值是(x)的二进制表达式中最低位的(1)所对应的值。

比如:

[(6)_{10}=(110)_2 ]

那么(lowbit(6))就等于(2),因为((110)_2)中最低位(就是从右往左数的第二位)对应的数是(2^1=2)

所以假设一个数的二进制最低位的(1)在从右往左数的第(k)位,那么它的(lowbit)值就是

[2^{k-1} ]

lowbit函数的实现

lowbit函数实现有两种方式:

一、

x&(x^(x-1))

二、

x&-x

简单解释一下:

我们得到lowbit的值,只需要得到最后一个1的位置,并且把除了这个位置之外的所有位置全部置成零。然后输出就可以。

那么我们看一看x&(x^(x-1))

拿上面的6举例:

[(110)_2-1=(101)_2 ]

我们发现,根据小学数学减法运算的借位原则(滑稽),对一个二进制数进行减1,那么会出现从这个这个数的最后一个1开始到最后的所有数都取反,即构成一个(01111cdots)的串。

我们把这个数与原数异或,就会造成:第一个1以后的数(包括第一个1)全部取1.其他的位全部取0.即构成一个由一堆0后面跟一堆1的串。

那么再把原式做一个与运算,那么除了原来的那个1(对应位都是1)为1,其他位全是0,完成任务。

那么我们再看一看x&-x

根据计算机补码的性质。

补码就是原码的反码加一

如:

[(110)_2=6 ]

反码:

[(001)_2 ]

加一:

[(010)_2 ]

可以发现变为反码后 x 与反码数字位每一位都不同, 所以当反码加1后神奇的事情发生了,反码会逢1一直进位直到遇到0,且这个0变成了1,所以这个数最后面构造了一个 100… 串。 由于是反码,进位之后由于1的作用使进位的部分全部取反及与原码相同,所以可以发现 lowbit 以前的部分 x 与其补码即 -x 相反, lowbit x 与 -x 都是1,lowbit 以后 x 与 -x 都是0 所以 x&-x 后除了 lowbit 位是1,其余位都是0。符合条件。

用lowbit运算统计1的个数

我们可以使用lowbit运算统计一个整数的二进制形式下1的个数。

实现原理很简单啦,就是:我们先用lowbit运算找出(lowbit(x)),然后用原数减去这个数,依次循环,直到为0为止。

这也是树状数组的实现原理。

代码:

while(x)
{
	x-=x&-x;
	ans++;
}

(巨短无比)

lowbit运算的应用

关于lowbit运算,最著名的应用应该算是树状数组。但是lowbit的神妙远远不止树状数组,在很多二进制和位运算的相关题目中,都有lowbit运算的影子。甚至,在状态压缩DP中,lowbit也扮演着一份不可忽视的角色。