一. 位操作基础,用一张表描述位操作符的应用规则并详细解释。
二. 常用位操作小技巧,有判断奇偶、交换两数、变换符号、求绝对值。
三. 位操作与空间压缩,针对筛素数进行空间压缩。
四. 位操作的趣味应用,列举了位操作在高低位交换、二进制逆序、二进制中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之间的所有奇数。
- for (i = 0; i < 100; ++i)
- if (i & 1)
- printf("%d ", i);
- putchar('\n');
2.交换两数
一般的写法是:
- void Swap(int &a, int &b)
- {
- if (a != b)
- {
- int c = a;
- a = b;
- b = c;
- }
- }
可以用位操作来实现交换两数而不用第三方变量:
- void Swap(int &a, int &b)
- {
- if (a != b)
- {
- a ^= b;
- b ^= a;
- a ^= b;
- }
- }
可以这样理解:
第一步 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即可。完整代码如下:
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- #include <stdio.h>
- int SignReversal(int a)
- {
- return ~a + 1;
- }
- int main()
- {
- printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows ) ---\n\n");
- int a = 7, b = -12345;
- printf("%d %d\n", SignReversal(a), SignReversal(b));
- return 0;
- }
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。完整代码如下:
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- int my_abs(int a)
- {
- int i = a >> 31;
- return i == 0 ? a : (~a + 1);
- }
现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下:
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- int my_abs(int a)
- {
- int i = a >> 31;
- return ((a ^ i) - i);
- }
注意这种方法没用任何判断表达式,而且有些笔面试题就要求这样做,因此建议读者记住该方法(^_^讲解过后应该是比较好记了)。
实际应用:
位运算存储数据
利用位运算存储数据,主要是为了减少程序占用的内存。以int数据为例子,如果按照十进制的方式存储数据,一个32位的int变量只能存储一个数值,而如果使用二进制方式存储数据(缺点是只能存储0或1两个数据)则可以存储32个数据,将极大的节约内存。例如,在一个int变量的从右侧开始倒数第2位存储数据,则存储和读取数据的代码如下所示:
//存储数值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时,会使用如下的代码实现:
其实这样是错误的,因为0无论左移多少位都还是0,这样在进行位或运算时,0和1位或得到的结果会是1,无法实现设置对应的位为0的目的,所以需要使用以上的代码进行实现。
异或位运算简单加密
利用位运算的计算速度快,以及异或的特性(和同一个数字异或两次还是自身),可以用来简单加密数据,且加解密的速度会非常快。这种加密方式强度比较低,但是可以用于一般的加解密任务。例如,假设密匙是数字123,则加解密一个byte数组的代码如下:
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位运算 位运算符在HashMap中运用的一个场景
相关推荐
按位异或在校验和计算中的应用通常涉及到多个数据块的异或,例如,文件的所有字节可以依次进行异或,生成一个单一的校验和。这个校验和可以用于检查文件是否在传输过程中发生错误。如果在接收端计算出的校验和与发送...
本篇文章将深入探讨异或操作符的工作原理、性质及其在实际应用中的作用。 首先,我们需要了解位运算的基本概念。在计算机中,所有的数据都以二进制形式存储,即由0和1组成。位运算就是对这些二进制位进行的操作,...
在IT领域,位运算是一种基础且...压缩包中的文件可能是Matlab代码示例,用于演示这些概念的实际应用。学习和掌握位异或及其在图像运算中的应用,对于提升IT专业技能,尤其是在图像处理和计算机视觉方面,是至关重要的。
在实际应用中,异或运算有以下几个关键特性: 1. **交换律**:异或运算满足交换律,即A XOR B = B XOR A。这意味着异或的结果不会因输入的顺序改变而改变。 2. **结合律**:异或不满足结合律,(A XOR B) XOR C 不...
在实际应用中,根据系统需求和性能要求,可能会选择不同的校验机制。例如,对于需要高度可靠性的通信系统,CRC可能更为合适;而对于资源有限的环境,BCC或LRC可能是更经济的选择。 总之,在线异或运算作为一种基础...
在实际应用中,可能还需要考虑端口的开漏模式、上拉电阻以及在写入P0口前先清除数据总线等细节。此外,为了确保正确显示,我们可能需要在每次改变P0口状态后加入适当的延时,以便人眼能捕捉到变化。 通过这样的程序...
在本次的单片机课程设计中,我们关注的是利用AT89S52单片机进行C语言编程,实现一个有趣的实用功能:通过P0端口显示按位异或运算的结果。这一设计不仅锻炼了我们的硬件接口操作能力,也深化了对C语言编程的理解。 ...
在实际应用中,这样的小工具可以帮助用户快速验证文件的完整性,尤其是在网络传输大量数据或进行磁盘备份时。例如,你可以先计算原始文件的异或校验和,然后在接收或恢复文件后,再次计算并对比校验和,以确认数据...
对于8位或9位的数据,可以采用逐位异或运算的方式来计算总的奇偶性。 #### 四、奇偶校验电路的应用 奇偶校验电路可以应用于发送端的奇偶校验位发生器或接收端的奇偶校验器。 - **发送端:** 产生校验位,确保数据...
在实际应用中,如加密算法,十六进制异或运算可以用于生成密文。比如简单的凯撒密码,通过一个固定的偏移量与明文异或,可以得到密文,解密时再用相同的偏移量与密文异或,就能恢复原始信息。异或运算是可逆的,这是...
在IT领域,16进制异或操作是一种基础但至关重要的计算方法,广泛应用于加密、校验和计算、数据比较等多个场景。...通过研究和使用这款工具,可以深入理解异或运算的原理,并将其应用到实际项目中,提升工作效率。
6. 实际应用: - 在网络通信中,异或校验可以用于简单的错误检测,比如FTP协议就使用了类似的方法。 - 在存储系统中,硬盘的SMART(自监测、分析和报告技术)也利用了异或来检测和预测硬盘的潜在故障。 - 在软件...
在实际应用中,通常会使用块加密模式,比如CBC(链式模式)或ECB(电子密码本模式),或者使用密钥派生函数(如PBKDF2)来增加密钥的安全性。 综上所述,"异或运算加密"是通过C#的Windows Forms应用程序实现的一种...
异或运算在编程中有很多实际应用。例如,在数据加密算法中,异或常用于混淆原始数据,因为异或两次相同的密钥会恢复原始数据。在错误检测和纠正码中,如CRC(循环冗余校验),异或用于检查数据传输的完整性。此外,...
总之,这个实验结合了硬件和软件,通过51单片机的P0口展示了按位异或运算的实际应用,不仅能够帮助学习者巩固理论知识,还锻炼了他们的实践操作技能。通过阅读和分析提供的程序源代码,可以深入学习51单片机的I/O...
5. 处理中断和循环:在实际应用中,可能需要处理中断事件,比如定时器中断,以周期性地更新显示或处理新的运算请求。此外,如果需要连续显示多个异或结果,还需要设计适当的循环结构。 这个实例的源代码很可能是用...
总之,这个实例通过实际操作,帮助学习者掌握单片机C语言编程,特别是如何使用P0口进行数据输出,以及理解按位异或运算在单片机系统中的应用。通过分析和运行提供的源代码,可以深入理解单片机硬件与软件的交互,为...
在IT领域,校验和是一种常见的数据完整性检查方法,用于检测数据在传输或存储过程中是否发生错误。"按位异或校验和计算器"是一种基于位操作的校验和算法,它...在实际应用中,需要根据具体需求选择合适的校验和算法。
从这些文件可以看出,这可能是一个简单的C# Windows Forms应用程序,用于演示或测试8位异或运算的功能。用户可能可以通过图形界面输入两个数字,然后程序会计算它们的8位异或结果,并显示出来。 总结,"XOR.rar...
在C++编程中,`CString`是MFC(Microsoft Foundation Classes)库提供的一种字符串类,它为C风格...在实际应用中,要确保正确处理边界条件,避免内存泄漏,并考虑到编码问题,尤其是在涉及多字节字符集(如UTF-8)时。