[JAVA]大整数乘法(分治法)

目录

简介

算法思路

改进思路

具体实现思路

思路补充

完整代码

声明


简介

大整数乘法是一种用于计算两个非常大的整数相乘的方法,通常超出了计算机硬件所能直接处理的范围。在这种情况下,我们需要使用一些特殊的算法来处理这类问题。

本文将详细介绍大整数乘法的分治算法


算法思路

将两个大整数A、B进行拆分

A\times B=(A_1\times 10^{\frac{n}{2}}+A_0)(B_1\times 10^{\frac{n}{2}}+B_0)

其中 `A1` 和 `B1` 代表 `A` 和 `B` 的高位部分,`A0` 和 `B0` 代表低位部分,`n` 是整数的位数。

对公式化简可以得到

C=A\times B=(A_1\times 10^{\frac{n}{2}}+A_0)(B_1\times 10^{\frac{n}{2}}+B_0) \newline =(A_1\times B_1)10^n+(A_1\times B_0+A_0\times B_1)10^{\frac{n}{2}}+(A_0\times B_0)\newline =C_210^n+C_110^\frac{n}{2}+C_0

在这里我们其实需要计算四个部分的乘积,而为了减少时间复杂度,一般会对C_1部分进一步化简,得到一个更快的算法,即

C_1=(A_1+A_0)(B_1+B_0)-(C_2+C_0)

按照以上思路,即可设计出大整数乘法的整个程序。

但是这个算法存在一个不足之处,就在于A、B两个数字的位数必须要相同,当位数不同时无法使用该方法,故本文将对该算法进一步改进,让其在基本大整数乘法的基础上得到优化。


改进思路

为了让算法适用范围更广,必然需要考虑到两个大整数位数不同的情况,

假设

其中B有x位数字,D有y位数字

则存在

XY=(A\times 10^x+B)(C\times10^y+D)\newline =AC\times 10^{x+y}+AD\times10^x+BC\times 10^y+BD

根据该公式即可编写程序实现优化后的大整数乘法


具体实现思路

定义大整数乘法方法,为了让整数可以取到特别大,需要用字符串来接收和操作

public static String largeInt(String X, String Y)

首先获取两个大整数的位数,并根据位数分别截取字符串XY,获取A、B、C、D的值

int x = X.length();
int y = Y.length();
String A = X.substring(0, x - x / 2);//分别截取数字为AB*CD
String B = X.substring(x - x / 2);
String C = Y.substring(0, y - y / 2);
String D = Y.substring(y - y / 2);

根据前面的公式,我们需要分别得到AC, AD, BC, BD的值

而ABCD是大整数XY的一部分,故可以看作大整数,嵌套调用函数来计算其值,即

String AC = largeInt(A, C);//按照上面公式,计算每一组乘数,递归实现
String BD = largeInt(B, D);
String AD = largeInt(A, D);
String BC = largeInt(B, C);

在看回公式。剩下的部分除了相加,还有乘以10的n次方

而一个十进制的整数乘以10的n次方,即小数点右移一位

具体到本题,我们只需要在字符串的后面补n个0即可

代码如下

        int i;
        for (i = 0; i < x+y; i++)
            AC = AC + "0";
        for (i = 0; i < x; i++)
            AD = AD + "0";
        for (i = 0; i < y; i++)
            BC = BC + "0";

到这里,我们就完成了所有公式中数字的准备

接下来将这些字符串类型的数字加起来即可

我们先准备一个字符串相加函数如下

public static String add(String X, String Y) {//字符串相加函数,可以将两个非负任意位整数相加
        int i = X.length() - 1;
        int j = Y.length() - 1;
        int add = 0;
        String sum = "";
        while (i >= 0 || j >= 0 || add != 0) {
            if (i >= 0)
                add = add + X.charAt(i--) - '0';
            if (j >= 0)
                add = add + Y.charAt(j--) - '0';

            sum = sum + String.valueOf(add % 10);
            add = add / 10;
        }
        StringBuilder sb = new StringBuilder(sum);

        return sb.reverse().toString();
    }

然后通过该函数,对我们已经得到的字符串进行相加

//字符串相加
        String sum = "";
        sum = add(AC, AD);
        sum = add(sum, BC);
        sum = add(sum, BD);

最后返回sum的值即可

但是!!!还没有结束


思路补充

我们只是实现了程序的大体框架

当我们不断嵌套调用该函数求AC, AD, BC, BD值时,什么时候结束?

答案是不会正常结束

当我们截取到最后必然出现空字符串,这时候就会出现问题,我们需要找到让它结束的条件

或者说创造一个让它结束嵌套调用的条件

最简单的方式就是当其中一个字符串长度小于某个值时结束,而此时另一个值长度不一定

比如一开始输入两个位数相差很大的数,还是会出错

