大整數乘法,參照BigInteger中改寫,直接上代碼

對於java來說,每一個整數運算都是int型,爲了能運用cpu本身的乘法且不溢出,所以選用int[]數組來存儲結果,在BigInteger的源代碼中,還有另外兩個算法,有兩個限值

private static final int KARATSUBA_THRESHOLD = 80;  //數據的Byte數=80*4

private static final int TOOM_COOK_THRESHOLD = 240;  //數據的Byte數=240*4

當任一乘數的表示需要的int數小於80時,選用multilplyToLen方法,當兩個乘數的int數都小於240且大於80時,選用KARATSUBA,否則選用TOOM_COOK,這兩種算法都是基於分治法來實現,然後利用多項式減少乘法的次數。

如果每次只取出一位十進制數做乘法運算,cpu執行的仍然是int型的乘法,很浪費,可以一次取出9位,然後做運算。

重新優化了一下循環中的代碼

/*
       大整數乘法,@see BigInteger#multiplyToLen
     */
    public static String multiply(String x, String y) {
        int len1 = x.length();
        int len2 = y.length();

        int l1 = len1 / 9;
        int rem1 = len1 % 9;
        if (rem1 !=0) {
            l1++;
        }
        int[] xArr = extract(x, l1, rem1);

        int l2 = len2 / 9;
        int rem2 = len2 % 9;
        if (rem2 !=0) {
            l2++;
        }
        int[] yArr = extract(y, l2, rem2);

        int[] res = new int[l1+l2];
        int xstart=l1-1;
        int ystart=l2-1;

        long L_MASK = 1000000000L;
        long carry = 0;
        int k = l1 + l2 - 1;
        for (int j = ystart; j >=0; j--, k --) {
            long product = ((long) yArr[j]) * xArr[xstart] + carry;
            res[k] = (int) (product % L_MASK);
            carry = product / L_MASK;
        }
        res[k] = (int) carry;
        for (int i = xstart -1; i >=0; i--) {
            carry = 0;
            k = l2 + i;
            for (int j = ystart; j >=0; j --, k--) {
                long product = ((long) yArr[j]) * xArr[i] + res[k] + carry;
                res[k] = (int) (product % L_MASK);
                carry = product / L_MASK;
            }
            res[k] = (int) carry;
        }
        StringBuilder ans = new StringBuilder();
        boolean flag = false;
        for (int i = 0; i < res.length; i++) {
            if (flag==false) {
                if (res[i] != 0) {
                    flag = true;
                    ans.append(Integer.toString(res[i]));
                }
            }else {
                String s = Integer.toString(res[i]);
                for (int i1 = 0; i1 < 9 - s.length(); i1++) {
                    ans.append('0');
                }
                ans.append(s);
            }
        }
        return ans.toString();
    }

    private static int[] extract(String s, int len, int rem) {
        int[] arr = new int[len];
        for (int i = 0; i < len; i++) {
            int temp = i * 9;
            if (rem != 0) {
                if (i == 0) {
                    arr[i] = Integer.valueOf(s.substring(0, rem));
                } else {
                    arr[i] = Integer.valueOf(s.substring(rem + temp - 9, rem + temp));
                }
                continue;
            }
            arr[i] = Integer.valueOf(s.substring(temp, temp + 9));
        }
        return arr;
    }

2.性能測試結果

代碼:

 public static void main(String[] args) {
        String s1 = "1325134651345642355434444462523452345632458888888888888888888888888888888888888888888888888888888888888888888888";
        String s2 = "13251346513456423554734564555555537388888888888888888888888888888888888888888888888888888888888888888888888888880000001";
        String multiply1 = null;
        String multiply2 = null;

        long t1 = System.nanoTime();
        for (int i = 0; i < 100000; i++) {
            multiply1 = new BigInteger(s1).multiply(new BigInteger(s2)).toString();
        }
        long t2 = System.nanoTime();
        for (int i = 0; i < 100000; i++) {
            multiply2 = multiply(s1, s2);
        }
        long t3 = System.nanoTime();
        System.out.println("" + (t2 - t1) / 100000.0 + "ns " + (t3 - t2) / 100000.0 + "ns");

        System.out.println(multiply1);
        System.out.println(multiply2);
    }

結果:

 

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