1. 程式人生 > >超長整數的加減乘除四則運算

超長整數的加減乘除四則運算

超長整數四則運算

前面寫了減法和加法,這裡做個彙總,寫一下四則運算的分別實現

概要

當參加運算的數字是超過long型長度的資料,以至於只能用string字串來儲存,這時候加減乘除的運算已經不能用了。

對於這種問題Java提供了一個BigInteger類,可以用來儲存,並有對應的加減乘除的方法,要求兩個運算元都必須是BIgInteger型。可以解決問題。

這裡從模擬計算機的內部實現解讀來解決問題。

這個時候就可以像小時候列豎式來計算一樣,一位一位的計算,或進位或借位,模擬計算的過程。將整個的計算拆解成一個個小的計算過程。

除法與加減和乘有所不同,除法是從高位到低位進行計算,加減乘是從低位到高位進行計算。

除法

被除數 A ÷ 除數 B = 商 C ...餘數 D

思路是:

  1. 取 A 第一位(從高到低),除以除數,得到商和餘數,
  2. 商加入一個數組,餘數乘10+被除數的下一位,除以除數,得到商和餘數,
  3. 依次執行 2,直到將A的每一位都遍歷完。

程式碼實現:

//這裡的在控制檯輸入一個長整數被除數和一個短int型除數
//這只是我可以人為的改變輸入
//沒有給定一個值,採用了BufferReader和StringBuilder來快取讀入和類物件加快處理速度

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main { public static void main(String[] args) throws IOException { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String[] ab = bufferedReader.readLine().split(" "); bufferedReader.close(); int b = Integer.parseInt(ab[1]), t = 0;
StringBuilder stringBuffer = new StringBuilder(); for (int i = 0; i < ab[0].length(); i++) { t = t * 10 + ab[0].charAt(i) - '0'; stringBuffer.append((char)(t / b + '0'));//商加入字串輸出 t = t % b;//計算餘數進行下一輪運算 } String result = stringBuffer.toString(); if (result.charAt(0) == '0' && result.length() != 1) { //若第一次運算的商是不足除的,則為零,這時候要把這個零舍掉 System.out.print(result.substring(1) + " " + t); } else { System.out.print(result + " " + t); } } }
輸入樣例:
123456789050987654321 7

輸出樣例:
17636684150141093474 3

加法

同號相加完成後列印符號就可以,這裡做的是正數相加。若要判斷符號加幾行程式碼就行。

若是異號相加,等同於減法,在後面。

由於加減乘法是從低位開始計算的,所以我們需要一個倒序的操作(也可以不導致,但是容易混淆)

思路是

  1. 將把整數倒序儲存,整數的個位存於陣列0下標位置,最高位存於陣列長度-1下標位置。之所以倒序儲存,更加符合我們從左到右訪問陣列的習慣。
  2. 建立結果陣列,結果陣列的最大長度是較大整數的位數+1,原因很明顯,可能會存在進位導致長度加一。
  3. 遍歷兩個陣列,從左到右按照對應下標把元素兩兩相加,不滿10直接在當前陣列位置儲存(當前陣列位置的值 += 計算後的值),滿10減10後存在對應的位置,進一位,填充1到下一個陣列位置中。
  4. 把Result陣列的全部元素再次逆序,若有0,去掉首位,0,就是最終結果。

程式碼實現

public class Main {
    public static String bigNumberSum(String bigNumberA, String bigNumberB) {

        //1.把兩個大整數用陣列逆序儲存
        char[] charsA = new StringBuffer(bigNumberA).reverse().toString().toCharArray();
        char[] charsB = new StringBuffer(bigNumberB).reverse().toString().toCharArray();

        //2.構建result陣列,陣列長度等於較大整數位數+1
        int maxLength = charsA.length > charsB.length ? charsA.length : charsB.length;
        int[] result = new int[maxLength + 1];
        //3.遍歷陣列,按位相加
        for (int i = 0; i < result.length; i++) {
            int temp = result[i];
            if (i < charsA.length) {
                temp += charsA[i] - '0';
            }
            if (i < charsB.length) {
                temp += charsB[i] - '0';
            }

        //判斷是否進位
            if (temp >= 10) {
                temp = temp - 10;
                result[i + 1] = 1;
            }
            result[i] = temp;
        }

       //4.把result陣列再次逆序並轉成String
        StringBuilder sb = new StringBuilder();
        boolean flag = true;
        for (int i = result.length - 1; i >= 0; i--) {
            //判斷最後一位是否發生了進位
            if (result[i] == 0 && flag) {
                flag = false;
                continue;
            }

            sb.append(result[i]);
        }
        return sb.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(bigNumberSum("426709752318", "95481253129"));
    }
}

減法

減法需要判斷兩個數的大小和正負,大小可以通過長度和對應位的大小來比較,長度不同,長的 數字大,相同長度,對應位比較,第一次出現較大的數是大的。則用他的符號。同時,負數-正數可以當做加法來算。流程同上,只不過有符號的判斷,這裡不考慮。

因操作有點麻煩,這裡預設是全正數且大數減小數

思路是

  1. 將把整數倒序儲存,整數的個位存於陣列0下標位置,最高位存於陣列長度-1下標位置。之所以倒序儲存,更加符合我們從左到右訪問陣列的習慣。
  2. 建立結果陣列,結果陣列的最大長度是較大整數的位數,不需要考慮加一的情況(負-正不考慮)。
  3. 遍歷兩個陣列,從左到右按照對應下標把元素兩兩相減,夠減直接在當前陣列位置儲存(當前陣列位置的值 += 計算後的值),不夠減按10減,借一位,第一個數字下一位的值減一。
  4. 把Result陣列的全部元素再次逆序,若有0,去掉首位,0,就是最終結果。

程式碼實現

package PATY;
public class BigNumberSub {
    public static String bigNumberSum(String bigNumberA, String bigNumberB) {

        //1.把兩個大整數用陣列逆序儲存
        char[] charsA = new StringBuffer(bigNumberA).reverse().toString().toCharArray();
        char[] charsB = new StringBuffer(bigNumberB).reverse().toString().toCharArray();

        //2.構建result陣列,陣列長度等於較大整數位數+1
        int maxLength = charsA.length > charsB.length ? charsA.length : charsB.length;
        int[] result = new int[maxLength + 1];
        //3.遍歷陣列,按位相減
        int[] need = new int[maxLength + 2];//判斷是否需要借位
        for (int i = 0; i < result.length; i++) {
            int temp = 0;
            int a=0,b=0;
            if (i < charsA.length) {
                if(need[i]==1)
                    a= charsA[i] - '0'-1;
                a= charsA[i] - '0';
            }
            if (i < charsB.length) {
                b= charsB[i] - '0';
            }

        //判斷是否借位
            if (a<b) {
                temp = a+10-b;
                need[i+1]=1;
            }else
                temp=a-b;
            result[i] = temp;
        }

       //4.把result陣列再次逆序並轉成String
        StringBuilder sb = new StringBuilder();
        boolean flag = true;
        for (int i = result.length - 1; i >= 0; i--) {
            //判斷最後一位是否發生了進位
            if (result[i] == 0 && flag) {
                flag = false;
                continue;
            }

            sb.append(result[i]);
        }
        return sb.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(bigNumberSub("222222222", "1111"));
    }
}

乘法

乘法與加法類似,不過有些複雜,兩個for迴圈,分別是兩個數字的位數長度作為範圍,兩個迴圈依次遍歷,這裡進位就變成了要對10取商進位,對商取餘儲存在當前位。思路大概就是這樣。
emmmm,程式碼懶的寫了,借鑑網上找的程式碼,有些bug,調通了貼出來。

這個程式碼一開始沒有進行倒置的操作,而是從後向前讀取,效果一樣。

思路是

  1. 建立結果陣列,結果陣列的最大長度是兩個數字的長度相加 l+r。
  2. 遍歷兩個陣列,從一個數的最低位開始,依次乘第二個數字(從低位到高位),加上進位和這個陣列位置本來的數(因為乘法,一個數組位置可能會有多次計算),對10取餘數得到新的當前陣列位置的數,取商得到新一輪的進位。
  3. 把Result陣列的全部元素再次逆序,若有0,去掉首位,0,就是最終結果。

程式碼實現


public class BigNumberMul {
    public static String multiply(String num1, String num2) {
        int l = num1.length();
        int r = num2.length();
        //用來儲存結果的陣列,可以肯定的是兩數相乘的結果的長度,肯定不會大於兩個數各自長度的和。
        int[] num = new int[l+r];//從低到高存貯,最後需倒置
        //第一個數按位迴圈
        for(int i=0;i<l;i++) {
            //得到最低位的數字
            int n1=num1.charAt(l-1-i)-'0';
            //儲存進位
            int tmp=0;
            //第二個數按位迴圈
            for(int j=0;j<r;j++) {
                int n2=num2.charAt(r-1-j)-'0';
                //拿出此時的結果數組裡存的數+現在計算的結果數+上一個進位數
                tmp=tmp+num[i+j]+n1*n2;
                //得到此時結果位的值
                num[i+j]=tmp%10;
                //此時的進位
                tmp/=10;
            }
            //第一輪結束後,如果有進位,將其放入到更高位
            num[i+r]=tmp;
        }

        int i=l+r-1;
        //計算最終結果值到底是幾位數,高位的零需要捨去(因為可能達不到l+r的長度)
        while(i>0&&num[i]==0){
            i--;
        }
        StringBuilder result=new StringBuilder("");
        while(i>=0) {
            result.append(num[i--]) ;
        }
        return result.toString();
    }

    public static void main(String[] args)
    {
        System.out.println(multiply("111", "1111"));
    }
}