使用大量数字进行计算时提高性能(BigInteger)

时间:2017-12-26 19:57:46

标签: java performance biginteger factorial catalan

我是一名经验不足的程序员,我需要为每n - 05000之间的值计算两个catalan sequences的乘积,然后总结那些产品。

代码当前输出正确答案,但需要2.9-3.3秒才能运行n - 值5000。我的目标是让代码每次都在3秒内运行,所以我需要大约半秒钟。

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

从测试开始,我发现下面显示的for-loop中的sum()是迄今为止完成时间最长的(约为运行时间的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;
        }
    }
}

1 个答案:

答案 0 :(得分:4)

  

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

嗯,这正是Catalan number的替代定义。

C n + 1 = SUM i = 0..n (C i * C ni

所以,你基本上需要的是计算C 5001 。要快速计算,您可以使用另一个递归关系:

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

以下是该计划:

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秒。享受!