zoukankan      html  css  js  c++  java
  • java 位运算 和实际应用

    1. public class Test {  
    2.     public static void main(String[] args) {  
    3.         // 1、左移( << )  
    4.         // 0000 0000 0000 0000 0000 0000 0000 0101 然后左移2位后,低位补0://  
    5.         // 0000 0000 0000 0000 0000 0000 0001 0100 换算成10进制为20  
    6.         System.out.println(5 << 2);// 运行结果是20  
    7.   
    8.         // 2、右移( >> ) 高位补符号位  
    9.         // 0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位补0:  
    10.         // 0000 0000 0000 0000 0000 0000 0000 0001  
    11.         System.out.println(5 >> 2);// 运行结果是1  
    12.   
    13.         // 3、无符号右移( >>> ) 高位补0  
    14.         // 例如 -5换算成二进制后为:0101 取反加1为1011  
    15.         // 1111 1111 1111 1111 1111 1111 1111 1011  
    16.         // 我们分别对5进行右移3位、 -5进行右移3位和无符号右移3位:  
    17.         System.out.println(5 >> 3);// 结果是0  
    18.         System.out.println(-5 >> 3);// 结果是-1  
    19.         System.out.println(-5 >>> 3);// 结果是536870911  
    20.   
    21.         // 4、位与( & )  
    22.         // 位与:第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0  
    23.         System.out.println(5 & 3);// 结果为1  
    24.         System.out.println(4 & 1);// 结果为0  
    25.   
    26.         // 5、位或( | )  
    27.         // 第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0  
    28.         System.out.println(5 | 3);// 结果为7  
    29.   
    30.         // 6、位异或( ^ )  
    31.         // 第一个操作数的的第n位于第二个操作数的第n位 相反,那么结果的第n为也为1,否则为0  
    32.          System.out.println(5 ^ 3);//结果为6   
    33.   
    34.         // 7、位非( ~ )  
    35.         // 操作数的第n位为1,那么结果的第n位为0,反之。  
    36.         System.out.println(~5);// 结果为-6   
    37.     }   
    38. }  


    难点解析:
    1 :正数 和负数 二进制如何转化?
    5 的二进制数 转化为-5的二进制数 ,换算方法: 取反+1.
    同样  -5 的二进制数 转化为5的二进制数 ,换算方法: 取反+1.


    2 符号解析:
    >>>是无符号右移,在高位补零
    >>是带符号的右移,如果是正数则在高位补零,负数则补1

    int a = -1;
    System.out.println(a>>1);
    System.out.println(a>>>1);

    结果:
    -1
    2147483647
    1111 1111 1111 1111 1111 1111 1111 1111       -1
    1111 1111 1111 1111 1111 1111 1111 1111       -1
    0111 1111 1111 1111 1111 1111 1111 1111       2147483647

    3  符号的实际含义:

     

    Java整型数据类型有:byte、char、short、int、long。要把它们转换成二进制的原码形式,必须明白他们各占几个字节。我们都知道,一个字节占8位。
          数据类型                           所占位数
          byte                                       8 
          boolean                                8
          short                                    16
          int                                         32 
          long                                      64 
          float                                      32 
          double                                  64 
          char                                     16

    还需要明白一点的是:计算机表示数字正负不是用+ -加减号来表示,而是用最高位数字来表示,0表示正,1表示负

    由于数据类型所占字节是有限的,而位移的大小却可以任意大小,所以可能存在位移后超过了该数据类型的表示范围,于是有了这样的规定:
    如果为int数据类型,且位移位数大于32位,则首先把位移位数对32取模,不然位移超过总位数没意义的。所以4>>32与4>>0是等价的。

    如果为long类型,且位移位数大于64位,则首先把位移位数对64取模,若没超过64位则不用对位数取模。

    如果为byte、char、short,则会首先将他们扩充到32位,然后的规则就按照int类型来处理。

    <<表示左移, 左移一位表示原来的值乘2.

    >>表示右移, 右移一位表示原来的值除2.

    位运算符包括: 与(&)、非(~)、或(|)、异或(^)


      &:当两边操作数的位同时为1时,结果为1,否则为0。如1100&1010=1000   


          | :当两边操作数的位有一边为1时,结果为1,否则为0。如1100|1010=1110   


          ~:0变1,1变0   


          ^:两边的位不同时,结果为1,否则为0.如1100^1010=0110


    位运算与位移动运行符的一个场景:


        HashMap的功能是通过“键(key)”能够快速的找到“值”。下面我们分析下HashMap存数据的基本流程: 
        1、当调用put(key,value)时,首先获取key的hashcode,int hash = key.hashCode(); 
        2、再把hash通过一下运算得到一个int h. 
    hash ^= (hash >>> 20) ^ (hash >>> 12); 
    int h = hash ^ (hash >>> 7) ^ (hash >>> 4); 
    为什么要经过这样的运算呢?这就是HashMap的高明之处。先看个例子,一个十进制数32768(二进制1000 0000 0000 0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000 1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111),现在应该很明显了,它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分布。


      3、得到h之后,把h与HashMap的承载量(HashMap的默认承载量length是16,可以自动变长。在构造HashMap的时候也可以指定一个长 度。这个承载量就是上图所描述的数组的长度。)进行逻辑与运算,即 h & (length-1),这样得到的结果就是一个比length小的正数,我们把这个值叫做index。其实这个index就是索引将要插入的值在数组中的 位置。第2步那个算法的意义就是希望能够得出均匀的index,这是HashTable的改进,HashTable中的算法只是把key的 hashcode与length相除取余,即hash % length,这样有可能会造成index分布不均匀。还有一点需要说明,HashMap的键可以为null,它的值是放在数组的第一个位置。


    4、我们用table[index]表示已经找到的元素需要存储的位置。先判断该位置上有没有元素(这个元素是HashMap内部定义的一个类Entity, 基本结构它包含三个类,key,value和指向下一个Entity的next),没有的话就创建一个Entity<K,V>对象,在 table[index]位置上插入,这样插入结束;如果有的话,通过链表的遍历方式去逐个遍历,看看有没有已经存在的key,有的话用新的value替 换老的value;如果没有,则在table[index]插入该Entity,把原来在table[index]位置上的Entity赋值给新的 Entity的next,这样插入结束。


    下面讲解一下原码->反码->补码之间的相互关系


    [-3]反=[10000011]反=11111100 
                 原码            反码
    负数的补码是将其原码除符号位之外的各位求反之后在末位再加1。 
    [-3]补=[10000011]补=11111101 
                 原码          补码


    也就是说原码转换成补码是先原码  反码 最后+1成补码。位运算都是补码运算的,所以位运算后要再取反+1才得到真正的原码。


    应用举例 
    (1) 判断int型变量a是奇数还是偶数            
    a&1  = 0 偶数 
          a&1 =  1 奇数 
    (2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1 
    (3) 将int型变量a的第k位清0,即a=a&~(1 < <k) 
    (4) 将int型变量a的第k位置1, 即a=a ¦(1 < <k) 
    (5) int型变量循环左移k次,即a=a < <k ¦a>>16-k  (设sizeof(int)=16) 
    (6) int型变量a循环右移k次,即a=a>>k ¦a < <16-k  (设sizeof(int)=16) 
    (7)整数的平均值 
    对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法: 
    int average(int x, int y)  //返回X,Y 的平均值 
    {    
        return (x&y)+((x^y)>>1); 

    (8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂 
    boolean power2(int x) 

        return ((x&(x-1))==0)&&(x!=0); 

    (9)不用temp交换两个整数 
    void swap(int x , int y) 

        x ^= y; 
        y ^= x; 
        x ^= y; 

    (10)计算绝对值 
    int abs( int x ) 

    int y ; 
    y = x >> 31 ; 
    return (x^y)-y ;        //or: (x+y)^y 

    (11)取模运算转化成位运算 (在不产生溢出的情况下) 
            a % (2^n) 等价于 a & (2^n - 1) 
    (12)乘法运算转化成位运算 (在不产生溢出的情况下) 
            a * (2^n) 等价于 a < < n 
    (13)除法运算转化成位运算 (在不产生溢出的情况下) 
            a / (2^n) 等价于 a>> n 
            例: 12/8 == 12>>3 
    (14) a % 2 等价于 a & 1        
    (15) if (x == a) x= b; 
                else x= a; 
            等价于 x= a ^ b ^ x; 
    (16) x 的 相反数 表示为 (~x+1)

    针对以上代码分析如下:

    ~1、 y = x > > 31 ;//右移31位,只保留符号位,如果是负数,则是-1,其二进制为1111 1111 1111 1111,全为1,如果是正数,则全0
    ~2、x^y//X与Y的异或运算,按位进行异或,当y=0时,实际上二者异或后运算的值保持不变,当Y=-1时,则实际上是将原值每位求反(1变成0,0变成1)
    ~3、-y//当y为0时保持不变,为-1时,则表示加1,因此
    (x^y)-y当Y=0时,表示保持x不变,当y=-1时,则表示将x各位求反后加1,实际上就是对该数求负,由于原来就是负数,因此就是变成正数。
    综上所述,该过程就是求绝对值。
    ~6.  取模运算,采用位运算实现:
         a % (2^n) 等价于 a & (2^n - 1) 
    ~7.  乘法运算   采用位运算实现
         a * (2^n) 等价于 a << n
    ~8.   除法运算转化成位运算
          a / (2^n) 等价于 a>> n 
    ~9.   求相反数
          (~x+1) 
    ~10  a % 2 等价于 a & 1 

     

    6.  取模运算,采用位运算实现:
         a % (2^n) 等价于 a & (2^n - 1) 
    7.  乘法运算   采用位运算实现
         a * (2^n) 等价于 a << n
    8.   除法运算转化成位运算
          a / (2^n) 等价于 a>> n 
    9.   求相反数(符号位也相反了)
          (~x+1) 
    10  a % 2 等价于 a & 1 


    实例
        功能               ¦          示例            ¦    位运算 
    ----------------------+-----------------------------+-------------------- 
    去掉最后一位             ¦ (101101->10110)               ¦ x >> 1 
    在最后加一个0           ¦ (101101->1011010)            ¦ x < < 1 
    在最后加一个1           ¦ (101101->1011011)             ¦ x < < 1+1 
    把最后一位变成1         ¦ (101100->101101)              ¦ x ¦ 1 
    把最后一位变成0         ¦ (101101->101100)              ¦ x ¦ 1-1 
    最后一位取反             ¦ (101101->101100)             ¦ x ^ 1 
    把右数第k位变成1        ¦ (101001->101101,k=3)         ¦ x ¦ (1 < < (k-1)) 
    把右数第k位变成0        ¦ (101101->101001,k=3)         ¦ x & ~ (1 < < (k-1)) 
    右数第k位取反           ¦ (101001->101101,k=3)         ¦ x ^ (1 < < (k-1)) 
    取末三位                   ¦ (1101101->101)                 ¦ x & 7 
    取末k位                   ¦ (1101101->1101,k=5)          ¦ x & ((1 < < k)-1)
    取右数第k位             ¦ (1101101->1,k=4)              ¦ x >> (k-1) & 1
    把末k位变成1             ¦ (101001->101111,k=4)         ¦ x ¦ (1 < < k-1) 
    末k位取反                ¦ (101001->100110,k=4)         ¦ x ^ (1 < < k-1) 
    把右边连续的1变成0     ¦ (100101111->100100000)      ¦ x & (x+1) 
    把右起第一个0变成1     ¦ (100101111->100111111)      ¦ x ¦ (x+1) 
    把右边连续的0变成1     ¦ (11011000->11011111)        ¦ x ¦ (x-1) 
    取右边连续的1         ¦ (100101111->1111)            ¦ (x ^ (x+1)) >> 1 
    去掉右起第一个1的左边 ¦ (100101000->1000)            ¦ x & (x ^ (x-1)) 
    判断奇数             ¦ (x&1)==1 
    判断偶数        ¦ (x&1)==0       

    例如求从x位(高)到y位(低)间共有多少个1
    public static int FindChessNum(int x, int y, ushort k) 
            { 
                int re = 0; 
                for (int i = y; i <= x; i++) 
                { 
                    re += ((k >> (i - 1)) & 1); 
                } 
                return re; 
            }

    来源:http://blog.csdn.net/xiaoliuliu2050/article/details/52994805

  • 相关阅读:
    POJ 1015 Jury Compromise【DP】
    POJ 1661 Help Jimmy【DP】
    HDU 1074 Doing Homework【状态压缩DP】
    HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
    占坑补题。。最近占的坑有点多。。。
    Codeforces 659F Polycarp and Hay【BFS】
    Codeforces 659E New Reform【DFS】
    Codeforces 659D Bicycle Race【计算几何】
    廖大python实战项目第四天
    廖大python实战项目第三天
  • 原文地址:https://www.cnblogs.com/lidj/p/8072848.html
Copyright © 2011-2022 走看看