昨天LeetCode随便找了一题准备日常开刷,题目是371.两整数之和。题目大概要求是这样的:不使用+和-的情况下,实现两个整数的加法运算操作。有兴趣请自行移步LeetCode查看这个题。
了解过计算机底层原理的应该知道,目前多数计算机是基于二进制,对十进制数字不能直接利用。即便是整体使用了二进制,但计算机利用其位操作确实实现了十进制的计算,所以算法上可以用类似的原理进行模拟实现这题的需求。原理核心主要是:异或负责表示该位运算结果,两个数相与后左移一位负责表示进位。当进位出现0之后就停止运算,输出这时异或的值。
开始没想太多,python写出来大概就4行左右,脚本运行后发现做运算的两个数在同符号的情况下没有问题,一旦两个数字是异号的时候,程序就陷入了无限循环。
while b:
a, b = a ^ b, (a & b) << 1
return a
然后用打印输出的方式查看,发现数字已经十分庞大,然而还在翻倍变化,没有停下来。
随后思考了一番,想到了python数字的特点,声明的时候没有类型的定义,小数和整数有时候运算比较模糊,重要的是一个整数超过表示范围之后是可以变长的,变成大数运算,不会发生整数溢出。因为这个特性,所以数字位数不断增多,按照算法设计想要在最后异或出0是个难事。自动转化这个现象很好验证,int表示范围内高次方或者大范围阶乘这样的大数运算,可以看得出来这个特性。
既然数字都是按照补码来的,可以通过与运算的方式人为地制造溢出,舍弃过大的那一部分。拿64位的python来说,每次异或运算或者与运算之后再和0xFFFFFFFF做一次与运算,从而保证数据的长度。最后注意一下数的大小,如果结果大于0x7FFFFFFF,溢出了,还要和0xFFFFFFFF异或之后取反。
最后通过的代码:
class Solution:
def getSum(self, a, b):
"""
:type a: int
:type b: int
:rtype: int
"""
while b:
a, b = (a ^ b) & 0xFFFFFFFF, ((a & b) << 1) & 0xFFFFFFFF
if a> 0x7FFFFFFF:
a = ~(a ^ 0xFFFFFFFF)
return a
位运算要慎重,位运算要慎重,位运算要慎重!