-3

hello I need to calculate this binomial coefficient

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

for big numbers, and I don't know how can i use data type LongWord or QWord.

Any idea? :)

lurker
  • 56,987
  • 9
  • 69
  • 103
MatFyzak
  • 11
  • 4
  • Can't you just declare your variables with, for example `var foo: qword;` or `var bar: longword;` and use them? It's unclear what your question is really all about. What do you mean that you to use them? – lurker Dec 19 '15 at 17:22

2 Answers2

0

If you try to calculate n! for n larger than a few hundred you will overflow pascals floating point numbers, so the naive method of calculating {2n choose n} by using (2n)!/(n!)^2 may not work, even though the final number might fit into a real without the overflow, as the (2n)! may overflow.

What you need to do is to mix the multiplication and division so you never get overflow or underflow. For example, assuming {2n choose n} itself won't overflow, something like:

 X2nChoosen = 1.0;
 for i := 1 to n do
   X2nChoosen := X2nChooseN*(2*i)*(2*i-1)/(i*i);
Penguino
  • 2,136
  • 1
  • 14
  • 21
0

In the age Pascal was designed, logarithms and slide rules were a commonly used tool among engineers, so much so it led to the inclusion of some basic logarithmic functions.

  • ln(i) takes the (natural) logarithm of a positive number. “Natural” refers to e, Euler’s constant.
  • exp(i) calculates Euler’s constant to the power of i, eⁱ.

You can utilize logarithmic identities to overcome limitations of integer to some degree. To use the factorial formula of the binomial coefficient you can write:

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;

I think, this is great, because it does not require you to use/load an extra library, and let’s not forget learn it. The GMP, GNU multiple precision library, for instance, is biiiiig and requires some time to get immersed with it. This way you can simply write

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

Yet the best way is of course to improve your algorithm. In this case, you are particularly interested in decreasing the magnitude for the factorial. Browsing some formulary I notice your expression looks similar to

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

So you do some substitution

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

Expand it in the original equivalence

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

Now that we got a product instead of a difference, we expand everything and merge it:

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!

This on the other hand reveals that we only need n − 2 iterations. In other words, calculating the following does not require any overhead introduced by logarithms, yet is just as precise, but also you don’t exceed the limits of integer, and last but not least it’s faster:

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

This is just the following written in a procedural style:

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

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

Neat, huh?

Kai Burghardt
  • 1,046
  • 1
  • 8
  • 23