热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

位运算中的异或运算

位运算是非常迅速的,因为它直接对内存中的二进制数据进行操作。按位运算除了,按位与,按位非,按位左移,按位右移,还有按位异或。按位异或运算定义,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亿的搜索引擎!!!!!


 



推荐阅读
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • 本文介绍如何在Java中实现一个罗马数字计算器,重点在于如何通过循环和字符验证确保用户输入合法。我们将探讨创建一个方法来检查字符串中的非法字符,并使用循环不断提示用户输入,直到输入符合要求。 ... [详细]
  • 2017-2018年度《网络编程与安全》第五次实验报告
    本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ... [详细]
  • 本文详细介绍了Java库XChart中的XYSeries类下的setLineColor()方法,并提供了多个实际应用场景的代码示例。 ... [详细]
  • java文本编辑器,java文本编辑器设计思路
    java文本编辑器,java文本编辑器设计思路 ... [详细]
  • 本文介绍了如何使用JFreeChart库创建一个美观且功能丰富的环形图。通过设置主题、字体和颜色等属性,可以生成符合特定需求的图表。 ... [详细]
  • 深入理解Java多线程并发处理:基础与实践
    本文探讨了Java中的多线程并发处理机制,从基本概念到实际应用,帮助读者全面理解并掌握多线程编程技巧。通过实例解析和理论阐述,确保初学者也能轻松入门。 ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • 软件工程课堂测试2
    要做一个简单的保存网页界面,首先用jsp写出保存界面,本次界面比较简单,首先是三个提示语,后面是三个输入框,然 ... [详细]
  • Java 中重写与重载的区别
    本文详细解析了 Java 编程语言中重写(Override)和重载(Overload)的概念及其主要区别,帮助开发者更好地理解和应用这两种多态性机制。 ... [详细]
  • 本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ... [详细]
  • 本文详细介绍了Java编程中的基本运算符,包括算术、赋值、关系和逻辑运算符,并深入探讨了三元运算符的使用。此外,还讲解了如何使用Scanner类进行用户输入处理以及if和switch语句等流程控制结构。 ... [详细]
  • 利用jstack进行死锁检测与线程堆栈分析
    本文介绍了如何使用jstack工具进行Java应用中的死锁检测及高CPU使用率线程的堆栈分析,帮助开发者快速定位并解决性能瓶颈。 ... [详细]
  • 在Java应用程序开发过程中,FTP协议被广泛用于文件的上传和下载操作。本文通过Jakarta Commons Net库中的FTPClient类,详细介绍如何实现文件的上传和下载功能。 ... [详细]
author-avatar
王强丫ES
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有