用于快速乘以非常大数字的Java库

时间:2018-04-07 05:38:16

标签: java biginteger multiplication

我正在编写一个程序,需要在一个点上乘以非常大的数字(百万位)。任何人都可以建议一个java库来快速乘以大数字吗?我找到this,但我不确定这是否是正确的解决方案,所以我试图找另一个尝试。

2 个答案:

答案 0 :(得分:4)

你链接的解决方案--Schönhage-Strassen - 确实是一种很好的方法,可以更快地增加非常大的BigIntegers。

由于开销很大,对于小得多的BigIntegers并不快,所以你可以使用它,递归地降低到某个阈值(你必须根据经验找出什么是theshold)然后使用BigInteger自己的乘法已经实施Toom-Cook和Karatsuba分治算法(自Java 8,IIRC以来),也递归到某些阈值。

忘记答案告诉你使用Karatsuba。 Java不仅实现了这一点,而且甚至更快(对于非常大的BigIntegers)Toom-Cook算法,它也慢得多(对于如此巨大的价值观)比Schönhage-Strassen。

结论

再次:对于小值,使用简单的教科书乘法(但使用 - unsigned - 整数作为“数字”或“bigits”)。对于更大的值,使用Karatsuba(这是一种递归算法,将大型BigIntegers分解为几个较小的并将它们相乘 - 一种分而治之的算法)。对于更大的BigIntegers,使用Toom-Cook(也是分而治之)。对于非常大的BigIntegers,使用Schönhage-Strassen(IIRC,一种基于FFT的算法)。请注意, Java已经为不同大小的Bigintegers 实现了教科书(或“基本案例”),Karatsuba和Toom-Cook乘法。它还没有实施Schönhage-Strassen。

但即使有了所有这些优化,非常大的值的乘法往往会很慢,所以不要指望奇迹。

注意:

您链接的Schönhage-Strassen算法将恢复为Karatsuba以获取较小的子产品。而不是Karatsuba,从那时起(2012年圣诞节)回归到了BigInteger的大大改进的实现,只需直接使用BigInteger::multiply(),而不是Karatsuba。您可能还需要更改使用的阈值。

答案 1 :(得分:1)

就我的思维能力而言,Karatsuba Algorithm可以这种方式实施:

This链接提供了相同的C ++实现,这也很容易被类似Java实现。

import java.math.BigInteger;
import java.util.Random;

class Karatsuba {
    private final static BigInteger ZERO = new BigInteger("0");

    public static BigInteger karatsuba(BigInteger x, BigInteger y) {

        // cutoff to brute force
        int N = Math.max(x.bitLength(), y.bitLength());
        if (N <= 2000) return x.multiply(y);                // optimize this parameter

        // number of bits divided by 2, rounded up
        N = (N / 2) + (N % 2);

        // x = a + 2^N b,   y = c + 2^N d
        BigInteger b = x.shiftRight(N);
        BigInteger a = x.subtract(b.shiftLeft(N));
        BigInteger d = y.shiftRight(N);
        BigInteger c = y.subtract(d.shiftLeft(N));

        // compute sub-expressions
        BigInteger ac    = karatsuba(a, c);
        BigInteger bd    = karatsuba(b, d);
        BigInteger abcd  = karatsuba(a.add(b), c.add(d));

        return ac.add(abcd.subtract(ac).subtract(bd).shiftLeft(N)).add(bd.shiftLeft(2*N));
    }


    public static void main(String[] args) {
        long start, stop, elapsed;
        Random random = new Random();
        int N = Integer.parseInt(args[0]);
        BigInteger a = new BigInteger(N, random);
        BigInteger b = new BigInteger(N, random);

        start = System.currentTimeMillis(); 
        BigInteger c = karatsuba(a, b);
        stop = System.currentTimeMillis();
        StdOut.println(stop - start);

        start = System.currentTimeMillis(); 
        BigInteger d = a.multiply(b);
        stop = System.currentTimeMillis();
        StdOut.println(stop - start);

        StdOut.println((c.equals(d)));
    }
}

希望这能很好地回答你的问题。