大整数,顾名思义就是特别大的整数。

一台64位的机器最大能表示的数字是2的64次方减一:

18446744073709551615

java语言中所能表示的整数(int)最小为-2147483648

public class test {
public static void main(String[] args) {
System.out.println(Integer.MIN_VALUE);
}
}

最大为

2147483647

public class test {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
}
}

而long所能表示的最大整数为

public class test {
public static void main(String[] args) {
System.out.println(Long.MAX_VALUE);
} }

9223372036854775807

最小为

public class test {

    public static void main(String[] args) {
System.out.println(Long.MIN_VALUE);
} }

-9223372036854775808

如果超出了这些范围,就会报 out of range的错,所以java提供了BigInteger这个类来进行大整数的运算。其实实现BigInteger的原理也并不困难。下面是加法,减法已经乘法的实现。

类里面需要一个bignum属性

public class MyBigInteger {
  String bignum;
  public MyBigInteger(String bigone) {
       this.bignum = bigone;
}

加法:根据老师课上得指导,大概思路很简单,就是把大整数分成好几个段(每四个数字一段),然后对应的数字段进行加法。

例如12345678987654321与8765432123456789相加。那么将这两个数字分成

1   2345  6789  8765  4321

8765  4321  2345  6789   对应的和则为

2 1111   1111    1111  1110

(分组的主要思路是从后往前,每四个数字一组,到最前面的一组若不够四位数,则自成一组)

// 分块,在加法和减法里面用到
private String[] getGroup(String number) {
int length = (int) Math.ceil(number.length() / 4.0);
String[] group = new String[length];
for (int i = length; i > 0; i--) {// 注意,substring是从前往后截取字符,不是,从后往前截取字符
if (number.length() - (length - i) * 4 - 4 < 0) {
group[length - i] = number.substring(0, number.length() - (length - i) * 4);
} else {
group[length - i] = number.substring(number.length() - (length - i) * 4 - 4,
number.length() - (length - i) * 4);
}
}
return group;
}

其中,需要考虑到块与块之间的进位问题。因为每个对应的块相加,可以使用Integer.parseInt(...)转化成数字后相加,但是相应的块相加后可能会超出9999,这样就要向前面的块进位。这就要设置一个属性boolean[] fillThousand 。来判断是否需要进位,这个数组的长度应该比两个数分块后较长的那个块的长度多一(如上面两个数字分块后,块的长度分别是5和4,所以fillThousand的长度应该是6.)

如果,某一个对应块想加大于9999,那就把这个数减去10000(或者转化为字符串后从第二个字符开始截取),并且把前一个块的fillThousand属性变成true。

如果某一个块对于的fillThousand属性为true,则把这个对应块的和加1.。如果,最前面的那个块的fillThousand属性为true(这就是为什么把ffillThousand长度设置为6),则将总结果前面加一个”1“。

(此处,我是将长度较长的数当作第一个数字,注意,只是长度,在减法里,我会将绝对值较大的数当成第一个数)

private String addPN(String bigtwo) {
// 暂时无考虑负数
int a1 = (int) Math.ceil(bignum.length() / 4.0);
int a2 = (int) Math.ceil(bigtwo.length() / 4.0);
String[] group1 = new String[a1];
String[] group2 = new String[a2]; group1 = getGroup(bignum);
group2 = getGroup(bigtwo); if (bigtwo.length() > bignum.length()) {// 将长的放到a1上
String[] temp = group1;
group1 = group2;
group2 = temp;
a1 = group1.length;
a2 = group2.length;
}
String addAll = "";
boolean[] fillThousand = new boolean[a1 + 1];// 每一块数字是否需要进位
for (int i = 0; i < a1; i++) {
if (i <= a2 - 1) {
Integer i1 = Integer.parseInt(group1[i]);
Integer i2 = Integer.parseInt(group2[i]);
Integer iall = i1 + i2;
if (fillThousand[i]) {
iall += 1;
}
if (iall > 9999 && i != a2 - 1) {
// iall=Integer.parseInt(iall.toString().substring(1,5));如是10000,则此方法不行。。0000=0
String subIall = iall.toString().substring(1, 5);
iall = Integer.parseInt(subIall);
fillThousand[i + 1] = true;
}
if (iall.toString().length() == 3 && i != a2 - 1) {
addAll = "0" + iall + addAll;
} else if (iall.toString().length() == 2 && i != a2 - 1) {
addAll = "00" + iall + addAll;
} else if (iall.toString().length() == 4 && i != a2 - 1) {
addAll = iall + addAll;
} else if (iall.toString().length() == 1 && i != a2 - 1) {
addAll = "000" + iall + addAll;
}
if (i == a2 - 1) {
if (iall > 9999) {
// iall=Integer.parseInt(iall.toString().substring(1,5));如是10000,则此方法不行。。0000=0
String subIall = iall.toString().substring(1, 5);
iall = Integer.parseInt(subIall);
fillThousand[i + 1] = true;
}
if (iall.toString().length() == 3) {
addAll = "0" + iall + addAll;
} else if (iall.toString().length() == 2) {
addAll = "00" + iall + addAll;
} else if (iall.toString().length() == 1) {
addAll = "000" + iall + addAll;// 保证最前面的数字不会多加0
} else if (iall.toString().length() == 4) {
addAll = iall + addAll;
} else if (a1 == a2) {
addAll = iall + addAll;
}
}
// 如果最前面的一块相加超过1000
if (fillThousand[a1]) {
addAll = "1" + addAll;
}
} else {
Integer iall = Integer.parseInt(group1[i]);
if (fillThousand[i]) {
iall += 1;
}
if (i == a1 - 1) {
addAll = iall + addAll;
}
if (iall.toString().length() == 3 && i != a1 - 1) {
addAll = "0" + iall + addAll;
} else if (iall.toString().length() == 2 && i != a1 - 1) {
addAll = "00" + iall + addAll;
} else if (iall.toString().length() == 1 && i != a1 - 1) {// iall是否超过1000
addAll = "000" + iall + addAll;// 保证最前面的数字不会多加0
} else if (iall.toString().length() == 1 && iall == 0 && i == a1 - 1) {
addAll = addAll;// 保证最前面的数字不会多加0
} else if (iall.toString().length() == 4 && i != a1 - 1) {
addAll = iall + addAll;
} // 如果最前面的一块相加超过1000
if (fillThousand[a1]) {
addAll = "1" + addAll;
}
}
}
// 若不进行此步,则会出现000000000001这样的情况
if (addAll != "") {
if (addAll.charAt(0) == '0') {
int a = 0;
while (Integer.parseInt(addAll.substring(a, a + 1)) == 0) {
a += 1;
if (a == addAll.length()) {
break;
}
}
addAll = addAll.substring(a, addAll.length());
if (a == addAll.length()) {
addAll = "0";
}
}
} else {
addAll = "0";
}
return addAll;
}

  

以上是,两个正整数进行加法运算时的代码,当然加法里面还会有负数相加。所以结合后来的,两个正整数的减法。可以得出加法的完整算法

public MyBigInteger add(MyBigInteger bigtwo) {

String returnNum=null;

if (!(this.bignum.charAt(0) == '-') && !(bigtwo.bignum.charAt(0) == '-')) {
returnNum = addPN(bigtwo.bignum);
} else if (this.bignum.charAt(0) == '-' && !(bigtwo.bignum.charAt(0) == '-')) {
bignum = bignum.substring(1, bignum.length());
if (substractPN(bigtwo.bignum).charAt(0) == '-') {
returnNum = substractPN(bigtwo.bignum).substring(1, substractPN(bigtwo.bignum).length());
} else {
returnNum = "-" + substractPN(bigtwo.bignum);
}
} else if (!(this.bignum.charAt(0) == '-') && bigtwo.bignum.charAt(0) == '-') {
bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length());
returnNum = substractPN(bigtwo.bignum);
} else// 两个都是负数
{
bignum = bignum.substring(1, bignum.length());
bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length());
returnNum = "-" + addPN(bigtwo.bignum);
}
return new MyBigInteger(returnNum);
}

  

