最近在跟着y总学算法。
今天学到了两个很经典的位运算,x&(-x)和x&(x-1):
x&(-x):保留二进制下最后出现的1的位置,其余位置置0(即一个数中最大的2的n次幂的因数
x&(x-1):消除二进制下最后出现1的位置,其余保持不变
第二个算法我之前写过博客,C语言求a的二进制表示中有几个1_工业废气的博客-CSDN博客
那么问题就在于第一个算法,有什么作用?
目录
-x的含义
当x为奇数时
当x为偶数时
当x为0时
将偶数拆成一个偶数和奇数相乘
LowBit
-x的含义
-x在计算机存储是用x的补码存储,是在x的值的基础上按位取反(~x)后再加1,也就是说:
x & -x == x & (~x + 1)
当一个奇数加1时,它表示的二进制数则会发生进位,这样最低位的那些连续的1都会成为0,比如:
0000000001111111 + 1 = 0000000010000000
当x为奇数时
举个例子:
000001001111按位取反后就成为了111110110000。那么再加上1就成为了111110110001。
将两个数进行按位与操作:
000001001111 & 111110110001 = 000000000001
也就是说,当一个奇数与它的负值按位与时,结果为1。
当x为偶数时
再举个例子:
000001001110按位取反后就成为了111110110001。再加上1就成为了111110110010。
将两个数进行按位与操作:
000001001110 & 111110110010 = 000000000010
这个值与原值的末位0的个数是一致的,而且这个结果只有一位值是1, 其他位均是0。
二进制000001001110的十进制是78,而二进制10的十进制是2,是能整除78的最大的2的幂。
当一个偶数与它的负值相与时,结果是能整除这个偶数的最大的2的幂。
即:当x为偶数时,m = x & -x , 则 x % m = 0, 且 m = 2^k。
当x为0时
当x为0时,x&(-x)的结果为0。
将偶数拆成一个偶数和奇数相乘
请看代码:
#include <stdio.h>
int main()
{
int x = 0;
printf("请输入要拆分的偶数\n");
scanf("%d",&x);
if ((x & (-x)) == 1)
{
printf("%d是奇数", x);
}
else if ((x & (-x)) == 0)
{
printf("%d是零", x);
}
else
{
int even_number = x & (-x);
int odd_number = x / even_number;
printf("%d = %d × %d", x, even_number, odd_number);
}
return 0;
}
当输入48时,输出结果如下:
LowBit
x&(-x)一般是用来获取某个二进制数的LowBit,在树状数组中会用到。
LowBit(x)是x的二进制表达式中最低位的1所对应的值。
int LowBit(int x)
{
return x & (-x);
}