位运算简介及实用技巧(一):基础篇
位运算简介及实用技巧(二):进阶篇(1)
位运算简介及实用技巧(三):进阶篇(2)
位运算简介及实用技巧(四):实战篇

以下作者为知乎力扣

位操作求绝对值

整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作

int abs(int a) {
  int i = a >> 31;
  return i == 0 ? a : (~a + 1);
}

上面的操作可以进行优化,可以将 i == 0 的条件判断语句去掉。我们都知道符号位 i 只有两种情况,即 i = 0 为正,i = -1 为负。对于任何数与 0 异或都会保持不变,与 -1 即 0xffffffff 进行异或就相当于对此数进行取反,因此可以将上面三目元算符转换为((a^i)-i),即整数时 a 与 0 异或得到本身,再减去 0,负数时与 0xffffffff 异或将 a 进行取反,然后在加上 1,即减去 i(i =-1)

int abs2(int a) {
  int i = a >> 31;
  return ((a^i) - i);
}

位操作进行二进制逆序

将无符号数的二进制表示进行逆序,求取逆序后的结果,如

数34520的二进制表示:
10000110 11011000

逆序后则为:
00011011 01100001
它的十进制为7009

在字符串逆序过程中,可以从字符串的首尾开始,依次交换两端的数据。在二进制中使用位的高低位交换会更方便进行处理,这里我们分组进行多步处理。

  • 第一步:以每 2 位为一组,组内进行高低位交换
交换前: 10 00 01 10 11 01 10 00
交换后: 01 00 10 01 11 10 01 00
  • 第二步:在上面的基础上,以每 4 位为 1 组,组内高低位进行交换
交换前: 0100 1001 1110 0100
交换后: 0001 0110 1011 0001
  • 第三步:以每 8 位为一组,组内高低位进行交换
交换前: 00010110 10110001
交换后: 01100001 00011011
  • 第四步:以每16位为一组,组内高低位进行交换
交换前: 0110000100011011
交换后: 0001101101100001

对于上面的第一步,依次以 2 位作为一组,再进行组内高低位交换,这样处理起来比较繁琐,下面介绍另外一种方法进行处理。先分别取原数 10000110 11011000 的奇数位和偶数位,将空余位用 0 填充:

原数:  10000110 11011000
奇数位: 10000010 10001000
偶数位: 00000100 01010000

再将奇数位右移一位,偶数位左移一位,此时将两个数据相或即可以达到奇偶位上数据交换的效果:

原数:  10000110 11011000
奇数位右移一位: 0 10000010 1000100
偶数位左移一位:0000100 01010000 0
两数相或得到: 01001001 11100100

上面的方法用位操作可以表示为:

  • 取a的奇数位并用 0 进行填充可以表示为:a & 0xAAAA
  • 取a的偶数为并用 0 进行填充可以表示为:a & 0x5555 因此,上面的第一步可以表示为:
    a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1)
    同理,可以得到其第二、三和四步为:
    a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2)
    a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4)
    a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8)
    因此整个操作为:
unsigned short a = 34520;

a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);

位操作统计二进制中 1 的个数

统计二进制1的个数可以分别获取每个二进制位数,然后再统计其1的个数,此方法效率比较低。这里介绍另外一种高效的方法,同样以 34520 为例,我们计算其 a &= (a-1)的结果:

  • 第一次:计算前:1000 0110 1101 1000 计算后:1000 0110 1101 0000
  • 第二次:计算前:1000 0110 1101 0000 计算后:1000 0110 1100 0000
  • 第二次:计算前:1000 0110 1100 0000 计算后:1000 0110 1000 0000 我们发现,没计算一次二进制中就少了一个 1,则我们可以通过下面方法去统计:
count = 0  
while(a){  
  a = a & (a - 1);  
  count++;  
}  

作者:力扣(LeetCode)
链接:https://www.zhihu.com/question/38206659/answer/736472332
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

另外如何二进制展示位表示?

#include <bits/stdc++.h>

using namespace std;

int main(){
    int value = -10;
    for(int i = 63; i >= 0; i--)
        if((value >> i) & 1) {
            cout << '1';
        } else cout << '0';
    bitset<32> bs(value);
    cout << endl << bs[10] << endl;
    return 0;
}

————————————

二进制在格雷码的应用

Here is the example:

1.The most significant bit MSB (left most) in the Gray code is the same as the corresponding MSB in the binary number.

2.Going from left to right, add each adjacent pair of binary code bits to get the next Gray code bit. Discard carries.

In fact, this is a series Xor operations. I think we can define how to convert bin code to gray code first.

It is easy to understand how to encode gray code. So, how to decode?

This picture shows how to turn 10101111 from gray code to bin code.

It may be hard to understand it intuitively.

The first step is as same as the encode operation. The MSB does not change. Then we do Xor within the previous bit in bin and the current gray bit.

To prove it, we use a Xor a is 0, and 0 Xor a is a. We can also apply commutative law.

——————————————–

另一个关于异或的性质:

X^Y=(X&~Y)|(~X&Y)

——————————————-

二进制乘法和与门的关系

——————————————-

Views: 116

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.