不使用加减乘除求两数之和

304 阅读1分钟

原题链接

写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

看到这题的第一感觉就是使用位运算来解决,但是我对位运算相当不熟悉,于是一想就是2个小时(话说这个题为啥是简单题呀,感觉一点也不简单), 先回顾一下位运算:

符号描述运算规则
与 两个位都为1时,结果才为1 
或 两个位都为0时,结果才为0 
异或 两个位相同为0,相异为1 
取反 0变1,1变0 
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0 
>> 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

干看完全没有头绪,于是就使用笨方法,每个运算符都计算一遍,找找规律,最后发现两个点:

先来看下例子:
10 + 7
转成2进制相加:
  1010  +
  0111  =
 10001
 
规律11010  &  
    0111  =
    0010
    根据&的运算规则,都为1结果为1,正好二进制1+1需要进一位,然后<<运算符刚好可以做的进位操作
    那么  0010 << 1  = 0100 (8)   可以得到需要进位的值
    
规律21010  ^
    0111  =
    1101
    根据^的运算规则,0 ^ 0 = 0 , 1 ^ 1 = 0 , 0 ^ 1 = 1 ;
    正好和加法的到的结果一致,只是 1 + 1 = 10 进位的 1 没有算上,
    而规律1可以得到进位的值,再和 ^ 得到的值相加可以了。
    
我们计算一下:
    0100 + 
    1101 = 
   10001
   刚好和预期一样.
   
于是我们可以的出结论:a + b = ((a & b) << 1) + (a ^ b) , 等式的右边也使用了 ”+“号,我们使用递归调用,随着递归的进行,进位的值会不断向高位推进,直到不需要进位,即 (a & b) << 1 == 0,这也是我们递归的结束条件。

最终代码:
func add(_ a: Int, _ b: Int) -> Int {
    if a == 0 {
        return b
    }
    return add((a & b) << 1 , a ^ b) //由于 << y优先级高于 & ,需要把 a & b 括起来
}

提交 -- 过啦!