8

位运算的奇淫技巧,非常有趣~

 3 years ago
source link: https://segmentfault.com/a/1190000039109400
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

位运算的奇淫技巧,非常有趣~

发布于 1 月 27 日

基本的位操作符有与、或、异或、取反、左移、右移这6种:

位运算示例操作

位运算示例操作

位运算功能示例x >> 1去掉最后一位101101->10110x << 1在最后加一个0101101->1011010x << 1在最后加一个1101101->1011011x\1把最后一位变成1101100->101101x & -2把最后一位变成0101101->101100x ^ 1最后一位取反101101->101100x \(1 << (k-1))把右数第k位变成1101001->101101,k=3x & ~ (1 << (k-1))把右数第k位变成0101101->101001,k=3x ^(1 <<(k-1))右数第k位取反101001->101101,k=3x & 7取末三位1101101->101x & (1 << k-1)取末k位1101101->1101,k=5x >> (k-1) & 1取右数第k位1101101->1,k=4x \((1 << k)-1)把末k位变成1101001->101111,k=4x ^ (1 << k-1)末k位取反101001->100110,k=4x & (x+1)把右边连续的1变成0100101111->100100000x \(x+1)把右起第一个0变成1100101111->100111111x \(x-1)把右边连续的0变成111011000->11011111(x ^ (x+1)) >> 1取右边连续的1100101111->1111x & -x去掉右起第一个1的左边100101000->1000x&0x7F取末7位100101000->101000x& ~0x7F是否小于127001111111 & ~0x7F->0x & 1判断奇偶00000111&1->1

使用位运算的两点注意事项:

  1. 位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。
  2. 位操作符的运算优先级比较低,因为尽量使用括号来确保运算顺序

1. 判断一个数值是不是2的整数次方

解题思路:

2的整数次方对应的二进制的最高位上只有一个1,如:8,二进制为 1000; 4,二进制为 0100,

那么将该数字减去1再与该数字进行与运算,减去1 后得到二进制:7,二进制为 0111;3,二进制为 0011,可以看出 8&7 为0,

4&3 为0

所以,如果 n 是2的整数次方,那么 n & ( n - 1 )结果一定为0:

n 的数值要大于 0 



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          int n = 8;
    
5.          if ((n & (n-1)) == 0){
    
6.              System.out.println("整数的二次方 true");
    
7.          }else{
    
8.              System.out.println("不是整数的二次方");
    
9.          }
    
10.      }
    
11.  }
    

2. 使用位运算交换两个数字【不使用中间变量】

使用异或



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          int n = 8, m = 10;
    
5.          n ^= m;
    
6.          m ^= n;
    
7.          n ^= m;
    
8.          System.out.println(n + ", " + m);
    
9.      }
    
10.  }
    

如: a = 13, b = 6:
a的二进制为 13 = 8 + 4 + 1 = 1101(二进制)
b的二进制为 6 = 4 + 2 = 110(二进制)

  1. a ^= b a = 1101 ^ 110 = 1011;
  2. b ^= a b = 110 ^ 1011 = 1101; 即b == 13
  3. a ^= b a = 1011 ^ 1101 = 110; 即a == 6

其他方法,使用加减法



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          int n = 8, m = 10;
    
5.          n = n + m;
    
6.          m = n - m;
    
7.          n = n - m;
    
8.          System.out.println(n + ", " + m);
    
9.      }
    
10.  }
    

3. 计算在一个 32 位的整数的二进制表示中有多少个 1

循环使用x & (x-1)消去最后一位1,计算总共消去了多少次即可。

13: 1101

12: 1100

相与:1100, 消去最后一位



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          // 计算在一个 32 位的整数的二进制表示中有多少个 1
    
5.          int m = 13, num = 0;
    
6.          while (true){
    
7.              if (m == 0) break;
    
8.              m &= (m-1);
    
9.              num ++;
    
10.          }
    
11.          System.out.println(num);
    
12.      }
    

14.  }
    

4. 正数变成负数,或者负数变成正数

变换符号只需要取反后加1即可



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          // 计算在一个 32 位的整数的二进制表示中有多少个 1
    
5.          int m = -13;
    
