[python2] 65 不用加减乘除做加法、减法

1. 不用加减乘除做加法

[python2] 65 不用加减乘除做加法、减法_第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 - 1 时,需要从前面的1借2,虽然结果仍然等于1,但是前面的那一位变成0。其它三种情况如0 - 0 = 0; 1 - 0 = 1; 1 - 1 = 0,都可以直接运算,不存在借位的问题。因此,在实现减法的过程中,应该着重考虑借位的情况。

若不考虑借位的情况,则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)
[python2] 65 不用加减乘除做加法、减法_第2张图片
代码运行结果

其中,有一个比较重要的函数getAllOne(),主要是在num2左移时,需要限制num2左移的位数。python对于int并没有位数限制,因此,需要根据num1和num2的位数,对num2 << 1进行截断,才能使得左移之后的结果等于0. 这里根据一定的位数得到相同位数,值为1的数的方法为:由于我们输入的十进制数,最高位肯定是1,(若不是1,前面的0可以省略),因此,想要得到十进制数对应二进制数的位数,就将该二进制数右移,知道该二进制数等于1,此时就可以得知二进制数的位数。想要得到相同位数,但值为1的数,就在右移时用另外一个数1左移,并给左移后的数加1,就可以了。具体代码如上,这里打印了部分用例结果。

[python2] 65 不用加减乘除做加法、减法_第3张图片
getAllOne()函数部分用例结果

参考:http://www.cppblog.com/qingbizhu/archive/2012/03/30/168148.html

你可能感兴趣的:([python2] 65 不用加减乘除做加法、减法)