Skip to content

Commit 8492de7

Browse files
committed
add binomialCoefficient
1 parent 10be2e5 commit 8492de7

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

source/mir/math/numeric.d

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,62 @@ unittest
578578
assert(cast(double) factorial(1, 100) == 100);
579579
assert(approxEqual(cast(double) factorial(100, 1000), iota([100], 1000).prod!double));
580580
}
581+
582+
/++
583+
Quickly computes binomial coefficient using extended
584+
precision floating point type $(MREF mir,bignum,fp).
585+
586+
Params:
587+
DivisionType = the type used for a single division at the final.
588+
By defualt, uses `real` precision. TODO: full `Fp` precision.
589+
n = number elements in the set
590+
k = number elements in the subset
591+
Returns: n choose k
592+
Complexity: O(min(k, n - k))
593+
+/
594+
template binomialCoefficient(DivisionType = void)
595+
if (is(DivisionType == void) || __traits(isFloating, DivisionType))
596+
{
597+
auto binomialCoefficient
598+
(size_t coefficientSize = 128, Exp = sizediff_t)
599+
(ulong n, uint k)
600+
if (coefficientSize % (size_t.sizeof * 8) == 0 && coefficientSize >= (size_t.sizeof * 8))
601+
in (k <= n)
602+
{
603+
import mir.bignum.fp: Fp;
604+
605+
alias R = Fp!(coefficientSize, Exp);
606+
607+
// TODO: For DivisionType == void
608+
// use full precision division for void when Fp division is ready
609+
static if (is(DivisionType == void))
610+
alias T = real;
611+
else
612+
alias T = DivisionType;
613+
614+
if (k > n - k)
615+
k = cast(uint)(n - k);
616+
617+
auto a = factorial!(coefficientSize, Exp)(k, n - k + 1);
618+
auto b = factorial!(coefficientSize, Exp)(k);
619+
auto exponent = a.exponent - b.exponent;
620+
a.exponent = b.exponent = -coefficientSize;
621+
auto ret = R(cast(T) a / cast(T) b);
622+
ret.exponent += exponent;
623+
return ret;
624+
}
625+
}
626+
627+
///
628+
version(mir_test)
629+
@safe pure nothrow @nogc
630+
unittest
631+
{
632+
import mir.bignum.fp: Fp;
633+
import mir.math.common: approxEqual;
634+
635+
static assert(is(typeof(binomialCoefficient!double(30, 18)) == Fp!128), typeof(binomialCoefficient!double(30, 18)).stringof);
636+
static assert(cast(double) binomialCoefficient(30, 18) == 86493225);
637+
638+
assert(approxEqual(cast(double) binomialCoefficient(100, 40), 1.374623414580281150126736972e+28));
639+
}

0 commit comments

Comments
 (0)