一.引言
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
Tips:
(1) 结果的符号只需判定除数和被除数是否同符号即可,除法统一转为两正整数相除
(2) 注意数字范围,默认取 int
MAX_INT = int(math.pow(2, 31) - 1)
MIN_INT = int(-math.pow(2, 31))
二.对数偷懒除法
题目要求不能使用乘法,除法和mod,这里可以通过对数减法达到除法的效果:
def logarithmDivide(_dividend, _divisor):
st = time.time()
if _divisor == 0:
return MAX_INT
if _dividend == 0:
return 0
# 判断正负号
isPositive = (_dividend < 0) == (_divisor < 0)
# 取绝对值
m = abs(_dividend)
n = abs(_divisor)
res = math.log(m) - math.log(n)
res = int(math.exp(res))
print("对数除法耗时: ", (time.time() - st))
if isPositive:
return min(res, 2147483647)
return max(0 - res, -2147483648)
三.递减法
除法本身其实是减法,可以通过逐级递减获取最终的值:
(1) result = 0
(2) dividend -= divisor; result + 1
(3) 直到 dividend 小于 divisor,退出
def oneByOneDivide(_dividend, _divisor):
st = time.time()
if _divisor == 0:
return MAX_INT
if _dividend == 0:
return 0
# 判断正负号
result = 0
isPositive = (_dividend < 0) == (_divisor < 0)
while _dividend >= _divisor:
_dividend -= _divisor
result += 1
print("递减除法耗时: ", (time.time() - st))
if isPositive:
return min(result, 2147483647)
return max(0 - result, -2147483648)
四.递减法 Plus
递减法中被除数每次减掉一个除数,当被除数 >> 除数时,程序执行时间很慢,因此可以适当将除数放缩,加快收敛速度:
(1) result = 0,k=1
(2) dividend -= divisor; result + k
(3) divisor *= 2,k *= 2 dividend -= divisor,result += k
(4) divisor 足够大是退出第一个 while,重新从 k=1 开始
(5) dividend 足够小时退出
def multipleDivide(_dividend, _divisor):
st = time.time()
if _divisor == 0:
return MAX_INT
if _dividend == 0:
return 0
# 判断正负号
result = 0
isPositive = (_dividend < 0) == (_divisor < 0)
tmp_dividend = _dividend
tmp_divisor = _divisor
while tmp_dividend >= tmp_divisor:
k = 1
tmp = tmp_divisor
while tmp_dividend >= tmp:
tmp_dividend -= tmp
result += k
k += k
tmp += tmp
print("倍数除法耗时: ", (time.time() - st))
if isPositive:
return min(result, 2147483647)
return max(0 - result, -2147483648)
五.递减法 Plussss
上面采用 2 作为递增的倍数,达到了快速收敛的速度,但是当 dividend >> divisor 时,2 指数增长也需要一定步数,此时可以给启动值一个大一点的除数,提高前面的收敛速度,后面被 dividend 和 divisor 差距不大时再退化为 2 指数增长:
def multipleDivideV2(_dividend, _divisor, factor):
st = time.time()
if _divisor == 0:
return MAX_INT
if _dividend == 0:
return 0
# 判断正负号
result = 0
isPositive = (_dividend < 0) == (_divisor < 0)
tmp_dividend = _dividend
tmp_divisor = _divisor
while tmp_dividend >= tmp_divisor:
k = 1
tmp = tmp_divisor
# 加大力度安排
while tmp_dividend >= factor * tmp:
tmp_dividend -= factor * tmp
result += factor * k
k += factor * k
tmp += factor * tmp
# 退化为2倍数增长收尾
while tmp_dividend >= tmp:
tmp_dividend -= tmp
result += k
k += k
tmp += tmp
print("倍数除法耗时: ", (time.time() - st))
if isPositive:
return min(result, 2147483647)
return max(0 - result, -2147483648)
六.总结
给定一个默认除法函数:
def commonDivide(_dividend, _divisor):
st = time.time()
re = _dividend / _divisor
print("正常除法耗时: ", (time.time() - st))
return int(re)
然后比较一个默认除法和上面几种方法的速度差异:
if __name__ == '__main__':
dividend = 265783921
divisor = 3
print(commonDivide(dividend, divisor))
print(logarithmDivide(dividend, divisor))
print(oneByOneDivide(dividend, divisor))
print(multipleDivide(dividend, divisor))
print(multipleDivideV2(dividend, divisor, 1000))
速度:common > logarithm > 加大力度 > 2倍数 >> oneByOne
初始除数给定恰当时,多倍数增长会优于2倍数增长;给的过大或过小时,两者速度相近
正常除法耗时: 1.1920928955078125e-06
88594640
logarithm 对数除法耗时: 3.0994415283203125e-06
88594640
oneByOne 递减除法耗时: 3.9744768142700195
88594640
2倍数增长 倍数除法耗时: 1.7881393432617188e-05
88594640
加大力度安排 倍数除法耗时: 6.9141387939453125e-06
88594640