【刷题笔记】快速乘算法

171 阅读1分钟

算法目标

快速计算两个整数相乘的结果。

例如,AA, BB 为整数(int),在不使用加法的前提下,计算 A×BA\times B 的结果。

算法思路

使用乘法分配律将乘法进行分解

举个例子来说明,计算 15×515 \times 5

5 可以分解为

5=4+1=22+205 = 4 + 1 = 2^2 + 2^0

那么,15×515\times 5 可以表示成:

15×22+15×2015 \times 2^2 + 15 \times 2^0

其中,15×2n15 \times 2^n 可以通过二进制的位移运算快速计算:15 >> n

如何通过代码快速进行分解

下一个问题,我们如何知道应该将 5 分解为 222^2202^0 呢?

其实很简单,了解二进制定义的可以不难看出,上面的分解方法,就是 5 的二进制表示:0101

那么代码写起来就比较简单了,只需要一位一位的计算 2 的乘法,然后再求和即可,2 的乘法又可以用二进制移位运算实现。

边界条件

正负号的处理

为了简单,我们在计算 A×BA\times B 时,不是一般性的假设 A>B|A| > |B|,并且 A<0A < 0, B>0B > 0

如果不是的话,可以交换 A、B 的值,单独计算正负号。

为什么将 A 当作负号、B 当作正号呢,因为这样相乘的结果也是负数,而整数的表示负数比正数多一个值。

代码实现

public int quickTimes(int a, int b) {
    if (a == 0 || b == 0) {
        return 0;
    }
    
    // |a| >= |b|
    if (Math.abs(a)) < Math.abs(b)) {
        int tmp = a;
        a = b;
        b = tmp;
    }
    
    if (b == 1) {
        return a;
    }
    
    if (b == -1) {
        if (a == Integer.MIN_VALUE) {
            return Integer.MAX_VALUE;
        }
        
        return -a;
    }
    
    if (a == Integer.MIN_VALUE) {
        return b > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
    }
    
    // a < 0, b > 0
    boolean reverse = false;
    if (a > 0) {
        a = -a;
        reverse = !reverse;
    }
    
    if (b < 0) {
        b = -b;
        reverse = !reverse;
    }
    
    int res = 0;
    int add = a;
    while (b > 0) {
        if ((b & 1) != 0) {
            // 需要判断是否溢出
            if (Integer.MIN_VALUE - res > add) {
                res = Integer.MIN_VALUE;
                break;
            }
            res += add;
        }
        
        if (b != 1) {
            // 需要判断是否溢出
            if (Integer.MIN_VALUE - add > add) {
                res = Integer.MIN_VALUE;
                break;
            }
            add += add;
        }
    }
    
    if (reverse) {
        return res == Integer.MIN_VALUE ? Integer.MAX_VALUE : -res;
    }
    return res;
}