减法:

减法的算法比加法的算法要复杂一些,因为减法不仅要考虑借位还要考虑正负。

还是先进行两个整数的减法运算

首先还是分组.......

然后将大的数当成被减数

减法里面要设置两个大属性,一个是boolean reverse,这个意味着,如果是第一个数字小于第二个数字,那就交换两个的位置,然后在结果前面加上”-“。

private String substractPN(String bigtwo) {
// 暂时无考虑负数
int a1 = (int) Math.ceil(bignum.length() / 4.0);
int a2 = (int) Math.ceil(bigtwo.length() / 4.0);
String[] group1 = new String[a1];
String[] group2 = new String[a2];
group1 = getGroup(bignum);
group2 = getGroup(bigtwo);
boolean reverse = false;// 判断是否是一个小数字减去一个大数字,若是,则交换两个的位置,并在最后加一个负号 boolean oneMoreTwo = true;// 位数相同,比较大小
if (bigtwo.length() == bignum.length()) {
// 如果两个数长度相等,比较前两段数字的大小
if (Integer.parseInt(group2[a1 - 1]) > Integer.parseInt(group1[a1 - 1])) {
oneMoreTwo = false;
}
if ((Integer.parseInt(group2[a1 - 1]) == Integer.parseInt(group1[a1 - 1]))
&& (Integer.parseInt(group2[a1 - 2]) > Integer.parseInt(group1[a1 - 2]))) {
oneMoreTwo = false;
}
}
if (bigtwo.length() > bignum.length() || !oneMoreTwo) {// 将长的数放到a1上
String[] temp = group1;
group1 = group2;
group2 = temp;
a1 = group1.length;
a2 = group2.length;
reverse = true;
} //。。。。。。。。。。。。
//。。。。。。。。。。。。
//。。。。。。。。。。。。 //这是最后的两行,中间省略若干代码
if (reverse) {
substractAll = "-" + substractAll;
}

  

另外一个boolean[] borrowone,是否借位,和加法的fillThousand属性类似。如果一个对应块相减后结果小于0,那就把这个结果加10000,然后相前一个块借1。然后将前一个块的borrowone设置为true。

若一个块的borrowone属性为true则将这个块的结果减1.

boolean[] borrowone = new boolean[a1 + 1];// 判断是否需要借位
String substractAll = "";
for (int i = 0; i < a1; i++) {
if (i <= a2 - 1) {
Integer i1 = Integer.parseInt(group1[i]);
Integer i2 = Integer.parseInt(group2[i]);
Integer isubstract = i1 - i2;// 处理isubstract是0000的情况
if (borrowone[i]) {
isubstract -= 1;
}
if (isubstract < 0) {
isubstract = isubstract + 10000;// 判断位数
borrowone[i + 1] = true;
if (isubstract > 0 && isubstract.toString().length() == 3) {
substractAll = "0" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 2) {
substractAll = "00" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 1) {
substractAll = "000" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 4) {
substractAll = isubstract + substractAll;
} else if (isubstract == 0) {
substractAll = "0000" + substractAll;
}
} else if (isubstract > 0 && isubstract.toString().length() == 3) {
substractAll = "0" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 2) {
substractAll = "00" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 1) {
substractAll = "000" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 4) {
substractAll = isubstract + substractAll;
} else if (isubstract == 0) {
substractAll = "0000" + substractAll;
}
} else {
Integer isubstract = Integer.parseInt(group1[i]);
if (borrowone[i]) {
isubstract -= 1;
}
if (i == a1 - 1) {
substractAll = isubstract + substractAll;
}
if (isubstract > 0 && isubstract.toString().length() == 3 && i != a1 - 1) {
substractAll = "0" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 2 && i != a1 - 1) {
substractAll = "00" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 1 && i != a1 - 1) {
substractAll = "000" + isubstract + substractAll;
} else if (isubstract > 0 && isubstract.toString().length() == 4 && i != a1 - 1) {
substractAll = isubstract + substractAll;
} else if (isubstract == 0) {
substractAll = "0000" + substractAll;
}
}
}

  

当然,减法,还要处理一下000001这类情况

// 若不进行此步,则会出现000000000001这样的情况

if (Integer.parseInt(substractAll.substring(0, 1)) == 0) {
int a = 0;
while (Integer.parseInt(substractAll.substring(a, a + 1)) == 0) {
a += 1;
}
substractAll = substractAll.substring(a, substractAll.length());
}

以上是正数减法的实现,结合加法,则可以实现完整的减法

public MyBigInteger substract(MyBigInteger bigtwo) {
// 只能用equals不能用== String returnNum=null; if (!(this.bignum.charAt(0) == '-') && !(bigtwo.bignum.charAt(0) == '-')) { returnNum = substractPN(bigtwo.bignum);
} else if (this.bignum.charAt(0) == '-' && !(bigtwo.bignum.charAt(0) == '-')) {
bignum = bignum.substring(1, bignum.length());
returnNum = "-" + addPN(bigtwo.bignum); } else if (!(this.bignum.charAt(0) == '-') && bigtwo.bignum.charAt(0) == '-') {
bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length());
returnNum = addPN(bigtwo.bignum);
} else {// 两个都是负数
bignum = bignum.substring(1, bignum.length());
bigtwo.bignum = bigtwo.bignum.substring(1, bigtwo.bignum.length());
if (substractPN(bigtwo.bignum).charAt(0) == '-') { returnNum = substractPN(bigtwo.bignum).substring(1, substractPN(bigtwo.bignum).length());
} else {
returnNum = "-" + substractPN(bigtwo.bignum);
} } return new MyBigInteger(returnNum);
}

