【问题标题】:Java library for fast multiplication of very big numbers用于快速乘以非常大的数字的 Java 库
【发布时间】:2018-04-07 05:38:15
【问题描述】:

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

【问题讨论】:

  • FWIW,感谢您的链接。它将帮助我将 Schönhage-Strassen 实现到我自己的(基于 Object Pascal 的)BigIntegers 实现中。我已经实现了 Karatsuba 和 Toom-Cook,但直到现在,Schönhage-Strassen 还是有点超出我的视野。

标签: java biginteger multiplication


【解决方案1】:

您链接到的解决方案 — Schönhage-Strassen — 确实是一种使非常大的 BigInteger 相乘更快的好方法。

由于开销很大,对于小得多的 BigInteger,它并不会更快,因此您可以使用它,递归地降低到某个阈值(您必须凭经验找出该阈值是什么),然后使用 BigInteger 自己的乘法,它已经实现了 Toom-Cook 和 Karatsuba 分治算法(从 Java 8,IIRC 开始),还递归地降低到某些阈值。

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

结论

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

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


注意:

您链接到的 Schönhage-Strassen 算法会还原为 Karatsuba 以获得较小的子产品。从那时起(2012 年圣诞节),不再使用 Karatsuba,而是使用 BigInteger 中大大改进的实现,并直接使用 BigInteger::multiply(),而不是 Karatsuba。您可能还必须更改使用的阈值。

【讨论】:

    【解决方案2】:

    就我的思维能力而言,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)));
        }
    }
    

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

    【讨论】:

    • @N00b Pr0grammer - 谢谢你的回答。我编译了 N=1000000000 的代码,得到以下输出:“5007448 1136159 true” 我有点困惑,karatsuba 做乘法需要更长的时间?
    • 您是否与任何其他实现进行了某种比较?
    • 不,还没有。我只是按原样运行您的代码。只需输入 N=1000000000
    • @user1340852 您的main 方法测量并打印运行时间,显然是为了进行基准测试。我想提供更多关于基准测试的背景知识,因为它并不像看起来那么容易。幼稚的实现(如您的)可能会给出错误的结果并导致错误的结论。我链接到的问题是关于微基准测试的“转到”SO 问题。
    猜你喜欢
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    相关资源
    最近更新 更多