目录
简介
大整数乘法是一种用于计算两个非常大的整数相乘的方法,通常超出了计算机硬件所能直接处理的范围。在这种情况下,我们需要使用一些特殊的算法来处理这类问题。
本文将详细介绍大整数乘法的分治算法
算法思路
将两个大整数A、B进行拆分
其中 `A1` 和 `B1` 代表 `A` 和 `B` 的高位部分,`A0` 和 `B0` 代表低位部分,`n` 是整数的位数。
对公式化简可以得到
在这里我们其实需要计算四个部分的乘积,而为了减少时间复杂度,一般会对部分进一步化简,得到一个更快的算法,即
按照以上思路,即可设计出大整数乘法的整个程序。
但是这个算法存在一个不足之处,就在于A、B两个数字的位数必须要相同,当位数不同时无法使用该方法,故本文将对该算法进一步改进,让其在基本大整数乘法的基础上得到优化。
改进思路
为了让算法适用范围更广,必然需要考虑到两个大整数位数不同的情况,
假设
其中B有x位数字,D有y位数字
则存在
根据该公式即可编写程序实现优化后的大整数乘法
具体实现思路
定义大整数乘法方法,为了让整数可以取到特别大,需要用字符串来接收和操作
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();
}
}
声明
本文为作者个人经验分享,不一定完全正确
本文是作者第一篇博客内容,可能存在比较多缺陷,欢迎大家指正
本文为作者原创学习笔记,期待与大家一起进步