前言:
目前计算机中数据存储最大为64位,对超过64位的数进行运算就会导致运算结果错误。所以针对大整数进行四则运算是十分必要的。这也是面试中非常经典的问题,所以这次我们特意整理了关于大整数的四则运算。所谓的大整数的运算其实就是模拟了人脑进行数字计算用到的规则。
1.大整数的存储
由于后续很多运算都是从低位开始,所以我们设计该存储方式为从低位向高位依次存放在数组。
public class bigInt{
int [] data=null;//存放数据(从低位到高位依次存放)
int symbol=1;//1代表整数,0代表负数
}
2.判断大整数大小
大整数的绝对值大小判断在后面将大量的应用到,后续运算都是按照第一操作数大于第二操作数(无符号数)。java中提供了comparable接口比较大小,所以我们实现接口即可。
3.大整数类设计与实现
class bigInt implements Comparable<bigInt>{
int [] data=null;//存放数据(逆序放置)
int symbol=1;//1代表整数,0代表负数
public bigInt(){
}
public bigInt(String value){
examSymbol(value);
}
public void examSymbol(String value){//检测符号问题
int start=0;//数据的开始位置
char head=value.charAt(0);
if(head=='+'||head=='-'){
start=1;
if(head=='-')
symbol=-1;
}
int len=value.length()-start;
data=new int[len];//数据长度为Len
for(;start<value.length();start++){
data[--len]=value.charAt(start)-'0';
}
}
@Override
public int compareTo(bigInt b1) {//比较的是绝对值
//找到最高位的第一个非0开始
int a_max=findMax(this.data);
int b_max=findMax(b1.data);
if(a_max!=b_max)
return a_max>b_max?1:-1;
//从高位开始比较
int c1,c2;
for(int k=a_max;k>=0;k--){
c1=this.data[k];
c2=b1.data[k];
if (c1!=c2)
return c1>c2?1:-1;
}
return 0;
}
#大整数中有效数字位,最高位不为0
public int findMax(int []data){
int m=data.length-1;
for(;m>=0;m--){
if(data[m]!=0)
return m;
}
return 0;
}
@Override
public String toString() {
char symbol='+';
if (symbol==-1)
symbol='-';
StringBuilder sb=new StringBuilder();
sb.append(symbol);
int k=findMax(data);//找到第一个不为0的高位
for(;k>=0;k--){//从高位到低位依次输出
sb.append(data[k]);
}
return sb.toString();
}
4.大整数同号相加
思路:假设大整数为a1,a2,默认|a1|>|a2|,先比较大小,若|a1|<|a2|,则交换2个操作数顺序,始终保持第一操作数大于第二操作数。
加法规则:分别从2个数的低位开始进行逐位相加,并加上前一位的进位,如果相加和超过9,则记录进位数,用于下一次进位。最后,如果最高位产生进位,则在最高位前面添加一个最高位1。
public bigInt unSignAdd(bigInt b1,bigInt b2){
// 保存累加的结果
StringBuilder sb=new StringBuilder();
// if(b1.compareTo(b2)<0){//默认b1>b2
// bigInt temp=b1;
// b1=b2;
// b2=temp;
// }
int m=b1.data.length;
int n=b2.data.length;
//从低位开始相加,设置进位
int carry=0;//记录上一位的进位
int b1_v,b2_v=0;
int currentv=0;//当前位累加和
for(int i=0;i<m;i++){
b2_v=0;
b1_v=b1.data[i];
if(i<n)
b2_v=b2.data[i];
currentv=b1_v+b2_v+carry;
if(currentv>9){//当前和产生进位
carry=currentv/10;
currentv=currentv%10;
}
else
carry=0; //当前位未产生进位,则置标记
sb.insert(0,currentv);
} //end for
//如果2数相加最高位产生进位
if(carry==1){
sb.insert(0,1);
}
char symbol='+';//同号加法
if(b1.symbol==-1)
symbol='-';
sb.insert(0,symbol);
return new bigInt(sb.toString());
}
5.大整数异号相加
设a1,a2为2个大整数,且2符号相反,|a1|>=|a2|,如果|a1|=|a2|,则结果为0。如果不等,则按照下面规则进行运算。使用无符号大数-无符号小数,最后运算结果的符号有绝对值大的数的符号来决定。
假设我们计算3+(-2),由于2个操作数异号,则进行减法法则3-2=1,最后结果符号根据3来决定,所以结果为1。
所以这里异号相加需要使用大整数减法的法则。
减法规则:分别从2个数的低位开始进行逐位相减,并减去前一位的借位,如果值小于0,则向高位借一位,记录借位数值。最后结果符号由大数(绝对值)决定。
public bigInt unSignSub(bigInt b1,bigInt b2){
StringBuilder sb=new StringBuilder();
// if(b1.compareTo(b2)<0){//默认b1>b2
// bigInt temp=b1;
// b1=b2;
// b2=temp;
// }
int borrow=0;
int m=b1.data.length;
int n=b2.data.length;
int b1_v,b2_v;//当前对应位的值
int currentV=0;
for(int i=0;i<m;i++){
b1_v=b1.data[i];
b2_v=0;
if(i<n)
b2_v=b2.data[i];
currentV=b1_v-b2_v-borrow;//相减之和的当前位的值
if(currentV<0){//该位不够减
currentV=currentV+10;//向高位借
borrow=1;//借位1
}
else{
borrow=0;
}
sb.insert(0,currentV);
}
//结果符号由大数符号决定
char symbol=b1.symbol==1?'+':'-';
sb.insert(0,symbol);
return new bigInt(sb.toString());
}
6.大整数有符号相加
有符号大整数a1,a2相加的计算规则更负责些。根据a1,a2(|a1|>|a2|)是否同号指定不同的计算规则。
1)a1,a2同号,则直接使用上面的大整数无符号相加的规则。
2)a1,a2异号.则使用大整数异号规则处理。
/**
* 有符号的加法
* @return
*/
public bigInt add(bigInt b1,bigInt b2){
int c=b1.compareTo(b2);
if(c<0){ //保证大数在前,小数在后
bigInt temp=b1;
b1=b2;
b2=temp;
}
//判断是否同号
if(b1.symbol*b2.symbol==-1){//异号
if(c==0)//a1,a2的绝对值相等,则结果为0
return new bigInt("0");
return unSignSub(b1,b2);
}
else//同号
return unSignAdd(b1,b2);
}
7.大整数有符号减法
减法可以转化为加法进行运算,如3-5=3+(-5),这样就就转化为加法进行运算。即只需要将第二操作数符号反置即可。
/**
* 有符号减法
*/
public bigInt sub(bigInt b1,bigInt b2){
b2.symbol*=-1;
bigInt r=add(b1,b2);//变成加法运算
b2.symbol*=-1;//将修改的符号变过来
return r;
}
8.大整数乘法
乘法法则:模拟手动计算乘法,乘数每一位与被乘数每一相乘的结果累加到相应位上。最后再针对每一位上的结果进行进位即可。
乘法过程:
/**
* 大整数相乘
* @param a1
* @param a2
* @return
*/
public bigInt multi(bigInt b1,bigInt b2){
//大数放前面
if(b1.compareTo(b2)<0){
bigInt temp=b1;
b1=b2;
b2=temp;
}
int m=b1.data.length;
int n=b2.data.length;
int []result=new int[m+n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
result[i+j]+=b1.data[i]*b2.data[j];
}
}
//考虑进位
int carry=0;
StringBuilder sb=new StringBuilder();
for(int k=0;k<m+n;k++){
result[k]+=carry;
if(result[k]>9){
carry=result[k]/10;
result[k]=result[k]%10;
}
else
carry=0;
sb.insert(0,result[k]);
}
bigInt r=new bigInt(sb.toString());
r.symbol=b1.symbol*b2.symbol;
return r;
}
9.大整数除法
采用试商法,设被除数为d,除数为v,从高位开始取被除数的下一位与当前余数连接形成p,
规则:用试商法求p除以除数v如果p大于v,使用递减法(不断使用p=p-v)来求出p/v的商。记录当前的商和余数,以此类推,直到计算完最后一位。
除法过程:
public bigInt div(bigInt b1,bigInt b2){
int m=b1.data.length;
int n=b2.data.length;
StringBuilder result=new StringBuilder();//保存商
bigInt yu=new bigInt(); //存放余数
yu.data=new int[n+1];//余数位数为除数+1
int k=0;
for(int i=m-1;i>=0;i--){ //从高位开始
//确保余数的位数为n+1位,如果不等则进行复制
if(yu.data.length!=n+1){
int len=yu.findMax(yu.data);
int mydata[]=new int[n+1];
for(i=0;i<=len;i++){
mydata[i]=yu.data[i];
}
yu.data=mydata;
}
//将取得的数放入低位和余数构成被除数p
System.arraycopy(yu.data,0,yu.data,1,n);
yu.data[0]=b1.data[i];
k=0;//试商的结果
while(yu.compareTo(b2)>=0){
yu=sub(yu,b2);//不断相减,来求商.
k+=1;
}
result.append(k);
}
return new bigInt(result.toString());
}