乘法:

乘法的算法思想比较简单。我采用的逐位相乘的思路,即两个数下标和相同的数字相乘之后相加。然后最后的和作为结果对对应的位数。即∑Ai(A的i位)*Bz-i(B的z-i位) = Cz

若Cz大于十,则保留其个位数,并且向Cz+1进位,进的位数为Cz十位以上的数字,例如Cz=123,则向前进十二位。

	public MyBigInteger multiply(MyBigInteger bigtwo) {
String returnNum=null;
boolean positive = false;
if ((bigtwo.bignum.charAt(0) == '-' && this.bignum.charAt(0) == '-')
|| (!(bigtwo.bignum.charAt(0) == '-') && !(this.bignum.charAt(0) == '-'))) {
positive = true;
}
if (bigtwo.bignum.charAt(0) == '-') {
bigtwo.bignum = bigtwo.bignum.substring(1);
}
if (this.bignum.charAt(0) == '-') {
this.bignum =this.bignum.substring(1);
}
int a = this.bignum.length();
int b = bigtwo.bignum.length();
String[] s1 = new String[a];
String[] s2 = new String[b];
int[] mulAll = new int[a + b - 1];
for (int i = 0; i < a; i++) {
s1[a - i - 1] = this.bignum.substring(i, i + 1);
}
for (int i = 0; i < b; i++) {
s2[b - i - 1] = bigtwo.bignum.substring(i, i + 1);
}
if (a < b) {
int temp = a;
a = b;
b = temp;
String[] stemp = s1;
s1 = s2;
s2 = stemp;
}
for (int i = 0; i < a; i++) {
for (int j = 0; j < b; j++) {
mulAll[i + j] +=Integer.parseInt(s1[i]) * Integer.parseInt(s2[j]);
}
}
for (int i = 0; i < mulAll.length - 1; i++) {
if (mulAll[i] > 9) {
while (mulAll[i] > 9) {
mulAll[i] -=10;
mulAll[i + 1] += 1;
}
}
}
returnNum = "";
for (int i = mulAll.length - 1; i >= 0; i--) {
returnNum = returnNum + mulAll[i];
}
if (positive) {
return new MyBigInteger2(returnNum);
} else {
returnNum = "-" + returnNum;
return new MyBigInteger(returnNum);
}
}

  小弟不才,以上是本人根据大整数的处理思路写出来的代码,其中需要优化的地方很多,需要不断修改。

