手撸代码系列(十)之分治法经典练习案例

第十期:实现大数乘法(分治法)

题目分析:以123456789*987654321为例

第一步: 当两个数都小于等于4位的时候,我们可以直接利用整数乘法进行相乘。(9999*9999=9998,0001,在int类型数据范围之中)

第二步:如果数 a 大于4位,我们对 a 进行相对平均切分成两段,对于长度为奇数的,后半段比前半段多一位。 a 1 = 1234 {a_1} = 1234 a1=1234(高位)和 a 2 = 56789 {a_2} = 56789 a2=56789(低位)。分别用 a 1 {a_1} a1, a 2 {a_2} a2 b {b} b 相乘得到 c 1 {c_1} c1 c 2 {c_2} c2。在 a 1 {a_1} a1 b {b} b 相乘的时候, a 1 {a_1} a1 小于4位, b {b} b 大于四位,这时我们将 a 1 {a_1} a1 b {b} b 的位置交换一下即可。判断出 b {b} b 超过4位,则利用相对平均切分将其分成两段。如此递归下去,终将两个数都会小于或等于4位。

第三步: c 1 {c_1} c1 c 2 {c_2} c2分别是高低位乘法结果,我们需要将二者利用加起来。但是不能直接相加,高位的结果后面需要先补一定数量的0。

第四步:对于补零函数,我们最容易想到的是利用循环一个一个的赋值,其实这样的效率不高。仍然可以利用分治思想,例如我们要生成100个0,我们先生成50个0,复制一下即可达到100个。

第五步:对于 大整数加法 , 我们依然利用分治的思想。如果加数都小于等于8位,直接相加。如果至少有一个数大于8位,我们先这两个加数相对平均分成两部分。高于8位的部分,以及末尾8位部分。我们从低位到高位计算。两个加数的末尾8位相加有三种情况。结果等于9位,结果等于8位,结果小于8位。如果等于9位,那么高于8位的部分相加需要加1后再和低位的和拼接起来。如果等于8,那么直接将其拼接在高于8位的部分的和之后。如果小于8位,先将其前面补零后直到补齐8位后再拼接。

Java代码:
import java.math.BigInteger;
/**
 * 大整数乘法(分治法)
 */
public class Test22 {
    public static void main(String[] args) {
        String res = multi("1846257934518462579345", "845169718462579345184625793452345");
        System.out.println(res);
        // 利用java的大数类型进行验证。
        BigInteger rres = (new BigInteger("1846257934518462579345")).
                multiply(new BigInteger("845169718462579345184625793452345"));
        System.out.println(rres);
    }

    private static String multi(String a, String b) {
        // 数据较小时,直接计算。
        if (a.length()<=4 && b.length()<=4){
            return Integer.parseInt(a)*Integer.parseInt(b)+"";
        }
        // 其中一个数大于4位。
        if (a.length()>4){
            int k = a.length()>>1;  // 近似二分
            String a1 = a.substring(0,k);   // 高位
            String a2 = a.substring(k); // 低位
            // 分别利用a1和a2与b相乘
            return add(multi(a1,b)+zero(a2.length()), multi(a2,b));
        }
        // 如果b大于4位数,a小于4位数。
        return multi(b,a);
    }
    private static String add(String a, String b) {
        // 当数比较小的时候,直接添加
        if (a.length()<=8 && b.length()<=8)
            return Integer.parseInt(a)+Integer.parseInt(b)+"";

        String a1 = "0";
        String a2 = a;
        if (a.length()>8){
            a1 = a.substring(0, a.length()-8);
            a2 = a.substring(a.length()-8);
        }
        String b1 = "0";
        String b2 = b;
        if (b.length()>8){
            b1 = b.substring(0, b.length()-8);
            b2 = b.substring(b.length()-8);
        }
        String t = add(a2, b2);
        // 相加结果超过8位,等于8位,小于8位
        // 小于8位,需要补0补到8位。
        while (t.length()<8)
            t = "0"+t;
        // 如果有进位,需要将第9位进位。
        if (t.length()==9){
            // 先将高位相加,再将进位1加到高位去。得到高位的和。在高位和基础上补上低位和。
            String temp = add(a1, b1);
            return add(temp,"1")+t.substring(1);
        }
        // 如果刚好等于8位,直接添加。
        return add(a1,b1)+t;
    }
    
    /**
     * 分治法生成length个0
     * @param length
     * @return
     */
    private static String zero(int length) {
        // 出口
        if (length==0)
            return "";
        if (length==1)
            return "0";
        int num = length>>1;
        return zero(num)+zero(num)+zero(length%2);
        // 一句话搞定。
        // return zero(length/2)+zero(length/2)+zero(length%2);
    }
}
运行结果:

在这里插入图片描述

知识补充: 分析基本数据类型的特点,最大值和最小值。
  1. 基本类型:int 二进制位数:32(4个字节)
    包装类:java.lang.Integer
    最小值:Integer.MIN_VALUE= -2147483648 2 − 31 {2^{ - 31}} 231
    最大值:Integer.MAX_VALUE= 2147483647 2 − 31 − 1 {2^{ - 31}}{\rm{ - }}1 2311
  2. 基本类型:short 二进制位数:16(2个字节)
    包装类:java.lang.Short
    最小值:Short.MIN_VALUE= -32768 2 − 15 {2^{ - 15}} 215
    最大值:Short.MAX_VALUE= 32767 2 − 15 − 1 {2^{ - 15}}{\rm{ - }}1 2151
  3. 基本类型:long 二进制位数:64 (8个字节)
    包装类:java.lang.Long
    最小值:Long.MIN_VALUE=-9223372036854775808 2 − 63 {2^{ - 63}} 263
    最大值:Long.MAX_VALUE=9223372036854775807 2 − 63 − 1 {2^{ - 63}}{\rm{ - }}1 2631
  4. 基本类型:float 二进制位数:32 (4个字节)
    包装类:java.lang.Float
    最小值:Float.MIN_VALUE=1.4E-45 2 − 149 {2^{ - 149}} 2149
    最大值:Float.MAX_VALUE=3.4028235E38 2 − 128 − 1 {2^{ - 128}}{\rm{ - }}1 21281
    5.基本类型:double 二进制位数:64 (8个字节)
    包装类:java.lang.Double
    最小值:Double.MIN_VALUE=4.9E-324 2 − 1074 {2^{ - 1074}} 21074
    最大值:Double.MAX_VALUE=1.7976931348623157E308 2 − 1024 − 1 {2^{ - 1024}}{\rm{ - }}1 210241

你可能感兴趣的:(手撸代码系列(十)之分治法经典练习案例)