`
wenjinglian
  • 浏览: 805912 次
  • 性别: Icon_minigender_1
  • 来自: 株洲->深圳
社区版块
存档分类
最新评论

位运算、异或的实际应用

    博客分类:
  • JAVA
阅读更多

一. 位操作基础,用一张表描述位操作符的应用规则并详细解释。

      二. 常用位操作小技巧,有判断奇偶、交换两数、变换符号、求绝对值。

      三. 位操作与空间压缩,针对筛素数进行空间压缩。

      四. 位操作的趣味应用,列举了位操作在高低位交换、二进制逆序、二进制中1的个数以及缺失的数字这4种趣味应用。

希望读者能认真学习和亲自上机输入代码进行实验,相信通过本文及适当的练习可以使你对位操作有更加深入的了解,在笔试面试中遇到位操作相关试题能更加从容。

一. 位操作基础

基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:

 

符号

 描述

 运算规则                        by MoreWindows

&      

 与

两个位都为1时,结果才为1

|  

 或    

两个位都为0时,结果才为0

^    

异或

两个位相同为0,相异为1

~   

取反

0变1,1变0

<< 

左移

各二进位全部左移若干位,高位丢弃,低位补0

>> 

右移

各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

 

 

 

1.判断奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

下面程序将输出0到100之间的所有奇数。

[cpp] view plaincopy
 
  1. for (i = 0; i < 100; ++i)  
  2.     if (i & 1)  
  3.         printf("%d ", i);  
  4. putchar('\n');  

2.交换两数

一般的写法是:

[cpp] view plaincopy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         int c = a;  
  6.         a = b;  
  7.         b = c;  
  8.     }  
  9. }  

可以用位操作来实现交换两数而不用第三方变量:

[cpp] view plaincopy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         a ^= b;  
  6.         b ^= a;  
  7.         a ^= b;  
  8.     }  
  9. }  

可以这样理解:

第一步  a^=b 即a=(a^b);

第二步  b^=a 即b=b^(a^b),由于^运算满足交换律,b^(a^b)=b^b^a。由于一个数和自己异或的结果为0并且任何数与0异或都会不变的,所以此时b被赋上了a的值。

第三步 a^=b 就是a=a^b,由于前面二步可知a=(a^b),b=a,所以a=a^b即a=(a^b)^a。故a会被赋上b的值。
再来个实例说明下以加深印象。int a = 13, b = 6;

a的二进制为 13=8+4+1=1101(二进制)

b的二进制为 6=4+2=110(二进制)

第一步 a^=b  a = 1101 ^ 110 = 1011;

第二步 b^=a  b = 110 ^ 1011 = 1101;即b=13

第三步 a^=b  a = 1011 ^ 1101 = 110;即a=6

3.变换符号

变换符号就是正数变成负数,负数变成正数。

如对于-11和11,可以通过下面的变换方法将-11变成11

      1111 0101(二进制) –取反-> 0000 1010(二进制) –加1-> 0000 1011(二进制)

同样可以这样的将11变成-11

      0000 1011(二进制) –取反-> 0000 0100(二进制) –加1-> 1111 0101(二进制)

因此变换符号只需要取反后加1即可。完整代码如下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )    
  2. #include <stdio.h>  
  3. int SignReversal(int a)  
  4. {  
  5.     return ~a + 1;  
  6. }  
  7. int main()  
  8. {  
  9.     printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");  
  10.     int a = 7, b = -12345;  
  11.     printf("%d  %d\n", SignReversal(a), SignReversal(b));  
  12.     return 0;  
  13. }  

4.求绝对值

位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对-6可以这样:

      1111 1010(二进制) –取反->0000 0101(二进制) -加1-> 0000 0110(二进制)

来得到6。

因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。完整代码如下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return i == 0 ? a : (~a + 1);  
  6. }  

现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return ((a ^ i) - i);  
  6. }  

注意这种方法没用任何判断表达式,而且有些笔面试题就要求这样做,因此建议读者记住该方法(^_^讲解过后应该是比较好记了)。

 

 

 

实际应用:

位运算存储数据

  利用位运算存储数据,主要是为了减少程序占用的内存。以int数据为例子,如果按照十进制的方式存储数据,一个32位的int变量只能存储一个数值,而如果使用二进制方式存储数据(缺点是只能存储0或1两个数据)则可以存储32个数据,将极大的节约内存。例如,在一个int变量的从右侧开始倒数第2位存储数据,则存储和读取数据的代码如下所示:

int bData = 0;
//存储数值1 
bData = bData |(1 << (2 - 1));
//存储数值0 
bData = bData & (~(1 << (2 - 1));
//读取数据 
int n = bData & (1 << (2 - 1));

  点评:在该代码中,将需要存储的数据(0或1)存储在变量bData的倒数第二位中,所以在存储时,则只需要将倒数第二位的数值改变,其他位的数值不改变即可。所以在存储1时,不论bData的数值是多少,只需要和二进制数据10进行位或运算就可以将倒数第二位置1,而需要存储0时,则需要bData和0xfffffffd进行位与运算,即可达到清零倒数第二位的目的。

  需要注意的是,有些可能会认为在存储数值0时,会使用如下的代码实现:

bData = bData |(0 << (2 - 1));

  其实这样是错误的,因为0无论左移多少位都还是0,这样在进行位或运算时,0和1位或得到的结果会是1,无法实现设置对应的位为0的目的,所以需要使用以上的代码进行实现。

  异或位运算简单加密

  利用位运算的计算速度快,以及异或的特性(和同一个数字异或两次还是自身),可以用来简单加密数据,且加解密的速度会非常快。这种加密方式强度比较低,但是可以用于一般的加解密任务。例如,假设密匙是数字123,则加解密一个byte数组的代码如下:

byte[] b = {1,2,3,5,6};
byte key = 123;
//依次加密的代码 
for(int i = 0;i < b.length;i++){
b[i] = (byte)(b[i] ^ key); //利用异或加密 
}
//解密的代码 
for(int i = 0;i < b.length;i++){
b[i] = (byte)(b[i] ^ key); //利用异或解密
}

  点评:在该代码中,对数组b使用密匙key进行加密,加密的过程是将数组b中每个元素和key进行异或,加密以后的数据可以在实际应用中进行存储或网络传输,而解密时的操作和加密时一样,使用这种简单的加密方式虽然保密性不高,但是加解密的速度确实是很值得称赞的。

 

高效对比两个数组有几个数字相同(竞彩投注与开奖的数字对比)

一组字符         1,0,3,1,3,31,31,3,1,0,0,0,3,3 

一组校准字符 1,0,0,1,1,3,0,1,0,1,0,3,1,3 

要如何高效的确定有几个是相同的呢 .

利用异或的:两个元素异或结果为0,则相同。

代码:产生两个100000长度数组元素为100以内的随机数(元素一一对应),比较两者有哪些相同的元素,测试结果:两个数直接对比与异或对比相差不大,而且异或有时比直接对比还慢。(对比的速度与元素值的大小有关系如果为10000以内的随机数测试结果又不一样)

 

public class Test {

	public static void main(String[] args) {
		int len = 100000;
		int[] arr1 = new int[len];
		int[] arr2 = new int[len];
		for (int i = 0; i < len; i++) {
			arr1[i] = RandomUtils.nextInt(100);
			arr2[i] = RandomUtils.nextInt(100);
		}
		long start = System.currentTimeMillis();
		int s = 10000;
		int count = s;
		while (count > 0) {
			cycleEqual(arr1, arr2);
			count--;
		}
		System.out.println("cycleEqual time: "
				+ (System.currentTimeMillis() - start));
		
		start = System.currentTimeMillis();
		count = s;
		
		while (count > 0) {
			cycleExclusiveOR(arr1, arr2);
			count--;
		}
		System.out.println("cycleExclusiveOR time: "
				+ (System.currentTimeMillis() - start));
		
		start = System.currentTimeMillis();
		count = s;
		while (count > 0) {
			haflCycleExclusiveOR(arr1, arr2);
			count--;
		}
		System.out.println("haflCycleExclusiveOR time: "
				+ (System.currentTimeMillis() - start));
		
		start = System.currentTimeMillis();
		count = s;
		while (count > 0) {
			haflCycleEqual(arr1, arr2);
			count--;
		}
		System.out.println("haflCycleEqual time: "
				+ (System.currentTimeMillis() - start));

	}

	/**
	 *  循环等于对比
	 * @param arr1
	 * @param arr2
	 */
	public static void cycleEqual(int[] arr1, int[] arr2) {
		for (int i = 0; i < arr1.length; i++) {
			if (arr1[i] == arr2[i]) {
//				System.out.print(arr1[i] + ",");
				continue;
			}
		}
//		System.out.println();
	}

	/**
	 * 循环异或对比
	 * @param arr1
	 * @param arr2
	 */
	public static void cycleExclusiveOR(int[] arr1, int[] arr2) {
		for (int i = 0; i < arr1.length; i++) {
			if ((arr1[i] ^ arr2[i]) == 0) {
//				System.out.print(arr1[i] + ",");
				continue;
			}
		}
//		System.out.println();
	}

	/**
	 * 减半循环异或对比
	 * @param arr1
	 * @param arr2
	 */
	public static void haflCycleExclusiveOR(int[] arr1, int[] arr2) {
		int len = arr1.length;
		int halfLen = len/2;
		for (int i = 0; i < halfLen; i++) {
			if ((arr1[i] ^ arr2[i]) == 0) {
				
			}
			if((arr1[len - i - 1] ^ arr2[len - i - 1]) == 0){
				
			}
		}
	}

	/**
	 * 减半循环对比
	 * @param arr1
	 * @param arr2
	 */
	public static void haflCycleEqual(int[] arr1, int[] arr2) {
		int len = arr1.length;
		int halfLen = len/2;
		for (int i = 0; i < halfLen; i++) {
			if ((arr1[i] == arr2[i])) {
				
			}
			if((arr1[len - 1 - i] == arr2[len - 1 - i])){
				
			}
		}
	}
	
}

 结果:

 

cycleEqual time: 3969

cycleExclusiveOR time: 3984

haflCycleExclusiveOR time: 3187

haflCycleEqual time: 3219

 

位运算

>> 右移 右移一位相当于除2,右移n位相当于除以2的n次方。

例: 8>>2   相当于  8/2 = 4  

<< 左移 左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方 

例: 3<<2    相当于  3*4 = 12

异或两次还是原来本身

例: a = a^b^b

 

 

参考:

位操作基础篇之位操作全面总结

如何高效判断两个随机数组里面的元素是否完全相同?

对比尽量减少循环次数与if次数

位运算 优化运算速度

java移位运算符详解

十进制 与 二进制转换百科

java位运算 位运算符在HashMap中运用的一个场景

分享到:
评论
1 楼 lihaimian 2017-04-25  
你好,有个问题咨询一个,为何我在java中,无法使用与运算符,在if判断的时候,提示int不转转换为boolean
"Type mismatch: cannot convert from int to boolean"

相关推荐

    异或运算在算法中的经典运用

    “一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字?”这是经典的算法题,乍看这个题的思路特别多。

    简单了解python的一些位运算技巧

    位运算常用的运算符包括&(按位与), | (按位或),~(按位非),^(按位异或),&lt;&lt; (有符号左移位) ,&gt;&gt;(有符号右移位)。 下面用几个例子说明其应用,希望对你有所启发。 1、判断奇数还是偶数 通常判断...

    JAVA基础之java的移位运算

    因为Java 使用2的补码来存储负数,并且因为Java 中的所有整数都是有符号的,这样应用位运算符可以容易地达到意想不到的结果。例如,不管你如何打算,Java 用高位来代表负数。为避免这个讨厌的意外,请记住不管高位的...

    MySql运算符详解!!!

    数据库中的表结构确立后,...位运算符,包括按位与、按位或、按位取反、按位异或、按位左移和按位右移等位运算符。位运算必须先将数据转换为二进制,然后在二进制格式下进行操作,运算完成后,将二进制的值转换为原来的

    python编写SM3算法更新

    处理每个数据块:对每个数据块进行一系列操作,包括按位异或、置换、线性变换等。这些操作将数据块转化为中间状态。 进行轮函数运算:使用多轮运算,对中间状态进行非线性变换和线性变换,得到最终的消息摘要。 ...

    javascript文档

    ^= 运算符 对变量和表达式的值执行按位异或运算,结果赋给变量。 @cc_on 语句 激活条件编译支持。 @if 语句 根据表达式的值,有条件地执行一组语句。 @set 语句 创建用于条件编译语句的变量。 abs 方法 返回一个...

    915MHz RFID读写器校验的FPGA设计与实现

     从以上的程序可以看出,利用数字逻辑的基本原理可以将复杂的乘法以及除法运算全部转化为加法运算,即按位异或运算。本设计得到准确的16位CRC校验码,并将校验码加到原始数据中,两次运算合并为加法运算,提高了...

    RFID技术中的915MHz RFID读写器校验的FPGA设计与实现

     从以上的程序可以看出,利用数字逻辑的基本原理可以将复杂的乘法以及除法运算全部转化为加法运算,即按位异或运算。本设计得到准确的16位CRC校验码,并将校验码加到原始数据中,两次运算合并为一次加法运算,提高...

    xilinx平台下memtester可执行程序

    主要是捕获内存错误和一直处于很高或者很低位的坏位,测试随机值、异或比较、减法、乘法、除法、与或运算等。memtester 是一个用于测试计算机内存(RAM)的命令行工具。它的主要目的是检测内存中的问题、错误或缺陷...

    modbus通信协议

    这包括了象不连续的寄存器地址,要处理项的数目,域中实际数据字节数。 例如,如果主设备需要从设备读取一组保持寄存器(功能代码03),数据域指定了起始寄存器以及要读的寄存器数量。如果主设备写一组从设备的...

    JScript 语言参考

    ^= 运算符 对变量和表达式的值执行按位异或运算,结果赋给变量。 @cc_on 语句 激活条件编译支持。 @if 语句 根据表达式的值,有条件地执行一组语句。 @set 语句 创建用于条件编译语句的变量。 abs 方法 返回一个...

    微软JavaScript手册

    ^= 运算符 对变量和表达式的值执行按位异或运算,结果赋给变量。 @cc_on 语句 激活条件编译支持。 @if 语句 根据表达式的值,有条件地执行一组语句。 @set 语句 创建用于条件编译语句的变量。 abs 方法 返回一个...

    整理后java开发全套达内学习笔记(含练习)

    进行高精度运算可以用java.math包中BigDecimal类中的方法。 自动类型提升又称作隐式类型转换。 强制类型转换:int ti; (byte) ti ; 强制转换,丢弃高位 宣告变量名称的同时,加上“final”关键词来限定,这个...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    12.1.3 按位异或运算 192 12.1.4 求反运算 193 12.1.5 左移运算 193 1012.1.6 右移运算 193 12.2 位域(位段) 194 12.3 本章小结 13 文件 13.1 C文件概述 197 13.2 文件指针 198 13.3 文件的打开与关闭 199 13.3.1 ...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    12.1.3 按位异或运算 192 12.1.4 求反运算 193 12.1.5 左移运算 193 1012.1.6 右移运算 193 12.2 位域(位段) 194 12.3 本章小结 13 文件 13.1 C文件概述 197 13.2 文件指针 198 13.3 文件的打开与关闭 199 13.3.1 ...

    基于ARM+FPGA平台的二值神经网络加速方法研究

    现有的卷积神经网络由于其结构复杂且依赖的数据集庞大,难以满足某些实际应用或者计算平台对运算性能的要求和能耗的限制。针对这些应用或计算平台,对基于ARM+FPGA平台的二值化算法进行了研究,并设计了二值神经网络...

    适用于任意存取结构的动态多秘密共享方案 (2013年)

    为满足一般存取结构的多秘密共享方案在实际应用中的可验证性和动态性需求,提出一种适用于任意存取结构的动态可验证多秘密共享方案,其中每个参与者各自选取秘密份额,采用RSA 公钥密码体制将该份额通过公开信道发送...

    Objective-C2.0程序设计

    4.5.3 按位异或运算符 4.5.4 一次求反运算符 4.5.5 向左移位运算符 4.5.6 向右移位运算符 4.6 类型:_Bool、_Complex和_Imaginary 4.7 练习 第5章 循环结构 5.1 for语句 5.1.1 键盘输入 5.1.2 嵌套的for循环 5.1.3 ...

    c# 加密和解密相关代码

    在执行按位“异或”运算时,如果两个二进制数的 相应位都为1 或两个二进制数的相应位都为0,则返回0;如果两个二进制数的相应位其中一个为1 一个为0, 则返回1。 现在来了解一下使用“异或”加密或解密的执行过程,...

Global site tag (gtag.js) - Google Analytics