java大整數四則運算

xqhadoop發表於2018-03-04

前言:
目前計算機中資料儲存最大為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());   
}

相關文章