计算大数的乘积

Computing the product of big numbers

我正在尝试计算

,

其中 Ci 是第 i 个加泰罗尼亚数字。

为了解决这个问题,我从 0 循环到 n 并对两个加泰罗尼亚数字的乘积求和:

BigInteger catalanSum = 0;
            for (int i = 0; i <= n; i++)
                catalanSum += catalan(i) * catalan(n - i);

catalan 函数返回二项式系数除以 n + 1:

    BigInteger catalan(int n)
    {
        return NchooseK(2 * n, n) / (n + 1);
    }

为了计算二项式系数,我使用了这个函数:

    BigInteger NchooseK(int n, int k)
    {
        BigInteger res = 1;

        if (k > n - k)
            k = n - k;

        for (int i = 0; i < k; ++i)
        {
            res *= (n - i);
            res /= (i + 1);
        }
        return res;
    }

它在 n = 1000 之前工作正常,但一旦它变得更高,它真的会慢很多。有什么方法可以优化这个计算吗?

编辑:

我通过首先使用以下代码片段保存加泰罗尼亚语来加快计算速度,感谢 xanatos 的回答:

BigInteger[] catalans = new BigInteger[n+1];
            BigInteger catalanSum = 0;
            for (int i = 0; i <= n; i++)
                catalans[i] = catalan(i); 
            for (int i = 0; i <= n; i++)
                catalanSum += catalans[i] * catalans[n - i];

编辑 2: 当catalan[i] == catalan[n - i]时,剩下的一半计算结果不会和前半部分一样吗?

您还可以缓存阶乘。

您所描述的计算似乎是计算第 nCatalan Number 的第一个递推关系(而且您也不必要地应用二项式计算,而您可以只使用加泰罗尼亚数字自己在复发)。这是 O(n^2) 复杂度加上所有二项式计算的复杂度。为什么不使用第二个递归关系?

catalan(0) = 1
catalan(n + 1) = 2*(2*n + 1) / (n + 2) * n

您可以做两件事:

首先,检查您序列的 OEIS。你会发现序列有an entry。这个条目有一个有用的公式:

2*(2*n-1)*a(n-1) = (n+1)*a(n)

因此,可以更有效地计算加泰罗尼亚数字:

BigInteger lastCatalan = 1;
catalans[0] = lastCatalan;    
for(int i = 1; i <= n; ++i)
{
    lastCatalan = (2 * (2 * i - 1) * lastCatalan) / (i + 1);
    catalans[i] = lastCatalan;
}

第二点是你的求和是对称的。即,您只需要对一半的条目求和:

BigInteger catalanSum = 0;
for (int i = 0; i < (n + 1) / 2; i++)
    catalanSum += catalans[i] * catalans[n - i];
catalanSum = 2 * catalanSum;
if (n % 2 == 0)
    catalanSum += catalans[n / 2] * catalans[n / 2];

在 גלעד ברקן 指出您要查找的总和是第 n+1 个加泰罗尼亚数字后,这可以大大简化:

BigInteger catalanSum= 1;
for(int i = 1; i <= n + 1; ++i)
    catalanSum = (2 * (2 * i - 1) * catalanSum) / (i + 1);