位运算是非常迅速的,因为它直接对内存中的二进制数据进行操作。按位运算除了,按位与,按位非,按位左移,按位右移,还有按位异或。按位异或运算定义,1^101^010^110^00
位运算是非常迅速的,因为它直接对内存中的二进制数据进行操作。
按位运算除了,按位与,按位非,按位左移,按位右移,还有按位异或。
按位异或运算定义,
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版本)如下
public class Test {
public static void encode(byte[] in, byte[] out, int password) {
int len = in.length;
int seed = password ^ 0x3e1e25e6;
for (int i = 0; i byte a = (byte) ((in[i] ^ seed) >> 3);
byte b = (byte) (((((int) in[i]) <<18) ^ seed) >>> (18 - 5));
a &= 0x1f;
b &= 0xe0;
out[i] = (byte) (a | b);
seed = (seed * 84723701 ^ seed ^ out[i]);
}
}
public static void decode(byte[] in, byte[] out, int password) {
int len = in.length;
int seed = password ^ 0x3e1e25e6;
for (int i = 0; i // fill the code here
}
}
public static void main(String[] args) throws Exception {
int password = 0xfdb4d0e9;
byte[] buf1 = { -5, 9, -62, -122, 50, 122, -86, 119, -101, 25, -64,
-97, -128, 95, 85, 62, 99, 98, -94, 76, 12, 127, 121, -32,
-125, -126, 15, 18, 100, 104, -32, -111, -122, 110, -4, 60, 57,
21, 36, -82, };
byte[] buf2 = new byte[buf1.length];
decode(buf1, buf2, password);
System.out.println(new String(buf2, "GBK"));
}
}
题目要求补充decode函数。那么根据encode函数就可以补充decode函数。解题要点是位运算中的左移,右移,按位与,按位异或,按位异或定理三。
先来理解encode函数。
public static void encode(byte[] in, byte[] out, int password) {
int len = in.length;
int seed = password ^ 0x3e1e25e6;
for (int i = 0; i byte a = (byte) ((in[i] ^ seed) >> 3);
//说明①: in[i]的高5位给了a的低5位
byte b = (byte) (((((int) in[i]) <<18) ^ seed) >> (18 - 5));
//说明②: in[i]的低3位给了b的高3位
a &= 0x1f;
//0x1f=16+15=31=2^5-1=00011111;
b &= 0xe0;
//0xe0=11100000;
out[i] = (byte) (a | b);
seed = (seed * 84723701 ^ seed ^ out[i]);
}
}
然后就可以编写decode函数了
public static void decode(byte[] in, byte[] out, int password) {
int len = in.length;// encode中的out[i]是这里decode中的in[i]
int seed = password ^ 0x3e1e25e6;
for (int i = 0; i byte a = (byte) (in[i] & 0x1f);
//参照式⑤,还原输出结果,取in[i]的低5位
byte b = (byte) (in[i] & 0xe0);
//参照式⑤,还原输出结果,取in[i]的高3位
a = (byte) (((a <<3) ^ seed) & 248);
//参照定理三B ^ A^ A = B,参照式①byte a = (byte) ((in[i] ^ seed) >>> 3)
//式①中的in[i]相当于B,seed相当于A,(a<<3)相当 B ^ A 故((a <<3) ^ seed)表示in[i]高5
//位的这5个数字,为了将这5个数字放入高5位的位置上,还需&11111000,即&248。
//11111000=2^7+2^6+2^5+2^4+2^3=128+64+32+16+8=248
b = (byte) ((((((int) b) <<(18 - 5)) ^ seed) >> 18) & 7);
//类似地,逆向式②,得到的结果将放入out[i]的低3位,故&00000111,即&7。
//00000111=2^0+2^1+2^2=1+2+4=7
out[i] = (byte) (a | b);
seed = (seed * 84723701 ^ seed ^ in[i]);
}
}
//最后的输出答案微雷,答案是“真双核引擎是全球最快的浏览器内核!!!!”
这道题还有C++版本的,几乎和JAVA版本一模一样。
#include "stdafx.h"
#include
//#include
#include
#include
#define uint8_t unsigned char
#define uint32_t unsigned int
#define size_t unsigned int
int encode(const void* raw_in, void* raw_out, uint32_t password, size_t len)
{
const uint8_t* in = (const uint8_t*)raw_in;
uint8_t* out = (uint8_t*)raw_out;
uint32_t seed = password ^ 0x3feb3c98u;
for (size_t i = 0 ; i uint8_t a = ( in[i] ^ seed ) >> 4;
uint8_t b = ( ( ((uint32_t)in[i]) <<17 ) ^ seed ) >> (17-4);
a &= 15; //00001111
b &= 240; //11110000=2^7+2^6+2^5+2^4=128+64+32+16=240
a = 15 & ( a ^ (b <<3));
out[i] = a | b;
seed = (seed * 48475829 ^ seed ^ in[i]);
}
return 0;
}
int decode(const void* raw_in, void* raw_out, uint32_t password, size_t len)
{
const uint8_t* in = (const uint8_t*)raw_in;
uint8_t* out = (uint8_t*)raw_out;
uint32_t seed = password ^ 0x3feb3c98u;
for (size_t i = 0 ; i // 请在此处补全代码 - 开始
uint8_t a=in[i]&15;
uint8_t b=in[i]&240;
a=((a<<4)^seed)&240;
b=((((uint32_t)b<<13)^seed)>>17)&15;
out[i] = a | b;
seed = (seed * 48475829 ^ seed ^ out[i]);
// 请在此处补全代码 - 结束
}
return 0;
}
int main()
{
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, };
uint8_t buf2[100] = "";
const uint32_t password = 0xe53e6eb6u;
const size_t len = sizeof(buf1);
decode(buf1, buf2, password, len);
printf("%s\n", buf2);
return 0;
}
//输出答案:搜狗搜索是全球首个中文网页收录量达到100亿的搜索引擎!!!!!