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? :)
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? :)
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);
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?