为大数字计算$ {2n \ choose n} - {2n \ choose n-1} $ \ PASCAL

时间:2015-12-19 16:27:10

标签: pascal binomial-coefficients

你好我需要计算这个二项式系数

${2n \choose n} - {2n \choose n-1}$

对于大数字,我不知道如何使用数据类型LongWordQWord

有什么想法吗? :)

1 个答案:

答案 0 :(得分:0)

在 Pascal 被设计的时代,对数和计算尺是工程师常用的工具,以至于导致包含一些基本的 logarithmic functions

  • ln(i) 取正数的(自然)对数。 “自然”是指 e,欧拉常数。
  • exp(i) 计算欧拉常数的 i 次方,eⁱ。

您可以在一定程度上利用logarithmic identities来克服integer的局限性。要使用二项式系数的阶乘公式,您可以编写:

type
    integerNonNegative = 0..maxInt;

function factorialLn(n: integerNonNegative): real;
var
    f: real value 0.0;
begin
    for n := n downto 2 do
    begin
        f := f + ln(n);
    end;
    
    factorialLn := f;
end;

function binomialCoefficient(
        protected n, k: integerNonNegative
    ): integerNonNegative;
begin
    binomialCoefficient := round(exp(
            factorialLn(n) -
            (factorialLn(k) + factorialLn(n - k))
        ));
end;

我认为,这很棒,因为它不需要您使用/加载额外的库,而且不要忘记学习它。例如,GMP(GNU 多精度库)是 biiiiig,需要一些时间来深入了解它。这样你就可以简单地写

binomialCoefficient(2 * n, n) - binomialCoefficient(2 * n, n - 1)

然而,最好的方法当然是改进 你的 算法。在这种情况下,您对降低阶乘的幅度特别感兴趣。浏览一些公式我发现你的表情看起来很像

     ⎛ p − 1 ⎞   ⎛ p − 1 ⎞     p − 2 q  ⎛ p ⎞
     ⎜       ⎟ − ⎜       ⎟  =  ―――――――  ⎜   ⎟
     ⎝   q   ⎠   ⎝ q − 1 ⎠        p     ⎝ q ⎠

所以你做了一些替换

                     p — 1  =  2 n                          │ + 1
                         p  =  2 n + 1

在原来的等价物上展开

       ⎛ 2 n ⎞   ⎛  2 n  ⎞     2 n + 1 − 2 n  ⎛ 2 n + 1 ⎞
       ⎜     ⎟ − ⎜       ⎟  =  ―――――――――――――  ⎜         ⎟
       ⎝  n  ⎠   ⎝ n − 1 ⎠        2 n + 1     ⎝    n    ⎠

现在我们得到了一个产品而不是一个差异,我们展开所有内容并合并它:

2 n + 1 − 2 n  ⎛ 2 n + 1 ⎞        1         (2 n + 1)!
―――――――――――――  ⎜         ⎟  =  ―――――――  ―――――――――――――――――
   2 n + 1     ⎝    n    ⎠     2 n + 1  n! (2 n + 1 − n)!

                                  1      (2 n)! (2 n + 1)   │
                            =  ―――――――  ―――――――――――――――――   │ cancel 2n+1
                               2 n + 1     n! (n + 1)!      │

                                  (2 n)! 
                            =  ―――――――――――
                               n! (n + 1)!

                                 2 n
                               ∏        i
                                 i = 1
                            =  ―――――――――――――――――――――――
                                 n
                               ∏        i   n! (n + 1)
                                 i = 1

                                 n            2 n
                               ∏        i   ∏           i
                                 i = 1        i = n + 1
                            =  ――――――――――――――――――――――――――
                                 n
                               ∏        i   n! (n + 1)
                                 i = 1

                                           2 n
                               (n + 1)   ∏           i
                                           i = n + 2
                            =  ――――――――――――――――――――――――――
                                 
                               (n + 1)   n!

                                 2 n
                               ∏           i
                                 i = n + 2
                            =  ―――――――――――――
                                    n!

另一方面,这表明我们只需要 n − 2 次迭代。换句话说,计算以下不需要对数引入的任何开销,但同样精确,而且不会超过 integer 的限制,最后但并非最不重要的是它更快:

r := 1.0;
for i := 2 to n do
begin
    r := r / i * (n + i);
end;
writeLn(round(r));

这只是以程序风格编写的以下内容:

                   8! / 5!                     6 ⋅ 7 ⋅ 8
                   ―――――――  =  ―――――――――――――――――――――――――
                      3!       1 ⋅ 2 ⋅ 3

                                6       7       8
                            =  ―――  ⋅  ―――  ⋅  ―――
                                1       2       3

整洁吧?