Factorial and variants #
This file defines the factorial, along with the ascending and descending variants.
For the proof that the factorial of n
counts the permutations of an n
-element set,
see Fintype.card_perm
.
Main declarations #
Nat.factorial
: The factorial.Nat.ascFactorial
: The ascending factorial. It is the product of natural numbers fromn
ton + k - 1
.Nat.descFactorial
: The descending factorial. It is the product of natural numbers fromn - k + 1
ton
.
Nat.factorial n
is the factorial of n
.
Instances For
factorial notation (n)!
for Nat.factorial n
.
In Lean, names can end with exclamation marks (e.g. List.get!
), so you cannot write
n!
in Lean, but must write (n)!
or n !
instead. The former is preferred, since
Lean can confuse the !
in n !
as the (prefix) boolean negation operation in some
cases.
For numerals the parentheses are not required, so e.g. 0!
or 1!
work fine.
Todo: replace occurrences of n !
with (n)!
in Mathlib.
Equations
- Nat.term_! = Lean.ParserDescr.trailingNode `Nat.term_! 10000 0 (Lean.ParserDescr.symbol "!")
Instances For
Ascending and descending factorials #
n.ascFactorial k = n (n + 1) ⋯ (n + k - 1)
. This is closely related to ascPochhammer
, but
much less general.
Equations
- n.ascFactorial 0 = 1
- n.ascFactorial n_1.succ = (n + n_1) * n.ascFactorial n_1
Instances For
(n + 1).ascFactorial k = (n + k) ! / n !
but without ℕ-division. See
Nat.ascFactorial_eq_div
for the version with ℕ-division.
n.ascFactorial k = (n + k - 1)! / (n - 1)!
for n > 0
but without ℕ-division. See
Nat.ascFactorial_eq_div
for the version with ℕ-division. Consider using
factorial_mul_ascFactorial
to avoid complications of ℕ-subtraction.
Avoid in favor of Nat.factorial_mul_ascFactorial
if you can. ℕ-division isn't worth it.
Avoid in favor of Nat.factorial_mul_ascFactorial'
if you can. ℕ-division isn't worth it.
n.descFactorial k = n! / (n - k)!
(as seen in Nat.descFactorial_eq_div
), but
implemented recursively to allow for "quick" computation when using norm_num
. This is closely
related to descPochhammer
, but much less general.
Equations
- n.descFactorial 0 = 1
- n.descFactorial n_1.succ = (n - n_1) * n.descFactorial n_1
Instances For
Alias of the reverse direction of Nat.descFactorial_eq_zero_iff_lt
.
n.descFactorial k = n! / (n - k)!
but without ℕ-division. See Nat.descFactorial_eq_div
for the version using ℕ-division.
Avoid in favor of Nat.factorial_mul_descFactorial
if you can. ℕ-division isn't worth it.
Factorial via binary splitting. #
We prove this is equal to the standard factorial and mark it @[csimp]
.
We could proceed further, with either Legendre or Luschny methods.
This is the highest factorial I can #eval
using the naive implementation without a stack overflow:
/-- info: 114716 -/
#guard_msgs in
#eval 9718 ! |>.log2
We could implement a tail-recursive version (or just use Nat.fold
),
but instead let's jump straight to binary splitting.
Factorial implemented using binary splitting.
While this still performs the same number of multiplications, the big-integer operands to each are much smaller.
Equations
- n.factorialBinarySplitting = if x : n = 0 then 1 else Nat.factorialBinarySplitting.prodRange 1 (n + 1) ⋯
Instances For
We are now limited by time, not stack space, and this is much faster than even the tail-recursive version.
#time -- Less than 1s. (Tail-recursive version takes longer for `(10^5) !`.)
#eval (10^6) ! |>.log2