lintcode中有这样一个题,是在不使用数学运算符的情况下,用位运算符实现两个数求和,关于这个问题,有不少朋友已经给出了自己的解法,这篇文章主要是记录了我在使用python2.7完成逻辑加之后,实现逻辑减的过程。
首先贴上代码部分如下,中间docstring部分详细讲解了代码实现流程
# coding: utf-8
"""
使用位运算的方法计算两数的和,以及计算两数的差
@version: v1.0
@author: SoulReaperrX
@license: Apache Licence
@contact: 407495044@qq.com
@software: PyCharm
@file: sumBitwise.py
@time: 2018/6/13 19:17
"""
def sumbitwise(a, b):
"""
给出两个整数a和b, 求他们的和, 但不能使用 + 等数学运算符
(原题连接:http://www.lintcode.com/zh-cn/problem/a-b-problem/)。
实际上,数的相加规则如下,以13+17为例。
1、首先各个位相加,十位1+1=2,个位3+7=0,进位为一
2、然后得到进位数,10,将第一步得到的20和进位数10采用同样的方法继续加
3、重复上述步骤,直到没有进位为止。
那么我们考虑二进制的该方案如何实现,因为考虑到使用位运算。
首先,2进制的转换在python中可以通过bin(num)函数实现,同样的,八进制可以用oct(num),16进制可以用hex(num)
二进制转十进制,我们则可以使用将形如0b101的二进制数首先转换为str类型,然后取str[2:]即可,第二个下标默认是字符串长度
我们得到二进制的13是1101,17是10001,那么我们考虑以上三步,只能运用到位运算符
1、首先,1101+10001,不考虑进位时,为11100,进位为10
2、我们将11100和进位相加,得到11110,如果没有进位,则输出
3、否则重复上面两步
那么位运算中,计算时1+1=0,1+0=1,0+1=1,0+0=0,这是一个典型的异或运算
即同真同假时才为真。
而得到进位的加法是纯粹的位运算与,1+1才为1,并进一位。
因此我们可以这样coding。
"""
str_bin_a = bin(a) # 得到的是str类型的二进制
str_bin_b = bin(b) # 得到的是str类型的二进制
bin_a = int(str_bin_a[2:], 2)
bin_b = int(str_bin_b[2:], 2)
if a == 0:
return b
if b == 0:
return a
summery = bin_a ^ bin_b # 将二进制的a和b取异或运算summery
carry = (bin_a & bin_b) << 1 # 将进位计算出来并且左移一位,和summery取异或
return sumbitwise(summery, carry)
def subbitwise(a, b):
"""
那么我们如何实现位运算符实现减法呢?我们来看两个位运算符的减法实现。
我们以二进制9即1001和二进制5即0101为例。
1001和0101的运算减结果为0100。
这个计算中,被减数位为0,减数相同的位为1时,得到的结果数同位为1,这是因为被减数发生了退位。
如果我们不考虑退位的情况,被减数的位为0时,且减数同位为1的情况下,我们默认将被减数该位看作2,
再进行位的减法操作,我们可以得到
1001 1201
0101 ==>> 0101
1100 1100
修改成1201是纯粹为了方便理解,事实上,这也是一个典型的异或运算。
这个操作中,由于我们在2^2这个位上,被减数借了一位,因此我们需要在之后把结果数中的位还回去
也就是将结果数1100,还掉借的1000这一位
1100
1000
0100
来得到真实的结果数,而退位如何得到呢?我们之前分析过,只有被减数的位是0,减数同样的位是1,即
xx0xxx
xx1xxx
这种情况时,才会需要退位
也就是如下才会产生退位。
0 0 1 1 被减数的位
0 1 0 1 减数的位
0 1 0 0 结果1表示有退位
这是一个符合非A且B的运算,即~a&b为1时,产生了退位。
同样是分析1001和0101,按照~a&b得到的退位是0100.
而这个退位是从2^3这一位上借的,所以退位也需要左移一位,再与前面产生的结果取异或运算。
于是,我们得到位运算符计算减法的算法。
1、首先,将两个运算数进行异或运算得到summery
2、然后,将两个运算数进行~a&b运算得到退位carry
3、重复以上两步,直到没有退位
"""
# 计算两数之差,并且不使用逻辑运算符+,而是使用位运算符
str_bin_a = bin(a) # 得到的是str类型的二进制
str_bin_b = bin(b) # 得到的是str类型的二进制
bin_a = int(str_bin_a[2:], 2)
bin_b = int(str_bin_b[2:], 2)
if a == 0:
return b
if b == 0:
return a
summery = bin_a ^ bin_b # 将二进制的a和b取异或运算得到summery
carry = (~bin_a & bin_b) << 1 # 将进位计算出来并左移一位,准备和summery取异或
return subbitwise(summery, carry)
if __name__ == '__main__':
print sumbitwise(13, 17)
print subbitwise(9, 5)
对于python初学者,可能会产生疑惑的地方,这里大概解释一下。
bin(number)函数是一个将十进制例如9数转换为形如'0b1001'的string类型数据的函数。
bin_a = int(str_bin_a[2:], 2)是将str类型的str_bin_a转换为二进制形式的数据bin_a,因为python中二进制的定义是通过这样的方式:binary = int('str', 2)的方式。因此,我们通过截取之前得到的'0b1001'字符串的下标2至最后一位,得到二进制数。
bin_a = int(str_bin_a[2:], 2)等价于bin_a = int('1001', 2)。
一个补充的小知识点,string类型在使用str[index1:index2]截取字符串时,如果index2为空,则会截取index1开始到字符串结尾的一个子串。
因为事实上大家面试做笔试题,或者有相关考试的时候,往往遇到的并不是在lintcode等等网站刷过的原题,所以适当的对lintcode上面的算法题进行举一反三是不错的提升方式,在这个过程中,大家也可以对某种算法或者思想的理解更加深入。我把这种方式当做一种学习方式,同时算是记录了自己的学习过程,希望和大家共同进步。
后续能否实现使用位运算符进行乘法和除法运算呢?大家可以思考一下,欢迎讨论。
参考:
1.csdn博客 -- wangyezi19930928的专栏。
https://blog.csdn.net/wangyezi19930928/article/details/52516332