位運算是非常迅速的,因為它直接對記憶體中的二進制資料進行操作。
按位運算除了,按位與,按位非,按位左移,按位右移,還有按位異或。
按位異或運算定義,
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 < len; ++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) {
// 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) {
//說明①: in[i]的高5位給了a的低5位
byte b = (byte) (((((int) in[i]) << 18) ^ seed) >> (18 - 5));
//說明②: in[i]的低3位給了b的高3位
//0x1f=16+15=31=2^5-1=00011111;
//0xe0=11100000;
然後就可以編寫decode函數了
int len = in.length;// encode中的out[i]是這裡decode中的in[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
seed = (seed * 84723701 ^ seed ^ in[i]);
//最後的輸出答案微雷,答案是“真雙核引擎是全球最快的浏覽器核心!!!!”
這道題還有C++版本的,幾乎和JAVA版本一模一樣。
#include "stdafx.h"
#include <stdio.h>
//#include <stdlib.h>
#include <assert.h>
#include <string.h>
#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 < len; ++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)
// 請在此處補全代碼 - 開始
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]);
// 請在此處補全代碼 - 結束
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);
//輸出答案:搜狗搜尋是全球首個中文網頁收錄量達到100億的搜尋引擎!!!!!