题目:不用乘、除、取模运算来实现除法。
减法可以实现除法在是我们早就知道的,但是可能会出现问题,比如极端情况,a = 0x7FFFFFFF,b = 1,求a/b,这要减法运算多少次?
回想下我们开始学习计算机的时候,涉及到的进制之间的转换,就是给定你一个十进制数,写出他的二进制,刚开始的时候很傻,就按着书上的方法去不停的除以2,除以2,除以2,。。。可笑的是居然天真的算过1024的二进制,更可笑的是,这样的方法算出来后,当时还很高兴。。。
对进制转化熟悉了以后,我们就不再这么算了,比如给我们一个十进制数47,我们会首先想到 32,接下来 47 - 32 = 15,想到 8,接下来 15 - 8 = 7, 想到 4 ,到2, 到 1。然后将各自对应的幂次位上写1.
我们这时为什么不再每次除2, 除2 的运算了呢? 因为这样太慢了,我们总是可以找到最接近 它的2的幂次的数,然后剩余的部分进行相同的操作,最后得到结果。
本题的减法实现除法也是相同思路,当除数很小时,就决定了每次操作的步长很小,就需要循环更多的次数。所以通过上面的进制转化的启发,我们决定应该给步长乘以一个放大因子。而且这个放大因子是2的幂次。
被除数 - i * 除数,随着i 每次加1 就是我们通常想到的减方法。
被除数 - 2 ^ i * 除数,
而一个数乘以2的幂次 恰好可以用移位来代替,这就不需要乘法了。
unsigned int remainder = dividend; int consult = 0; while (remainder >= divisor) { int power = 0; while (remainder >= (divisor << power)) { remainder -= (divisor << power); consult += (1 << power); power++; } }核心的思想就是这些了,跟我们分析十进制转换的问题不同的是,这里总是从最小开始,试探着使用更大的步长。但本质上是一致的。
问题本身还有些比较繁琐的地方,涉及到有符号整形,最大最小值就够闹心,当然,我们可以在程序的实现中,将给定的参数强制转换成更高长度的类型,比如long long 来处理。
int Abs(int n){ int sign = n >> 31; int re = n ^ sign; return (re - sign); } int divide(int dividend, int divisor) { if(divisor == 1) return dividend; if(0x7fffffff == dividend && 0x7fffffff == divisor) return 1; if (0x80000000 == dividend && 0x80000000 == divisor) return 1; //get the sign int sign = divisor ^ dividend; // get absolute value. dividend = Abs(dividend); divisor = Abs(divisor); unsigned int remainder = dividend; int consult = 0; while (remainder >= divisor) { int power = 0; while (remainder >= (divisor << power)) { remainder -= (divisor << power); consult += (1 << power); power++; } } if(sign < 0) consult = -consult; return consult; }
【leetcode】Divide Two Integers,布布扣,bubuko.com
原文地址:http://blog.csdn.net/shiquxinkong/article/details/26393257