Java大數加減乘法

加減法就是模擬筆算的過程,包括進位和借位。乘法若用筆算過程,時間複雜度爲O(n2)。現有Karatsuba算法,時間複雜度爲O(nlog23)

原理如下:

比如1234*5678,先把數字拆爲12, 34和 56, 78
令z=34*78
r1=12*56*10000
r2=(12*78+34*56)*100
=((12+34)*(56+78)-34*78)*100
//這樣算可以少算一次乘法,因爲34*78已經在上一步算過
最後結果=r1+r2+z
如果第一次拆分之後數還是比較大,可以遞歸調用接着拆分,直到拆分後的數足夠小。

代碼如下:

package test;

import java.math.BigInteger;

public class BigNumberCalculater {

    public static void main(String[] args) {
        String num1 = "987654321";
        String num2 = "123456789";

        System.out.println("加=" + AddString(num1, num2));
        System.out.println("減=" + MinusString(num1, num2));
        System.out.println("乘=" + KaratsubaMultiply(num1, num2));

        BigInteger a = new BigInteger(num1);
        BigInteger b = new BigInteger(num2);

        System.out.println("加=" + a.add(b));
        System.out.println("減=" + a.subtract(b));
        System.out.println("乘=" + a.multiply(b));
        System.out.println("除=" + a.divide(b));
        System.out.println("取餘=" + a.mod(b));
    }

    // 大數減法
    public static String MinusString(String num1, String num2) {
        int len1 = num1.length();
        int len2 = num2.length();
        if (num1.equals(num2)) {
            return "0";
        }
        boolean positive = true;
        if (len1 < len2 || (len1 == len2 && num1.compareTo(num2) < 0)) {
            positive = false;
            String tmp = num1;
            num1 = num2;
            num2 = tmp;
            int temp = len1;
            len1 = len2;
            len2 = temp;
        }
        String result = "";
        int i = len1 - 1, j = len2 - 1;
        int a, b, sum, carray = 0;

        while (i >= 0 || j >= 0) {// 從低位到高位對位做減法
            a = (i >= 0 ? num1.charAt(i) - '0' : 0);
            b = (j >= 0 ? num2.charAt(j) - '0' : 0);
            sum = a - b + carray;
            carray = 0;

            if (sum < 0) {// 借位
                sum += 10;
                carray = -1;
            }
            result = (sum) + result;
            --i;
            --j;
        }
        result = result.replaceAll("^[0]+", "");// 刪除前導0
        return positive ? result : "-" + result;
    }

    // 大數加法
    public static String AddString(String num1, String num2) {
        int len1 = num1.length();
        int len2 = num2.length();
        if (len1 <= 0) {
            return num2;
        }
        if (len2 <= 0) {
            return num1;
        }
        String result = "";
        int i = len1 - 1, j = len2 - 1;
        int a, b, sum, carry = 0;
        while (i >= 0 || j >= 0 || carry > 0) {// 從末位開始相加
            a = i >= 0 ? num1.charAt(i) - '0' : 0;
            b = j >= 0 ? num2.charAt(j) - '0' : 0;
            sum = a + b + carry; // 按位相加並加上進位
            carry = sum / 10;// 進位
            result = (sum % 10) + result;
            --i;
            --j;
        }
        return result;
    }

    // Karatsuba大數乘法
    public static String KaratsubaMultiply(String num1, String num2) {
        int len1 = num1.length();
        int len2 = num2.length();
        int len = len1;
        if (len1 < len2) {
            for (int i = 0; i < len2 - len1; ++i) {
                num1 = "0" + num1;
            }
            len = len2;
        } else {
            for (int i = 0; i < len1 - len2; ++i) {
                num2 = "0" + num2;
            }
            len = len1;
        }
        if (len == 0) {
            return "0";
        }
        if (len == 1) {
            return String.valueOf(((num1.charAt(0) - '0') * (num2.charAt(0) - '0')));
        }
        int mid = len / 2;

        String x1 = num1.substring(0, mid);
        String x0 = num1.substring(mid, len);
        String y1 = num2.substring(0, mid);
        String y0 = num2.substring(mid, len);

        String z0 = KaratsubaMultiply(x0, y0);
        String z1 = KaratsubaMultiply(AddString(x1, x0), AddString(y1, y0));
        String z2 = KaratsubaMultiply(x1, y1);

        String r1 = ShiftString(z2, 2 * (len - mid));
        String r2 = ShiftString(MinusString(MinusString(z1, z2), z0), len - mid);

        return AddString(AddString(r1, r2), z0);
    }

    // 在右邊添加len個0
    public static String ShiftString(String num, int len) {
        if (num.equals("0")) {
            return num;
        }
        for (int i = 0; i < len; ++i) {
            num += "0";
        }
        return num;
    }
}

java自帶的BigInteger可以滿足任意大整數的各種運算,實數的話可以用BigDecimal,於是我順便用BigInteger驗證了一下此算法的結果。結果爲:

加=1111111110
減=864197532
乘=121932631112635269
加=1111111110
減=864197532
乘=121932631112635269
除=8
取餘=9

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章