大致思路
在计算大数字的时候可以使用数组来储存数字,然后通过数组操作来完成数学运算。
大数加法:数组对位运算,将大数储存到数组中之后每一位做简单的加法运算,思路很简单
大数乘法:我们可以将超大整数转化若干的小操作。
例如:
a
b
c
d
e
×
p
q
r
s
t
=
a
b
c
d
e
×
p
×
10000
+
a
b
c
d
e
×
q
×
1000
+
a
b
c
d
e
×
r
×
100
+
a
b
c
d
e
×
s
×
10
+
a
b
c
d
e
×
t
×
1
\begin{aligned} abcde \times pqrst & = abcde \times p \times 10000 \\ & +abcde \times q \times 1000 \\ & +abcde \times r \times 100 \\ & +abcde \times s \times 10 \\ & +abcde \times t \times 1 \end{aligned}
abcde×pqrst=abcde×p×10000+abcde×q×1000+abcde×r×100+abcde×s×10+abcde×t×1上面这样子就可以把大数乘法划分成较小的数值运算。
我们将其划分成一个大数与一个个位数相乘,再乘以一个10的幂次;
大数与仅有个位的数相乘的思路,在代码实现上和相加大同小异;
至于乘以10的运算,则可以通过数组位移实现,稍后会讲;
最后,而每一个子项都可以进行一次加法运算,如:
a
b
c
d
e
×
p
×
10000
+
a
b
c
d
e
×
q
×
1000
abcde \times p \times 10000+abcde \times q \times 1000
abcde×p×10000+abcde×q×1000, 可以视为两个大数的加法。
借助数组存储大数数值
考虑到位数和权重是等价的,第一位(个位)的权重为0(即100),第二位(十位)的权重为1(即101),所以在大数储存中为了保留权重信息,我们应该将数字倒序存储
比如:存储数字
12345
12345
12345
因为:
12345
=
1
×
1
0
4
+
2
×
1
0
3
+
3
×
1
0
2
+
4
×
1
0
1
+
5
×
1
0
0
12345 = 1 \times 10^4 + 2 \times 10^3 + 3 \times 10^2 + 4 \times 10^1 + 5 \times 10^0
12345=1×104+2×103+3×102+4×101+5×100
所以数组应该储存为:arr=[5, 4, 3, 2, 1];
此时,arr[0] = 5
,arr[4] = 1
,下标对应指数。
在一个数乘100时,只需要将数组的数值向后移两位,前两位补零即可(因为是倒序存储所以不能从后面补零)。还是上面这个例子,如果要计算
12345
×
100
12345 \times 100
12345×100,只需要将数组前补两个0:arr=[0, 0, 5, 4, 3, 2, 1];
这样子表示的数字就是
1234500
1234500
1234500
思路到这里就差不多了,下面是实现代码,已经写了一些注释。可能在结果输出那里有点难以理解,因为会有多余的0需要检查,可以做一下debug试试。
大数类LargeNumber :
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LargeNumber {
private int[] nums;
public int[] getNums() {
return nums;
}
//私有化,不允许外部改变nums数组
private void setNums(int[] nums) {
this.nums = nums;
}
@Override
public String toString() {
int len = nums.length;
//将大数拼接成字符串输出
StringBuilder s = new StringBuilder();
//数字之前如果为0,且不只有一位数字,不输出最高位的0
//如0159,只输出159
boolean b = true;
/* 在乘法运算时发现,有时候结果数字会有很多个0,
如 10*9,运算中在exponentArithmetic方法会算出090,
在addByArray方法中会算出0090,如果只判断第一个数字是否为0,
则最终输出结果为 10*9=090,不够理想,
所以此处判断前n项是否会0序列串,
如果前n想均为0,则不输出,只输出数字串,如000010230 输出 10230
*/
for (int i = len - 1; i >= 0; i--) {
if (nums[i] == 0 && b) {
continue;
} else if (nums[i] != 0) {
b = false;
}
s.append(nums[i]);
}
String result = s.toString();
if (result.equals("")) {
return "0";
}
return result;
}
private LargeNumber() {
}
private LargeNumber(String num) {
try {
nums = setNumArray(num);
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
/** 通过数字串创建 */
public static LargeNumber getLargeNumber(String n) {
return new LargeNumber(n);
}
/** 通过数组创建 */
public static LargeNumber getLargeNumber(int[] a) {
LargeNumber largeNumber = new LargeNumber();
largeNumber.setNums(a);
return largeNumber;
}
/** 将字符串转为int数组 */
private int[] setNumArray(String n) throws NumberFormatException {
//异常判断,只允许输入纯数字串
String pattern = "^[1-9][0-9]*$";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(n);
if (!m.find()) {
if (!n.equals("0")) {
throw new NumberFormatException("请输入纯数字串");
}
}
int[] nums;
int len = n.length();
nums = new int[len];
//大数数组倒序赋值,下标即位数权重。
//比如 321 = 1*10^0 + 2*10^1 + 3*10^2
for (int i = 0; i < len; i++) {
char c = n.charAt(i);
//将字符转为数字存入数组
nums[len - i - 1] = Integer.parseInt(String.valueOf(c));
}
return nums;
}
/** 大数加法 */
public LargeNumber add(LargeNumber ln) {
//获取两个大数数组来相加
int[] nums1 = this.getNums();
int[] nums2 = ln.getNums();
//通过方法将两个大数数组相加
//再通过数组创建大数类型,并返回
return LargeNumber.getLargeNumber(addByArray(nums1, nums2));
}
/** 将两个大数数组相加 */
private int[] addByArray(int[] nums1, int[] nums2) {
/*
获取较长的数组,并将两个数组的长度设为相同
避免后续for循环超出数组长度
*/
int maxLen;
if (nums1.length > nums2.length) {
maxLen = nums1.length;
nums2 = Arrays.copyOf(nums2, nums1.length);
} else {
maxLen = nums2.length;
nums1 = Arrays.copyOf(nums1, nums2.length);
}
//以较长的数组长度+1来初始化结果数组
//因为加法的最大结果会大一位数
int[] result = new int[maxLen + 1];
//进位单元
int temp = 0;
for (int i = 0; i < maxLen; i++) {
int add = nums1[i] + nums2[i];//对于单位相加
int tens = add / 10;//获取十位数 (进位单元)
int single = add % 10;//获取个位数
//将个位数和上个循环的十位数添加到结果数组
int res = single + temp;
//如果它们的和大于10,只添加个位
//十位数再次添加到进位单元
int tens2 = res / 10;
int single2 = res % 10;
result[i] = single2;
//十位数在下个循环添加
//如果该次循环没有进位单元,则赋值结果为0
temp = tens + tens2;
}
//将最后一个进位单元添加,如果最后没有进位单元,则为0,不影响结果
result[maxLen] = temp;
return result;
}
/**
* 大数乘法
* @param ln
* @return
*/
public LargeNumber multiply(LargeNumber ln) {
//获取两个大数数组来相加
int[] nums1 = this.getNums();
int[] nums2 = ln.getNums();
// 获取较长的数组,并将两个数组的长度设为相同
// 避免后续for循环超出数组长度
int maxLen;
if (nums1.length > nums2.length) {
maxLen = nums1.length;
} else {
maxLen = nums2.length;
//保证nums1数字更大,方便后续乘法运算计算位数
int[] nums3 = nums1;
nums1 = nums2;
nums2 = nums3;
}
int[] prev = null;
for (int i = 0; i < maxLen; i++) {
//取较大数nums1中的一位数,与nums2相乘,获取结果数组
int[] temp = multipyWithSingle(nums2, nums1[i]);
//算上权重
temp = exponentArithmetic(temp, i);
if (prev != null) {
//把这一次的结果和上一次的结果加起来
prev = addByArray(temp, prev);
} else {
prev = temp;
}
}
//通过数组创建大数类型,并返回
return LargeNumber.getLargeNumber(prev);
}
/**
* @param nums 被乘大数数组
* @param singleNum 将大数数组扩大singleNum倍
*/
private int[] multipyWithSingle(int[] nums, int singleNum) {
assert singleNum / 10 == 0;
int[] result = new int[nums.length + 1];
//进位单元
int temp = 0;
for (int i = 0; i < nums.length; i++) {
int multiply = nums[i] * singleNum;
//获取十位数 (进位单元)
int tens = multiply / 10;
//获取个位数
int single = multiply % 10;
//将个位数和上个循环的十位数添加到结果数组
int res = single + temp;
//如果它们的和大于10,只添加个位
//十位数再次添加到进位单元
int tens2 = res / 10;
int single2 = res % 10;
result[i] = single2;
//十位数在下个循环添加
//如果该次循环没有进位单元,则将被设为0
temp = tens + tens2;
}
result[nums.length] = temp;
return result;
}
/** 指数运算 */
private int[] exponentArithmetic(int[] nums, int exponent) {
int[] result = new int[nums.length + exponent];
System.arraycopy(nums, 0, result, exponent, nums.length);
return result;
}
private String outputLargeNumberArray(int[] nums) {
return LargeNumber.getLargeNumber(nums).toString();
}
}
测试类LargeNumberCalculate :
import java.util.Scanner;
public class LargeNumberCalculate {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("测试:1234566*89 = " + LargeNumber.getLargeNumber("1234567").multiply(LargeNumber.getLargeNumber("89")));
while (sc.hasNext()) {
String n1 = sc.next();
String n2;
if (n1.contains("+")) {
/* 有“+”号,直接计算运算公式,方便调试,
通过+分割。正则表达式中 + 需要使用\\转义分开 */
n2 = n1.split("\\+")[1];
n1 = n1.split("\\+")[0];
LargeNumber ln1 = LargeNumber.getLargeNumber(n1);
LargeNumber ln2 = LargeNumber.getLargeNumber(n2);
LargeNumber add = ln1.add(ln2);
System.out.println(ln1 + " + " + ln2 + " = " + add);
} else if (n1.contains("*")) {
n2 = n1.split("\\*")[1];
n1 = n1.split("\\*")[0];
LargeNumber ln1 = LargeNumber.getLargeNumber(n1);
LargeNumber ln2 = LargeNumber.getLargeNumber(n2);
LargeNumber add = ln1.multiply(ln2);
System.out.println(ln1 + " * " + ln2 + " = " + add);
}
}
}
}