题目
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
解析
预备知识
首先观察题目要求,不能使用加减乘除法,此时必然只能用位运算来做。题目考察的其实解释加法的底层实现机制,比如常见的加法器的原理。
一般的加法器原理:会先对相加的两个数做不进位加法,然后再将结果与进位值相加,重复以上过程,直到没有进位产生即可。这里我们就用10进制模拟此过程,比如34加57:
1. 首先对34和57做不进位加法,结果为81。
2. 计算进位值,这里4 + 7 = 11,故而进位值为10
3. 81 + 10 = 91,即可最终结果。若这里如果还有进位值产生的话,就要重复以上2个步骤,直到没有进位产生。
思路一
预备知识里是对十进制计算过程的描述,而在计算机里十进制数都是采用二进制形式的,这种算法当然适合二进制。问题就在于题目要求不能使用加法,问题变成如何使用位运算模拟加法器原理的算法。先模拟一下二进制加法的过程的吧,2(10) + 3(11):
1. 不进位加法:10 + 11 = 01
2. 进位值为:100
3. 100 + 01 = 101,所以10 + 11 = 5(101)
我们可以发现在不进位加法中,0+1, 1+0等于1,而0+0,1+1等于0,很明显这符合异或运算。
进位值的则可以看做是把两个数的二进制中所含的公共的1的位置找出来,同时左移1位。那么如何找出公共的1呢?与运算可以找出公共1的位置。
/**
* 支持所有整数
* @param num1
* @param num2
* @return
*/
public static int Add2(int num1,int num2) {
int result = 0, carry = 0;
do {
result = num1 ^ num2;
carry = (num1 & num2) << 1;
num1 = result;
num2 = carry;
//直到不产生进位为止
} while(num2 != 0);
return result;
}
思路二
常规思路,对应位相加并进位即可。这里可以利用逻辑右移并不断对两个数的最后一位进行异或,同时产生进位值。
在这处理的过程中,会有一个中间值来表示若此位有进位,则应该加上这个值。这里通过或操作实现当前结果前面进位。
/**
* 我的初始做法
* @param num1
* @param num2
* @return
*/
public static int Add(int num1,int num2) {
int result = 0, carry = 0, bitResult = 1;
do {
int bit1 = num1 & 1;
int bit2 = num2 & 1;
int bitValue = bit1 ^ bit2;
boolean flag = false;
if(bit1 == 1 && bit2 == 1) {
flag = true;
}
int temp = bitValue ^ carry;
if(bitValue == 1 && carry == 1) {
flag = true;
}
if(temp == 1) {
result |= bitResult;
}
carry = flag ? 1 : 0;
bitResult <<= 1;
num1 >>>= 1;
num2 >>>= 1;
} while(num1 != 0 || num2 != 0);
if(carry == 1) {
result |= bitResult;
}
return result;
}
拓展
如何在申请新的变量的情况下,实现2个变量交换值呢?
//--------拓展-----------
//不使用新的变量,交换2个值
public static void exchange1(int[] num, int i, int j) {
num[i] = num[i] + num[j];
num[j] = num[i] - num[j];
num[i] = num[i] - num[j];
}
那么如果不能使用加法。那就利用位运算,通过异或操作,可以获得2个数每一位的相同或不相同的情况,之后再将异或结果与其中任何一个数做异或,即可得到另一个数。因为若当前位为0,表示该位上两者相同,所以可以根据任何一个数得到另一个数该位上的值。若当前位为1,表示该位上两者相反,这样异或结果后即可得到相反的数。
public static void exchange2(int[] num, int i, int j) {
num[i] = num[i] ^ num[j];
num[j] = num[i] ^ num[j];
num[j] = num[i] ^ num[j];
}
总结
传统的做法不再适用时,就可以利用位运算了。