Documentation

Mathlib.Data.Nat.Factorial.Basic

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 #

def Nat.factorial :

Nat.factorial n is the factorial of n.

Instances For
    def Nat.term_! :
    Lean.TrailingParserDescr

    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.

    Instances For
      theorem Nat.factorial_succ (n : ) :
      (n + 1).factorial = (n + 1) * n.factorial
      theorem Nat.mul_factorial_pred {n : } (hn : n 0) :
      n * (n - 1).factorial = n.factorial
      theorem Nat.factorial_ne_zero (n : ) :
      n.factorial 0
      theorem Nat.dvd_factorial {m n : } :
      0 < mm nm n.factorial
      theorem Nat.factorial_le {m n : } (h : m n) :
      theorem Nat.factorial_mul_pow_le_factorial {m n : } :
      m.factorial * (m + 1) ^ n (m + n).factorial
      theorem Nat.factorial_lt {m n : } (hn : 0 < n) :
      n.factorial < m.factorial n < m
      theorem Nat.factorial_lt_of_lt {m n : } (hn : 0 < n) (h : n < m) :
      @[simp]
      theorem Nat.one_lt_factorial {n : } :
      1 < n.factorial 1 < n
      @[simp]
      theorem Nat.factorial_eq_one {n : } :
      n.factorial = 1 n 1
      theorem Nat.factorial_inj {m n : } (hn : 1 < n) :
      n.factorial = m.factorial n = m
      theorem Nat.factorial_inj' {m n : } (h : 1 < n 1 < m) :
      n.factorial = m.factorial n = m
      theorem Nat.lt_factorial_self {n : } (hi : 3 n) :
      theorem Nat.add_factorial_succ_lt_factorial_add_succ {i : } (n : ) (hi : 2 i) :
      i + (n + 1).factorial < (i + n + 1).factorial
      theorem Nat.add_factorial_lt_factorial_add {i n : } (hi : 2 i) (hn : 1 n) :
      i + n.factorial < (i + n).factorial
      theorem Nat.add_factorial_le_factorial_add (i : ) {n : } (n1 : 1 n) :
      i + n.factorial (i + n).factorial
      theorem Nat.factorial_mul_pow_sub_le_factorial {n m : } (hnm : n m) :
      n.factorial * n ^ (m - n) m.factorial

      Ascending and descending factorials #

      def Nat.ascFactorial (n : ) :

      n.ascFactorial k = n (n + 1) ⋯ (n + k - 1). This is closely related to ascPochhammer, but much less general.

      Instances For
        @[simp]
        theorem Nat.ascFactorial_zero (n : ) :
        n.ascFactorial 0 = 1
        theorem Nat.ascFactorial_succ {n k : } :
        n.ascFactorial k.succ = (n + k) * n.ascFactorial k
        theorem Nat.zero_ascFactorial (k : ) :
        ascFactorial 0 k.succ = 0
        theorem Nat.succ_ascFactorial (n k : ) :
        n * n.succ.ascFactorial k = (n + k) * n.ascFactorial k
        theorem Nat.factorial_mul_ascFactorial (n k : ) :
        n.factorial * (n + 1).ascFactorial k = (n + k).factorial

        (n + 1).ascFactorial k = (n + k) ! / n ! but without ℕ-division. See Nat.ascFactorial_eq_div for the version with ℕ-division.

        theorem Nat.factorial_mul_ascFactorial' (n k : ) (h : 0 < n) :
        (n - 1).factorial * n.ascFactorial k = (n + k - 1).factorial

        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.

        theorem Nat.ascFactorial_eq_div (n k : ) :
        (n + 1).ascFactorial k = (n + k).factorial / n.factorial

        Avoid in favor of Nat.factorial_mul_ascFactorial if you can. ℕ-division isn't worth it.

        theorem Nat.ascFactorial_eq_div' (n k : ) (h : 0 < n) :
        n.ascFactorial k = (n + k - 1).factorial / (n - 1).factorial

        Avoid in favor of Nat.factorial_mul_ascFactorial' if you can. ℕ-division isn't worth it.

        theorem Nat.ascFactorial_of_sub {n k : } :
        (n - k) * (n - k + 1).ascFactorial k = (n - k).ascFactorial (k + 1)
        theorem Nat.pow_lt_ascFactorial' (n k : ) :
        (n + 1) ^ (k + 2) < (n + 1).ascFactorial (k + 2)
        theorem Nat.pow_lt_ascFactorial (n : ) {k : } :
        2 k(n + 1) ^ k < (n + 1).ascFactorial k
        theorem Nat.ascFactorial_le_pow_add (n k : ) :
        (n + 1).ascFactorial k (n + k) ^ k
        theorem Nat.ascFactorial_lt_pow_add (n : ) {k : } :
        2 k(n + 1).ascFactorial k < (n + k) ^ k
        theorem Nat.ascFactorial_pos (n k : ) :
        0 < (n + 1).ascFactorial k
        def Nat.descFactorial (n : ) :

        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.

        Instances For
          @[simp]
          theorem Nat.descFactorial_zero (n : ) :
          @[simp]
          theorem Nat.descFactorial_succ (n k : ) :
          n.descFactorial (k + 1) = (n - k) * n.descFactorial k
          theorem Nat.succ_descFactorial_succ (n k : ) :
          (n + 1).descFactorial (k + 1) = (n + 1) * n.descFactorial k
          theorem Nat.succ_descFactorial (n k : ) :
          (n + 1 - k) * (n + 1).descFactorial k = (n + 1) * n.descFactorial k
          @[simp]
          theorem Nat.descFactorial_eq_zero_iff_lt {n k : } :
          n.descFactorial k = 0 n < k
          @[simp]
          theorem Nat.descFactorial_pos {n k : } :
          0 < n.descFactorial k k n
          theorem Nat.descFactorial_of_lt {n k : } :
          n < kn.descFactorial k = 0

          Alias of the reverse direction of Nat.descFactorial_eq_zero_iff_lt.

          theorem Nat.factorial_mul_descFactorial {n k : } :
          k n(n - k).factorial * n.descFactorial k = n.factorial

          n.descFactorial k = n! / (n - k)! but without ℕ-division. See Nat.descFactorial_eq_div for the version using ℕ-division.

          theorem Nat.descFactorial_mul_descFactorial {k m n : } (hkm : k m) :
          (n - k).descFactorial (m - k) * n.descFactorial k = n.descFactorial m
          theorem Nat.descFactorial_eq_div {n k : } (h : k n) :

          Avoid in favor of Nat.factorial_mul_descFactorial if you can. ℕ-division isn't worth it.

          theorem Nat.descFactorial_le (n : ) {k m : } (h : k m) :
          theorem Nat.pow_sub_le_descFactorial (n k : ) :
          (n + 1 - k) ^ k n.descFactorial k
          theorem Nat.pow_sub_lt_descFactorial' {n k : } :
          k + 2 n(n - (k + 1)) ^ (k + 2) < n.descFactorial (k + 2)
          theorem Nat.pow_sub_lt_descFactorial {n k : } :
          2 kk n(n + 1 - k) ^ k < n.descFactorial k
          theorem Nat.descFactorial_lt_pow {n : } (hn : n 0) {k : } :
          2 kn.descFactorial k < n ^ k
          theorem Nat.factorial_two_mul_le (n : ) :
          (2 * n).factorial (2 * n) ^ n * n.factorial

          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
          

          Similarly, evaluation of ascFactorial 100 15000 fails with the naive implementation but works with the binary recursion.

          We could implement a tail-recursive version (or just use Nat.fold), but instead let's jump straight to binary splitting.

          @[irreducible]
          def Nat.ascFactorialBinary (n k : ) :

          ascFactorial implemented using binary splitting.

          While this still performs the same number of multiplications, the big-integer operands to each are much smaller.

          Instances For
            def Nat.factorialBinarySplitting (n : ) :

            Factorial implemented using binary splitting.

            While this still performs the same number of multiplications, the big-integer operands to each are much smaller.

            Instances For
              @[deprecated Nat.ascFactorialBinary (since := "2025-10-21")]
              def Nat.factorialBinarySplitting.prodRange (lo hi : ) :
              autoParam (lo < hi) prodRange._auto_1

              This function was used in the definition of factorialBinarysplitting before it was migrated to ascFactorialBinary.

              Instances For
                @[deprecated Nat.factorial_mul_ascFactorial (since := "2025-10-21")]
                theorem Nat.factorialBinarySplitting.factorial_mul_prodRange (lo hi : ) (h : lo < hi) :
                lo.factorial * prodRange (lo + 1) (hi + 1) = hi.factorial
                @[deprecated Nat.factorial_eq_factorialBinarySplitting (since := "2025-10-21")]

                Alias of Nat.factorial_eq_factorialBinarySplitting.

                def Nat.descFactorialBinary (n k : ) :

                descFactorial implemented using binary splitting.

                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