位操作[c/c++]
常见的位操作
循环左移两位 : (a >> (N - 2) | a << 2)
循环右移两位 : (a << (N - 2) | a >> 2)
判断奇数 : (x&1)==1
判断偶数 : (x&1)==0
二进制数字尾部第一个1所处的位置: x & -x
二进制数字表示包含几个1:n-1 & n
& | | | ^ | ~ | >> | << |
---|---|---|---|---|---|
逻辑与运算 | 逻辑或运算 | 异或运算 | 非运算 | 右移运算 | 左移运算 |
利用位运算来表示集合运算
可以利用二进制来表示{0,1,2,3,…,n - 1}的子集S;从右往左第i位(各位从0开始编号)表示元素i是否在集合S中,下展示了二进制0100011000110111是如何表示{0,1,2,4,5,9,10,14}的
0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
位运算与集合运算的对应关系
A | B | A & B | A | B | A ^ B | |
---|---|---|---|---|---|
二进制 | 10110 | 01100 | 00100 | 11110 | 11010 |
集合 | {1,2,4} | {2,3} | {2} | {1,2,3,4} | {1,3,4} |
注:
A&B、A|B、A^B分别对应集合的交,并,对称差运算。
空集{Ø}表示为0
全集{0,1,2,3,…,n - 1}对应的值定义为All_bits = (1<<n) - 1;
如果A对应的全集为All_bits,则A的补集为A ^ All_bits;
利用位运算表示加法操作
解题趣谈
本人在leetcode上刷题遇到了这个有趣的题目
Sum of Two Integers
int getSum(int a, int b) {
long long carry; // 64-bit
while (b != 0) {
//b在第2轮循环之后都是作为进位出现的。如果进位为0,代表循环结束
carry = a & b;//可以对比真值表观察到:carry恰好代表了进位。只不过进位的位置在原本应该在的位置的后一位
a = a ^ b;//模拟无进位的加法运算[可以对比真值表]
b = ((carry & 0xffffffff) << 1); //将carry限制在32bit之内。防止溢出。
//将carry左移一位是为了将进位放在正确的位置上面
}
return a;
}
- 通过真值表可以发现carry=a & b 和不带进位的sum = a ^ b
x | y | x & y | x ^ y |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 |
1 | 0 | 0 | 1 |
1 | 1 | 1 | 0 |
模拟全加器的加减乘除运算
计算机加法的实现
半加器真值表
x | y | sum | carry |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
由此可以看出来:
半加器中sum = a + b对应于a ^ b
半加器中carry(a + b的进位)对应于a & b
在半加器的基础上实现多位全加器
x | y | icarry(低位进位) | sum | carry |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
对比真值表可以发现:
sum = x ^ y ^ icarry
carry = (x & y) | (x & icarry) | (y & icarry)
计算机减法的实现
减法实质上是一个数加上另一个数的相反数
因此实现减法,只要对减数求2-补码(取反加一),然后跟被减数相加即可得到差值
也就是说:b = getsum(~b,1)//getsum的具体实现过程前面[解题趣谈]有,本节不再赘述。
sum = getsum(a,b)
计算机乘法和除法的实现
- 后续补充
请参考:用基本位运算实现加减乘除
位运算的相关应用
请务必注意
以后遇到关于2的题或者是真假的题,一定要敏感啊,想到二进制
1.判断是否是2的幂
思路分析:
2的整数次方对应的二进制的最高位上只有一个1,那么将该数字减去1再与该数字进行与运算,如果是2的整数次方,那么结果一定为0
if(n & (n - 1) == 0){
printf("是偶数!\n");
}
1.2判断是否是4的幂(上题的变种形式)
题目描述:
Power of Four
思路描述:
请注意:如果一个数是4的幂,那么必然是最高为是1,后面跟着偶数个0
首先如果一个数是4的幂,那么必然满足2的幂的条件。那么这个数只有最高位是1
其次再与0x55555555,如果该数的最高位的1出现在奇数位上。(在这里我们设定0x55555555下标从1开始,而不是从0开始,也就是从1…32位)
bool isPowerOfFour(int num) {
if(num < 0) return false;
if ( num & (num - 1) ) //判断x是否为2的幂次方
return false;
return num & 0x55555555; //判断1是不是出现在奇数位上
}
2.判断奇数偶数
//比较常用的一个小知识点
if((i&1) == 0){//这个只是一个小技巧,因为位运算的速度远远超过其余运算
cout<<"偶数"<<endl;
}else{
cout<<"奇数"<<endl;
}
3.计算二进制数字从尾部开始连续1的数目
int v;
int count = 0; // count用来保存计算的结果
while(v & 1){
v = v >> 1;
count ++;
}