6.          int changeM = ~m + 1;
    
7.          System.out.println(changeM);
    
8.      }
    

10.  }
    

5. 判断一个数值的奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数,所以只需要与 1 相与。

因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          int m = -14;
    

6.          if ((m & 1) == 1){
    
7.              System.out.println("ji");
    
8.          }else{
    
9.              System.out.println("ou");
    
10.          }
    
11.      }
    

13.  }
    

6. 乘以2 的m次方操作

乘以2的操作,即2的1次方,左移 1 位

System.out.println(10<<1);

 推导扩展:

乘以2的m次方

System.out.println(10<<2); // 乘以 2的2次方,相当于乘以 4 

7.除以2运算(负奇数的运算不可用)

System.out.println(10>>1);

8. 转换成绝对值



1.  public class Main {
    

3.      public static void main(String[] args) {
    
4.          int n = 12;
    

6.          System.out.println(0 >> 31); // 0
    
7.          System.out.println(10 >> 31);  // 0
    
8.          System.out.println(-10 >> 31);  // -1
    

10.          System.out.println((n ^ (n >> 31)) - (n >> 31));  // 12 
    

12.      }
    

14.  }
    

  1. 首先:n>>31 取得n的符号

若n为正数,n>>31等于0;若n为负数,n>>31等于-1 若n为正数 n^0-0数不变;

  1. 若 n 为负数 n^-1 需要计算 n 和 -1 的补码,异或后再取补码, 结果n变号并且绝对值减1,再减去-1就是绝对值

9.判断两数符号是否相同

true 表示 x和y有相同的符号, false表示x,y有相反的符号。

System.out.println((a ^ b) > 0);

10. 求两个整数(int)的平均数

System.out.println((a+b) >> 1);

11. 求两个整数的最大值



1.  int max(int a,int b){
    
2.      return b & ((a-b) >> 31) | a & (~(a-b) >> 31);
    
3.      /*如果a>=b,(a-b)>>31为0,否则为-1*/
    
4.  }
    

12.求两个整数的最小值



1.  int min(int a,int b){
    
2.      return a & ((a-b) >> 31) | b & (~(a-b) >> 31);
    
3.      /*如果a>=b,(a-b)>>31为0,否则为-1*/
    
4.  }
    

13. 两个整数的加法运算

使用 ^ 和 & 将两个整数相加

  1. 两个数异或:相当于两个数相加,而不考虑进位;
  2. 两个数相与,并左移一位:相当于求得进位;

在这里插入图片描述

在这里插入图片描述

13+11 = ?;

13 的二进制      1 1 0 1                     -----a        13

11 的二进制      1 0 1 1                     -----b        11  

 (a&b) <<1  ->   1 0 0 1 0                         -----d         18

          a^b  ->     0 1 1 0                   -----e          6

 (d&e) <<1  ->   0 0 1 0 0                       ------f         4

          d^e  ->  1 0 1 0 0                  -----g        20

 (f&g) <<1  ->   0 1 0 0 0                       ------h        8

          f^g  ->  1 0 0 0 0                   ------i           16

 (h&i) <<1  ->   0 0 0 0 0                      ------h        0       ---- -------- 没有进位了, 则退出循环

          h^i  ->  1 1 0 0 0                  ------i           24



1.  private static int getSum(int a, int b) {
    
2.      if (a == 0) return b;
    
3.      if (b == 0) return a;
    
4.      while (b != 0) {
    
5.          int carry = a & b; // 得到有进位的位置
    
6.          a = a ^ b; // 直接相加,但是没有进位
    
7.          b = carry << 1; // 得到进位
    
8.      }
    
9.      return a;
    
10.  }
    

项目推荐:

2000多G的计算机各行业电子资源分享(持续更新)

2020年微信小程序全栈项目之喵喵交友【附课件和源码】

Spring Boot开发小而美的个人博客【附课件和源码】

Java微服务实战296集大型视频-谷粒商城【附代码和课件】

Java开发微服务畅购商城实战【全357集大项目】-附代码和课件

最全最详细数据结构与算法视频-【附课件和源码】​​​​​​​

在这里插入图片描述


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK