【问题标题】:Increasing performance when doing calculations with huge numbers (BigInteger)在进行大量计算时提高性能 (BigInteger)
【发布时间】:2017-12-26 19:57:45
【问题描述】:

我是一个经验不足的编码员,我在做一个练习,我需要计算两个 catalan sequences 的乘积,对于 05000 之间的每个 n-value,然后总结这些乘积。

代码当前输出正确答案,但运行时间为 2.9-3.3 秒,n-值为5000。我的目标是让代码每次在 3 秒内运行,因此我需要获得大约半秒的时间。

计算中的最大数字 (10,000!) 超过 35,000 位数,因此 intlong 不能用于任何更重的计算,我也不能使用任何外部库,这几乎让我留下了BigInteger

通过测试,我发现下面显示的sum() 中的for-loop 是迄今为止完成时间最长的部分(约85% 的运行时间),因此这可能是最需要提高性能的地方。任何关于如何优化它的提示都值得赞赏。

// For all n-values
for (int k=0; k < n/2 + rest; k++) {
    result = result.add(catalan(k).multiply(catalan(n-k)));
}

这是完整的代码:

import java.math.BigInteger;
import java.util.Scanner;

public class FactorialSum {

    static BigInteger[] bigInt;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try {
            int n = sc.nextInt();

            // Creates a new array and initializes the default values
            bigInt = new BigInteger[n*2+1];
            bigInt[0] = BigInteger.ONE;
            if (n > 0)
                bigInt[1] = BigInteger.ONE;

            calcFactorials(n);

            // Calculates and prints the results
            System.out.println(sum(n));
        } finally {
            sc.close();
        }
    }

    // Calculates and stores all the factorials up to and including the specified n-value
    private static void calcFactorials(int n) {
        for (int factor = 2; factor <= n*2; factor++) {
            bigInt[factor] = bigInt[factor-1].multiply(BigInteger.valueOf(factor));
        }
    }

    // Calculates the catalan number using the binomial coefficient for the
    // specified n-value
    private static BigInteger catalan(int n) {
        BigInteger binomial = bigInt[n*2].divide(bigInt[n].pow(2));
        BigInteger cn = binomial.divide(BigInteger.valueOf(n+1));
        return cn;
    }

    // Calculates the sum for the specified range 0-n
    private static BigInteger sum(int n) {
        if (n > 0) {
            BigInteger result = BigInteger.ZERO;
            int rest = n % 2;

            // For all n-values
            for (int k=0; k < n/2 + rest; k++) {
                result = result.add(catalan(k).multiply(catalan(n-k)));
            }
            result = result.multiply(BigInteger.valueOf(2));

            // For even n-values
            if (rest == 0) {
                BigInteger lastNumber = catalan(n/2);
                result = result.add(lastNumber.pow(2));
            }
            return result;
        } else {
            return BigInteger.ONE;
        }
    }
}

【问题讨论】:

  • 您能否尝试在多个线程中运行您的总和,也许在一个中运行偶数 # 并在另一个中运行奇数,然后在两个线程完成后将这些总数相加?
  • 使用帕斯卡三角计算二项式系数
  • 有一些优化的潜力。而不是x.pow(2),而是使用x.multiply(x)。那应该更快。 pow() 有相当多的开销。此外,使用预先计算的值,例如而不是BigInteger.valueOf(2),预先计算一次并使用它。这样就不必每次都重新分配。而且您可能还想存储调用catalan(n) 的结果,因此您不必再次计算catalan(n-k)(假设它已经计算过了)。请注意,我还没有尝试过,但我会尝试。
  • @RudyVelthuis x.multiply(x) 已经检查它是否是multiplied by itself,并使用专门的平方算法。所以这可能会更快(或者,至少,同样快;大概该阈值已调整)。另外,BigInteger.valueOf(2)will return a cached value
  • 一种优化技术是尽可能使用多头。例如,如果您将两个值相乘,并且最大值小于 Long.MAX_VALUE 的平方根,则使用 longs,不要先转换为 BigInteger 和多个 BigInteger,先进行乘法,然后再转换为 BigInteger。加法和减法也是如此。仅在必要时使用 BigInteger。

标签: java performance biginteger factorial catalan


【解决方案1】:

我需要计算两个加泰罗尼亚语序列的乘积 0 到 5000 之间的单个 n 值,然后汇总这些产品。

嗯,这正是Catalan number 的另一种定义。

Cn+1 = SUMi=0..n ( Ci * Cni )

所以,你基本上需要计算C5001。为了快速计算,您可以使用另一个递归关系:

Cn+1 = 2*(2n+1) / (n+2) * Cn

这是程序:

public static void main(String[] args) {
    int n = 5000;

    BigInteger Cn = BigInteger.ONE;
    for (int i = 0; i <= n; i++) {
        Cn = Cn.multiply(BigInteger.valueOf(4 * i + 2)).divide(BigInteger.valueOf(i + 2));
    }

    System.out.println(Cn);
}

在我的笔记本电脑上运行不到 0.04 秒。享受吧!

【讨论】:

  • 2*(2n+1) / (n+2) 可以纯粹在整数数学中完成,进一步提高性能
  • @LưuVĩnhPhúc 不,它不能。尝试替换 n=2 :)
  • 我不明白。为什么(4*2 + 2)/(2+2) 不起作用?
  • @LưuVĩnhPhúc 因为在整数算术中(4*2 + 2)/(2+2) == 2,但是这里你需要将C2乘以2.5或者乘以10然后除以4。
  • 哇哦,我从来没想到会这么简单。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 1970-01-01
相关资源
最近更新 更多