• 位运算中的异或运算 .


    位运算是非常迅速的,因为它直接对内存中的二进制数据进行操作。 

    按位运算除了,按位与,按位非,按位左移,按位右移,还有按位异或。


    按位异或运算定义,

    1 ^ 1=0

    1 ^ 0=1

    0 ^ 1=1

    0 ^ 0=0

    异或,就是“看看你们到底一样不一样。不一样就为1,一样就为0。”

    按位异或运算的规律是

    定理一a ^ b = b ^ a

    定理二 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;

    定理三 a ^ b ^ a = b, a ^ a^ b = b, b ^ a^ a = b

    定理四若d = a ^ b ^ c,则a = d ^ b ^ c

    证明:

    在d = a ^ b ^ c两边同时异或^ b ^ c,得

    d ^ b ^ c =a ^ b ^ c ^ b ^ c

    d ^ b ^ c =a ^ b ^ b ^ c ^ c,由定理三得

    d ^ b ^ c =a ^ c ^ c,同样由定理三得

    d ^ b ^ c =a

    异或的几个常见用途:

    (1) 实现两个值的交换,而不必使用临时变量。

        例如交换两个整数a=10100001,b=00000110的值,可通过下列语句实现:

        a = a^b;  //a=10100111

        b = b^a;  //b=10100001

        a = a^b;  //a=00000110

    (2) 在汇编语言中经常用于将变量置零:

       xor   a,a

    (3) 使某些特定的位翻转

        例如对数10100001的第2位和第3位翻转,则可以将该数与00000110进行按位异或运算。

           10100001^00000110 = 10100111

    (4)使用定理三进行编码解码

    由B ^ A^ A = B,我们可以假设一聊天记录是B,密钥是A。现在B ^ A之后,成了密文了。为了解密,对密文再使用密钥A进行一次异或运算即可。也即是B ^ A^ A = B。


    看看今年SOGOU校招在线测试的一道编码解码题目。原题(JAVA版本)如下

    1. public class Test {  
    2.   
    3.     public static void encode(byte[] in, byte[] out, int password) {  
    4.         int len = in.length;  
    5.   
    6.         int seed = password ^ 0x3e1e25e6;  
    7.         for (int i = 0; i < len; ++i) {  
    8.             byte a = (byte) ((in[i] ^ seed) >> 3);  
    9.             byte b = (byte) (((((int) in[i]) << 18) ^ seed) >>> (18 - 5));  
    10.             a &= 0x1f;  
    11.             b &= 0xe0;  
    12.             out[i] = (byte) (a | b);  
    13.             seed = (seed * 84723701 ^ seed ^ out[i]);  
    14.         }  
    15.     }  
    16.   
    17.     public static void decode(byte[] in, byte[] out, int password) {  
    18.         int len = in.length;  
    19.         int seed = password ^ 0x3e1e25e6;  
    20.         for (int i = 0; i < len; ++i) {  
    21.             // fill the code here   
    22.         }  
    23.     }  
    24.   
    25.     public static void main(String[] args) throws Exception {  
    26.         int password = 0xfdb4d0e9;  
    27.         byte[] buf1 = { -59, -62, -12250122, -86119, -10125, -64,  
    28.                 -97, -1289585629998, -947612127121, -32,  
    29.                 -125, -1261518100104, -32, -111, -122110, -46057,  
    30.                 2136, -82, };  
    31.         byte[] buf2 = new byte[buf1.length];  
    32.         decode(buf1, buf2, password);  
    33.         System.out.println(new String(buf2, "GBK"));  
    34.     }  
    35. }  

    题目要求补充decode函数。那么根据encode函数就可以补充decode函数。解题要点是位运算中的左移,右移,按位与,按位异或,按位异或定理三。


     先来理解encode函数。

    1. public static void encode(byte[] in, byte[] out, int password) {  
    2.         int len = in.length;  
    3.   
    4.         int seed = password ^ 0x3e1e25e6;  
    5.         for (int i = 0; i < len; ++i) {  
    6.             byte a = (byte) ((in[i] ^ seed) >> 3);  
    7.             //说明①: in[i]的高5位给了a的低5位   
    8.             byte b = (byte) (((((int) in[i]) << 18) ^ seed) >> (18 - 5));  
    9.             //说明②: in[i]的低3位给了b的高3位   
    10.             a &= 0x1f;  
    11.             //0x1f=16+15=31=2^5-1=00011111;   
    12.             b &= 0xe0;  
    13.             //0xe0=11100000;   
    14.             out[i] = (byte) (a | b);  
    15.             seed = (seed * 84723701 ^ seed ^ out[i]);  
    16.         }  
    17.     }  


    然后就可以编写decode函数了

    1.     public static void decode(byte[] in, byte[] out, int password) {  
    2.         int len = in.length;// encode中的out[i]是这里decode中的in[i]   
    3.         int seed = password ^ 0x3e1e25e6;  
    4.         for (int i = 0; i < len; ++i) {  
    5.             byte a = (byte) (in[i] & 0x1f);  
    6. //参照式⑤,还原输出结果,取in[i]的低5位   
    7.             byte b = (byte) (in[i] & 0xe0);   
    8. //参照式⑤,还原输出结果,取in[i]的高3位   
    9.             a = (byte) (((a <<3) ^ seed) & 248);   
    10. //参照定理三B ^ A^ A = B,参照式①byte a = (byte) ((in[i] ^ seed) >>> 3)   
    11. //式①中的in[i]相当于B,seed相当于A,(a<<3)相当 B ^ A 故((a <<3) ^ seed)表示in[i]高5   
    12. //位的这5个数字,为了将这5个数字放入高5位的位置上,还需&11111000,即&248。   
    13. //11111000=2^7+2^6+2^5+2^4+2^3=128+64+32+16+8=248   
    14.             b = (byte) ((((((int) b) << (18 - 5)) ^ seed) >> 18) & 7);  
    15. //类似地,逆向式②,得到的结果将放入out[i]的低3位,故&00000111,即&7。   
    16. //00000111=2^0+2^1+2^2=1+2+4=7   
    17.             out[i] = (byte) (a | b);  
    18.             seed = (seed * 84723701 ^ seed ^ in[i]);  
    19.         }  
    20.     }  
    21.   
    22. //最后的输出答案微雷,答案是“真双核引擎是全球最快的浏览器内核!!!!”  


    这道题还有C++版本的,几乎和JAVA版本一模一样。

    1. #include "stdafx.h"   
    2. #include <stdio.h>     
    3. //#include <stdlib.h>     
    4. #include <assert.h>     
    5. #include <string.h>     
    6.   
    7. #define uint8_t unsigned char     
    8. #define uint32_t unsigned int     
    9. #define size_t unsigned int    
    10.   
    11.   
    12. int  encode(const  void*  raw_in,  void*  raw_out,  uint32_t  password,  size_t  len)   
    13. {   
    14. const  uint8_t*  in  =  (const  uint8_t*)raw_in;   
    15. uint8_t*  out  =  (uint8_t*)raw_out;   
    16.   
    17. uint32_t  seed  =  password  ^  0x3feb3c98u;   
    18. for  (size_t  i  =  0  ;  i  <  len;  ++i)  {   
    19. uint8_t  a  =  (  in[i]  ^  seed  )  >>  4;   
    20. uint8_t  b  =  (  (  ((uint32_t)in[i])  <<  17  )  ^  seed  )  >>  (17-4);   
    21. a  &=  15;  //00001111   
    22. b  &=  240; //11110000=2^7+2^6+2^5+2^4=128+64+32+16=240   
    23. a  =  15  &  (  a  ^  (b  <<  3));   
    24. out[i]  =  a  |  b;   
    25. seed  =  (seed  *  48475829  ^  seed  ^  in[i]);   
    26. }   
    27. return 0;  
    28. }   
    29.   
    30.   
    31. int  decode(const  void*  raw_in,  void*  raw_out,  uint32_t  password,  size_t  len)   
    32. {   
    33. const  uint8_t*  in  =  (const  uint8_t*)raw_in;   
    34. uint8_t*  out  =  (uint8_t*)raw_out;   
    35.   
    36. uint32_t  seed  =  password  ^  0x3feb3c98u;   
    37. for  (size_t  i  =  0  ;  i  <  len;  ++i)  {   
    38. //  请在此处补全代码 - 开始   
    39.     uint8_t a=in[i]&15;  
    40.     uint8_t b=in[i]&240;  
    41.     a=((a<<4)^seed)&240;  
    42.     b=((((uint32_t)b<<13)^seed)>>17)&15;  
    43.     out[i]  =  a  |  b;   
    44.     seed  =  (seed  *  48475829  ^  seed  ^  out[i]);   
    45. //  请在此处补全代码 - 结束   
    46. }   
    47. return 0;  
    48. }   
    49. int  main()   
    50. {   
    51. const  uint8_t  buf1[]  =  {0x1e,  0x7b,  0x8f,  0x63,  0x6f,  0x69,  0x26,  0x23,  0x64,  0xe1,  0x09,  0x21,  0x13,  0x2b,  0x37,  0xdf,  0xa4,  0x7f,  0x45,  0xe3,  0x6b,  0xda,  0x6a,  0x00,  0x93,  0x4b,  0xd1,  0x81,  0x92,  0x20,  0x69,  0x74,  0xf9,  0xf1,  0x1f,  0xb9,  0x1f,  0x6d,  0x20,  0x7b,  0xe8,  0x0c,  0x89,  0x29,  0x77,  0x65,  0xaa,  0x0f,  0xdb,  0x45,  0x4e,  0x58,  0x39,  0x98,  0x7f,  0xa7,  0x04,  0x71,  0xb4,  0xe1,  0xe4,  };   
    52. uint8_t  buf2[100]  = "";   
    53. const  uint32_t  password  =  0xe53e6eb6u;   
    54. const  size_t  len  =  sizeof(buf1);   
    55. decode(buf1,  buf2,  password,  len);   
    56. printf("%s\n",  buf2);   
    57. return 0;  
    58. }   
    59.   
    60. //输出答案:搜狗搜索是全球首个中文网页收录量达到100亿的搜索引擎!!!!!  
  • 相关阅读:
    DevOps工具链
    内网穿透工具
    SVN无法检出项目
    IDEA安装插件
    实习过程中学到关于各版本操作系统的知识(2)
    实习过程中学到关于各版本操作系统的知识(1)
    lib1funcs.S(用于解决裸板实现 printf 中的问题: undefined reference to `__aeabi_uidivmod' 和 undefined reference to `__aeabi_uidiv')
    交叉编译工具参数笔记
    vim 源码安装
    Git clone 下载慢解决方案
  • 原文地址:https://www.cnblogs.com/lidabo/p/2963959.html
Copyright © 2020-2023  润新知