1. 不用加减乘除做加法
#方法一:使用位运算
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
class Solution:
def Add(self, num1, num2):
# write code here
while num2:
mainPart = num1^num2 #不带进位的加法,1 + 1 = 0; 1 + 0 = 1; 0 + 1 = 1; 0 + 0 = 0;相当于异或运算
num2 = ((num1&num2)<<1) #只得到加法计算的进位,1 + 1 = 0; 0 + 1 = 0; 1 + 0 = 0; 0 + 0 = 0;相当于与运算
# 其实1 + 1之后应该变成10和nonCarry相加从而得到最终的和。因此相当于与运算之后左移一位
num1 = mainPart&0xffffffff #截断,使得num1永远只有32位
#截断的原因是python中int没有限制为32位
#return num1 if num1<0x7fffffff else ~(num1^0xffffffff)
return num1 if num1<0x7fffffff else -(((~num1)+1)&0xffffffff)
#if num1<0x7fffffff,则num1为正数,否则,num1为负数
#若num1为负数,则将其取反加1得到其相反数也就是正数形式,给正数加一个负号就得到了num1的正确值
#方法二:使用python自带的库
# -*- coding:utf-8 -*-
class Solution:
def Add(self, num1, num2):
# write code here
return sum([num1, num2]) #注意这里必须传入一个list
2. 不用加减乘除做减法
1). 思路1
a - b = a + (-b) 因此可以使用上面的加法来间接达到减法的目的
!!!如何得到整数的相反数?取反加1
2). 思路二
位操作实现减法(以14 - 7 为例)
首先将被减数 14 和 减数 7 都转化为二进制,看看二进制减法如何运算。
(14) 10 = (1110) 2
(7) 10 = (0111) 2
若不考虑借位的情况,则0 - 0 = 0; 1 - 0 = 1; 0 - 1 = 1;1 - 1 = 0;这个操作等同于异或运算。
对于借位的情况,我们假设不从前一位数借位,而是从上天借位,计算得到差之后,再还给上天即减去借来的位即可。只有在被减数为0,减数为1时才需要借位,而且借来的数值为10,即减数为左移一位。为了能够顺利找出这种情况,我们先将被减数为1,减数为1的可能性排除,在不考虑借位的情况下,被减数的1永远都不会由于借给别人而变成0,因此若被减数为1,减数也为1,二者的差一定为0,于是我们想办法去除被减数为1,减数也为1的这些位,剩下的位,若减数为1,被减数一定为0,则需要向上天借位。
如何去除被减数a和减数b中,被减数为1,减数也为1的这些位呢?首先 a & b则只剩下被减数为1,减数也为1的位是1,其他位都为0。然后再使用a = a & b ^ a和 b = a & b ^ b,此时的 a 和 b就去掉了同时为1的位,变成同时为0.
1). 先 a&b得到a 和 b中都是1的位,然后使用a = a & b^ a和 b = a & b ^ b 得到新的a和新的b,它们不存在同时都是1的位。2). 使用nonBorrow = a ^ b 得到的nonBorrow,是不考虑借位时a - b的差值。3). 此时的 a 和 b,若减数b为1,被减数a一定为0,因此肯定会向上天借位,将减数b左移一位便是需要向上天借来的,使得 0 - 1 = 1的值。nonBorrow 是被减数a使用上天借来的位减去减数b的差值,使用nonBorrow - (b << 1)将借来的位还给上天,即为 a - b之后实际的差值。nonBorrow - (b << 1)仍然重复前两步的操作,将nonBorrow 作为被减数,b << 1作为减数,重复,直到b << 1 == 0为止。
# subtraction.py
class solution:
def subtraction(self, num1, num2):
allOneNum1 = self.getAllOne(num1) #获取与num2位数相同,但是值全是1的数
# print("allOneNum1 = ", allOneNum1)
while num2:
# print("num2 = ", num2)
temp = num1 & num2 # 取出num1和num2中同时为1的位
num1 = temp ^ num1
num2 = temp ^ num2 # 将num1和num2中同时为1的位,都置为0
nonBorrow = num1 ^ num2 #不考虑借位的情况,1 - 1 = 0; 1 - 0 = 1; 0 - 1 = 1; 0 - 0 = 0,类似于异或操作
num2 = (num2 << 1) & allOneNum1 #num2 << 1即向上天额外借来的数值,需要从nonBorrow中减去
# 这里对num2 << 1之后的数需要截断,截断的标准为num1或num2中较长的位数,这里为了简便,使用被减数num1的位数
num1 = nonBorrow & 0xffffffff #此时num1 = nonBorrow作为被减数,num2作为减数,继续相减直到num2 == 0
# & 0xffffffff是为了在位数溢出时进行截断
return num1 if num1 < 0x7fffffff else -((~num1 + 1) & 0xffffffff)
# 获取与num2位数相同,但是值全是1的数(即获取位数相同时的最大值)
def getAllOne(self, num_input):
num_output = 1
while num_input - 1:
num_input = num_input >> 1
num_output = (num_output << 1) + 1
return num_output
if __name__ == "__main__":
s = solution()
sumResult = s.subtraction(1400, 7)
print("140 - 7 = ", sumResult)
其中,有一个比较重要的函数getAllOne(),主要是在num2左移时,需要限制num2左移的位数。python对于int并没有位数限制,因此,需要根据num1和num2的位数,对num2 << 1进行截断,才能使得左移之后的结果等于0. 这里根据一定的位数得到相同位数,值为1的数的方法为:由于我们输入的十进制数,最高位肯定是1,(若不是1,前面的0可以省略),因此,想要得到十进制数对应二进制数的位数,就将该二进制数右移,知道该二进制数等于1,此时就可以得知二进制数的位数。想要得到相同位数,但值为1的数,就在右移时用另外一个数1左移,并给左移后的数加1,就可以了。具体代码如上,这里打印了部分用例结果。
参考:http://www.cppblog.com/qingbizhu/archive/2012/03/30/168148.html