不知道上面的表达是否清楚,没关系,我们直接看代码

if ((x <= 1 || y <= 1) && (x + y < 5)) {

            int n = Integer.parseInt(X);
            int m = Integer.parseInt(Y);
            return String.valueOf(n * m);//当两数长度小时,直接相乘,也是为了后面递归操作可以停下来
        }

这里表示当整数长度足够小,且是两个整数都比较小时,直接利用相乘来得到积即可

但是还不够,我们说当两个传入的整数相差过大时,在递归操作中可能传入空字符串

这时候程序运行不到这里就出错了

而一个空字符串乘以另一个值,就肯定是对于0,所以我们直接返回0

if (X.isEmpty() || Y.isEmpty())
            return "0";//判断字符串是否为空,当两数位数差异大时,在递归操作会出现空字符串,此时需要返回0

到这里程序就可以正常运行了

包括任意大的正整数乘以任意大的整数或乘以0、两个相差很大的正整数相乘

没错,是正整数

因为这里我们没有考虑到负数的情况,如果此时使用负数进去还是会出错

如何解决?

其实很简单,我们只需要把负号去掉,用两个数的绝对值计算,最后判断它是正数还是负数,若是负数再给它加上负号即可

if (judge % 2 != 0)//当judge=0,两数为正;当judge=1,一正一副;judge=2,两数为负
            sum = "-" + sum;

完整代码

package TestProblem;

import java.util.Scanner;

public class LargeInt {
    static int judge = 0;

    public static void main(String[] args) {
        String X, Y;
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个整数");
        X = sc.next();
        System.out.println("请输入第二个整数");
        Y = sc.next();//从系统输入获取字符串XY
        String sum = "";//准备一个空字符串,接收结果,这里是为了与负数相乘方便修改符号
        sum = largeInt(X, Y);//得到两数积的绝对值
        if (judge % 2 != 0)//当judge=0,两数为正;当judge=1,一正一副;judge=2,两数为负
            sum = "-" + sum;
        System.out.println(X + " × " + Y + " \n= " + sum);
    }

    public static String largeInt(String X, String Y) {
        if (X.isEmpty() || Y.isEmpty())
            return "0";//判断字符串是否为空,当两数位数差异大时,在递归操作会出现空字符串,此时需要返回0
        if (X.charAt(0) == '-') {
            X = X.substring(1);
            judge++;
        }
        if (Y.charAt(0) == '-') {
            Y = Y.substring(1);
            judge++;
        }//当出现负号,将负号去掉并记录下来
        int x = X.length();
        int y = Y.length();
        if ((x <= 1 || y <= 1) && (x + y < 5)) {

            int n = Integer.parseInt(X);
            int m = Integer.parseInt(Y);
            return String.valueOf(n * m);//当两数长度小时,直接相乘,也是为了后面递归操作可以停下来
        }
        String A = X.substring(0, x - x / 2);//分别截取数字为AB*CD
        String B = X.substring(x - x / 2);
        String C = Y.substring(0, y - y / 2);
        String D = Y.substring(y - y / 2);
        //AC*10^(x/2+y/2)+(AD*10^(x/2)+BC*10^(y/2))+BD
        String AC = largeInt(A, C);//按照上面公式,计算每一组乘数,递归实现
        String BD = largeInt(B, D);
        String AD = largeInt(A, D);
        String BC = largeInt(B, C);

        int xy0 = x / 2 + y / 2;//记录下乘以10的几次方,即在后方补多少0
        int x0 = x / 2;
        int y0 = y / 2;

        int i;
        for (i = 0; i < xy0; i++)
            AC = AC + "0";
        for (i = 0; i < x0; i++)
            AD = AD + "0";
        for (i = 0; i < y0; i++)
            BC = BC + "0";
        //到这里,字符串已经完整,接下来

        //字符串相加
        String sum = "";
        sum = add(AC, AD);
        sum = add(sum, BC);
        sum = add(sum, BD);

        return sum;

    }

    public static String add(String X, String Y) {//字符串相加函数,可以将两个非负任意位整数相加
        int i = X.length() - 1;
        int j = Y.length() - 1;
        int add = 0;
        String sum = "";
        while (i >= 0 || j >= 0 || add != 0) {
            if (i >= 0)
                add = add + X.charAt(i--) - '0';
            if (j >= 0)
                add = add + Y.charAt(j--) - '0';

            sum = sum + String.valueOf(add % 10);
            add = add / 10;
        }
        StringBuilder sb = new StringBuilder(sum);

        return sb.reverse().toString();
    }
}

声明

本文为作者个人经验分享,不一定完全正确

本文是作者第一篇博客内容,可能存在比较多缺陷,欢迎大家指正

本文为作者原创学习笔记,期待与大家一起进步

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值