home *** CD-ROM | disk | FTP | other *** search
- (*:Version: Mathematica 2.0 *)
-
- (*:Name: NumberTheory`Binomial` *)
-
- (*:Title: Binomial coefficients *)
-
- (*:Author: Ilan Vardi
-
- *)
-
- (*:Keywords: Binomial coefficients, discrete mathematics, factorials *)
-
- (*:Requirements: none. *)
-
- (*:Warnings: none *)
-
- (* Sources: P. Goetgheluck, ``Prime Divisors of Binomial Coefficients,''
- Mathematics of Computation #51 (1988), p. 325-329.
-
- I. Vardi, ``Computational Recreations in Mathematica,''
- Addison Wesley 1991.
- *)
-
- (*:Limitations: FastBinomial is more efficient than the built-in
- Binomial only for arguments with hundreds of digits.
-
- FastFactorial is more efficient than the built in
- Factorial only for numbers of thousands of digits.
-
- BinomialMod requires a prime modulus
- *)
-
- (*:Discussion: This package implements fast evaluation of binomial
- coefficients. The evaluation of the standard binomial
- coefficients is done by using their prime factorization
- (see the references above). This reduces the number of
- multiplications by a factor of log.
- In the case of Binomial[2 n, n] one has an even better
- algorithm due to its explicit factorization.
- These algorithms require the function PrimePi[x].
-
- The fast evaluation of Mod[Binomial[n, k], p] is
- done via a well known theorem of E. Lucas (see
- Computational Recreations, page 70) which reduces
- the computation to n, k less than p. This is done
- as before, but with all products in the computation
- being taken mod p.
- *)
-
- BeginPackage["NumberTheory`Binomial`"]
-
- FastBinomial::usage = "FastBinomial[n, k] returns Binomial[n, k]
- efficiently for values of n and k with hundreds of digits."
-
- FastFactorial::usage = "FastFactorial[n] return n! efficiently
- for n having thousands of digits."
-
- BinomialMod::usage = "BinomialMod[n, k, p] returns Mod[Binomial[n, k], p]
- efficiently, where p is a prime number."
-
- Begin["`Private`"]
-
- FastBinomial[n_, k_]:= Binomial[n, k] /; k < 500
-
- FastBinomial[n_, k_]:= FastBinomial[n, n - k] /; 2 k > n
-
- FastBinomial[n_, k_]:=
- (Times @@
- (#^(Quotient[n, #] - Quotient[k, #] -
- Quotient[n - k, #])& /@
- Prime[Range[PrimePi[N[Sqrt[n]]] + 1,
- PrimePi[n]]])) *
- Times @@ (#^(Plus @@
- (Block[{r = #^Range[Log[n]/Log[#]]},
- Quotient[n, r] - Quotient[k, r] -
- Quotient[n - k, r]]))& /@
- Prime[Range[PrimePi[N[Sqrt[n]]]]]) /; n != 2 k
-
- FastBinomial[n_, k_]:=
- Times @@ Prime[Range[PrimePi[k] + 1,
- PrimePi[n]]] *
- Times @@ Prime[Range[PrimePi[k/2] + 1,
- PrimePi[2 k/3]]] *
- (Times @@
- (#^(Quotient[2 k, #] - 2 Quotient[k , #]) & /@
- Prime[Range[PrimePi[N[Sqrt[2k]]] + 1,
- PrimePi[2 k/5]]])) *
- Times @@
- (#^(Plus @@ (Block[{r = #^Range[Log[2 k]/Log[#]]},
- Quotient[2 k, r] - 2 Quotient[k, r]]))& /@
- Prime[Range[PrimePi[N[Sqrt[2 k]]]]]) /;
- n == 2 k
-
- FastFactorial[0]= 1
-
- FastFactorial[n_]:=
- If[OddQ[n],
- n FastFactorial[n-1],
- FastBinomial[n, n/2] FastFactorial[n/2]^2]
-
- (* Modular calculations: ProductMod gives the product of a list of
- numbers mod p keeping intermediate results small *)
-
- ProductMod[list_, m_]:= Fold[Mod[#1 #2, m]&, 1, list]
-
- BinomialMod[n_, k_, p_]:= Mod[Binomial[n, k], p] /; n <= 100
-
- Attributes[BinomialMod]= {Listable}
-
- BinomialMod[n_, k_, p_]:= 0 /; n < k
-
- BinomialMod[n_, k_, p_]:= BinomialMod[n, n - k, p] /; 2 k > n
-
- t[n_, k_, p_]:= Block[{tt =
- {BinomialMod[n, k , p]//Timing,
- Mod[Binomial[n, k], p]//Timing}},
- {tt[[1, 1]], tt[[2, 1]], tt[[1, 2]] == tt[[2,2]]}]
-
- (* If n < p then use above algorithms with modular multiplications *)
-
- BinomialMod[n_, k_, p_]:=
- Mod[ProductMod[
- (#^(Quotient[n, #] - Quotient[k , #] -
- Quotient[n - k, #])& /@
- Prime[Range[PrimePi[N[Sqrt[n]]] + 1,
- PrimePi[n]]]), p] *
- ProductMod[(#^(Plus @@
- (Block[{r = #^Range[Log[n]/Log[#]]},
- Quotient[n, r] - Quotient[k, r] -
- Quotient[n - k, r]]))& /@
- Prime[Range[PrimePi[N[Sqrt[n]]]]]), p],
- p] /; n < p && n != 2 k
-
- BinomialMod[n_, k_, p_]:=
- Mod[
- ProductMod[Prime[Range[PrimePi[k] + 1, PrimePi[n]]], p] *
- ProductMod[Prime[Range[PrimePi[k/2] + 1, PrimePi[2 k/3]]], p] *
- ProductMod[
- (#^(Quotient[2 k, #] - 2 Quotient[k , #]) & /@
- Prime[Range[PrimePi[N[Sqrt[2k]]] + 1,
- PrimePi[2 k/5]]]), p] *
- ProductMod[
- (#^(Plus @@ (Block[{r = #^Range[Log[2 k]/Log[#]]},
- Quotient[2 k, r] - 2 Quotient[k, r]]))& /@
- Prime[Range[PrimePi[N[Sqrt[2 k]]]]]), p],
- p] /; n < p && n == 2 k
-
-
- (* For n >= p use Lucas' theorem *)
-
- BinomialMod[n_, k_, p_]:=
- Block[{kd = IntegerDigits[k, p], nkd = IntegerDigits[n - k, p],
- nd, min},
- min = Min[Length[kd], Length[nkd]];
- If[Select[Take[nkd, -min] + Take[kd, -min], # >= p&] != {},
- Return[0]];
- nd = IntegerDigits[n, p];
- min = Min[Length[nd], Length[kd]];
- ProductMod[BinomialMod[Take[nd, -min], Take[kd, -min], p], p]]
-
- End[] (* NumberTheory`Binomial` *)
-
- Protect[FastBinomial, FastFactorial, BinomialMod]
-
- EndPackage[] (* NumberTheory`Binomial` *)
-
- (* Tests:
-
- Test[FastBinomial[1000, 501] == Binomial[1000, 501],
- True
- ]
-
- Test[FastBinomial[2000, 1000] == Binomial[2000, 1000],
- True
- ]
-
- Test[FastFactorial[1000] == 1000!,
- True
- ]
-
- Test[FastFactorial[5000] == 5000!,
- True
- ] (* Crossover point seems to be 2,500 digits *)
-
- Test[BinomialMod[200, 100, 19],
- 6
- ]
-
- Test[BinomialMod[200, 99, 19],
- 5
- ]
-
- Test[BinomialMod[200, 100, 97],
- 40
- ]
-
- Test[BinomialMod[2 10^4, 10^4, 65537],
- 4351
- ]
-
- Test[BinomialMod[2 10^5, 10^5, Prime[10^8]],
- 1194079563
- ] (* 45 seconds on a DEC3100 *)
-
- Test[BinomialMod[2 10^6, 10^6, 10^64 + 57],
- 8325550413494218558772064233832167627413775156731422716387786753
- ] (* 10 minutes on a DEC3100 *)
-
-
- *)
-
-
-