基于Java的大整数运算的实现(加法,减法,乘法)学习笔记的更多相关文章

  1. 九度OJ 1037:Powerful Calculator(强大的计算器) (大整数运算)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1821 解决:528 题目描述: Today, facing the rapid development of business, SJTU ...

  2. 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承

    <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...

  3. 用Java的大整数类BigInteger来实现大整数的一些运算

    关于BigInteger的构造函数,一般会用到两个: BigInteger(String val); //将指定字符串转换为十进制表示形式: BigInteger(String val,int rad ...

  4. Java 实现大整数加减乘除

    自己用Java实现的大整数加减乘除运算.还有可以改进的地方,有兴趣的童鞋可以加以改进.仅供参考,请勿转载! package barrytest; import java.util.ArrayList; ...

  5. C语言课程设计大整数运算

    该大整数运算系统用于对有符号的位数不超过500位的大整数进行加.减.乘.除四则运算和计算N(0<=N<=10000)的阶乘.注意事项 :    1.操作期间,进行四则运算时若大整数为正数请 ...

  6. Java实现大整数乘法

    1 问题描述 计算两个大整数相乘的结果. 2 解决方案 2.1 蛮力法 package com.liuzhen.chapter5; import java.math.BigInteger; publi ...

  7. A1136 | 字符串处理、大整数运算

    题目链接: https://www.patest.cn/contests/pat-a-practise/1136 今天是12月17号.最近这几天都有点不在状态.已经整整一周没有练算法了,自从12.3考 ...

  8. 【Weiss】【第03章】练习3.9:大整数运算包

    [练习3.9] 编写任意精度的整数运算包,要求使用类似多项式运算的方法.计算24000内数字0到9的分布.

  9. 《基于 Java EE 的高校重修管理系统设计与实现》论文笔记(九)

    标题:基于 Java EE 的高校重修管理系统设计与实现 一.基本信息 时间:2015 来源:河海大学文天学院 关键词::Java EE 架构: B/S 模式: 重修管理系统 二.研究内容 1.需求分 ...

随机推荐

  1. linux软件操作

    操作 命令 ubuntu 源操作 源配置 https://www.cnblogs.com/wenlin-gk/p/11146228.html 源更新 sudo apt-get update 查看源中包 ...

  2. 如何解决MSVCR120.dll在Windows上缺少错误?

    在安装MySQL的时候,执行mysqld的时候,提示msvcr120.dll,是因为计算机中丢失了 一些文件 然后点击我下载完成安装就好了, 备注:两个都得安装!

  3. [原创]PHP代码修正之CodeSniffer

    目录 参考链接 介绍 安装 使用 命令行模式 PHPStorm 让编辑器使用PSR-2标准 集成phpcbf 参考链接 PHP开发规范之使用phpcbf脚本自动修正代码格式 在PhpStorm中使用P ...

  4. NOIP模拟赛(by hzwer) T1 小奇挖矿

    [题目背景] 小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值 w)的飞船,按既定 路线依次飞过喵星系的 n 个星球. [问题描述] 星球分为 2 类:资源型和维修型. 1. 资源型:含矿物质量 ...

  5. Linux内核设计与实现 总结笔记(第十六章)页高速缓存和页回写

    页高速缓存是Linux内核实现磁盘缓存.磁盘告诉缓存重要源自:第一,访问磁盘的速度要远远低于访问内存. 第二,数据一旦被访问,就很有可能在短期内再次被访问到.这种短时期内集中访问同一片数据的原理称作临 ...

  6. ajax +formdata ,后台为PHP 实现上传整个文件夹(只适合谷歌浏览器)带进度条

    PHP用超级全局变量数组$_FILES来记录文件上传相关信息的. 1.file_uploads=on/off 是否允许通过http方式上传文件 2.max_execution_time=30 允许脚本 ...

  7. Java——容器(Interator)

    [Interator接口]   <1> 所有实现了Collection接口的容器类都有一个interator方法用以返回一个实现了Interaor接口的对象. <2> Inte ...

  8. Java中高级面试题(1)

    List和Set比较,各自的子类比较 对比一:Arraylist与LinkedList的比较 1.ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高 ...

  9. android 后台运行service实现和后台的持续交互

    在项目中有这么一种需求 需要后台开启服务,时刻记录用户和软件的交互行为,一旦交互发生,就向服务器测发送一条消息 解决方案: 一.创建一个service服务类 在service中开启一个线程,servi ...

  10. 原生javascript兼容性总结

    1.addEventListener() :方法用于向指定元素添加事件句柄.// Internet Explorer 8 及更早IE版本不支持,Opera 7.0 及 Opera 更早版本也不支持. ...