Zulip Chat Archive
Stream: new members
Topic: Simple proof to show Lean tactics ((n+1)! - n! = n(n!))
Isak Colboubrani (Dec 17 2024 at 11:11):
I am attempting to construct a simple proof to illustrate for absolute beginners (fellow undergraduates) that one can choose between using Lean tactics and performing manual step-by-step rewriting.
import Mathlib
-- Prove the identity: (n+1)! - n! = n(n!)
-- More verbose method
example (n : ℕ) : Nat.factorial (n + 1) - Nat.factorial n = n * (Nat.factorial n) := by
rw [Nat.factorial_succ]
rw [add_mul]
rw [one_mul]
rw [add_tsub_cancel_right]
-- Prove the identity: (n+1)! - n! = n(n!)
-- Less verbose method
example (n : ℕ) : Nat.factorial (n + 1) - Nat.factorial n = n * (Nat.factorial n) := by
rw [Nat.factorial_succ]
ring_nf
simp
Are the examples idiomatic? How can they be improved? Can the less verbose version be shortened further?
Kevin Buzzard (Dec 17 2024 at 11:16):
open scoped Nat
gives you notation:
example (n : ℕ) : (n + 1)! - n ! = n * n ! := by
Factorial notation is a bit hairy though, because n!
doesn't work.
I would not have chosen this example for beginners though because it involves natural subtraction, which is why tactics are struggling.
Kevin Buzzard (Dec 17 2024 at 11:23):
Here's a toy example for beginners:
import Mathlib
open Finset
example (n : ℕ) : ∑ i in range n, (2 * i + 1) = n^2 := by
induction n with
| zero =>
simp
| succ n IH =>
rw [sum_range_succ, IH]
ring
If you remove the natural subtraction from your original question by moving the n! onto the other side, it becomes easier:
import Mathlib
open Nat
example (n : ℕ) : (n + 1)! = n ! + n * n ! := by
rw [factorial_succ]
ring
Isak Colboubrani (Dec 17 2024 at 13:23):
That is a great example for beginners. Thanks!
Yes, the !
notation is a bit hard to explain to beginners.
I might convince someone that n!
is written as n !
, but after doing so I have the problem of describing why (n + 1)!
cannot be written as (n + 1) !
(in all contexts).
What is the best way to explain that in a way that makes sense for a mathematics undergraduate?
We have |
and \mid
: could something similar be introduced to separate !
and say \fact
?
Mario Carneiro (Dec 17 2024 at 13:24):
Isak Colboubrani said:
I might convince someone that
n!
is written asn !
, but after doing so I have the problem of describing why(n + 1)!
cannot be written as(n + 1) !
.
Is that so? These should both work
Ruben Van de Velde (Dec 17 2024 at 13:26):
Yeah, either is possible if there's a parenthesis, but not if it's just an identifier
Isak Colboubrani (Dec 17 2024 at 13:27):
In this context:
import Mathlib
open scoped Nat
-- Works.
example (n : ℕ) : (n + 1)! - (n !) = n * (n !) := by
rw [Nat.factorial_succ]
ring_nf
simp
-- Does not work.
example (n : ℕ) : (n + 1) ! - (n !) = n * (n !) := by
rw [Nat.factorial_succ]
ring_nf
simp
-- Works.
example (n : ℕ) : ((n + 1) !) - (n !) = n * (n !) := by
rw [Nat.factorial_succ]
ring_nf
simp
Ruben Van de Velde (Dec 17 2024 at 13:28):
Wait what-
example (n : ℕ) : ((n + 1) !) - (n !) = n * (n !) := by
also works. Is the !
interfering with the -
? Or is it reading - (n !)
as (- (n !))
?
Mario Carneiro (Dec 17 2024 at 13:31):
this is because of the funky parsing of prefix !
(boolean not)
Mario Carneiro (Dec 17 2024 at 13:31):
I think that should be fixed
Mario Carneiro (Dec 17 2024 at 13:32):
(n + 1) ! - (n !)
gets parsed as (n + 1) (! (- (n !)))
Mario Carneiro (Dec 17 2024 at 13:32):
By the way, if you want to make this look more regular, be aware that an alternative to n !
is (n)!
Mario Carneiro (Dec 17 2024 at 13:37):
I would really like to implement a parser for n!
which does the right thing, but this will require changes to lean core, so maybe I can interest @Kyle Miller ? The idea is to make it so that in an expression like foo!
or (e).foo!
, if foo!
does not resolve as an identifier (there is no declaration with this name, in the namespace of e
's type in the latter case), and there is a syntax term "!" : term
syntax rule, then split the identifier as "foo" + "!"
and try again. Ditto for ?
suffixes.
Yaël Dillies (Dec 17 2024 at 13:39):
That sounds like a great solution from the usability POV, I must say!
Mario Carneiro (Dec 17 2024 at 13:39):
(lean already has to do something similar for foo.bar
which can be either a namespaced name or (foo).bar
)
Kyle Miller (Dec 17 2024 at 18:26):
You mean a parser like this @Mario Carneiro?
Kyle Miller (Dec 17 2024 at 18:28):
In another direction, lean4#5824 and lean4#5826 were experiments in modifying the !
bool negation notation. I think both of these would fix the "does not work" example in
Mario Carneiro (Dec 17 2024 at 18:29):
yes exactly. I have reasons to believe that that elaborator is insufficient because identifiers can be used in more ways than just in elabIdent
, plus it's probably a performance issue to implement in that way
Isak Colboubrani (Jan 28 2025 at 23:59):
Are lean4#5824 and lean4#5826 the right places to follow the progress on this suggested change?
Kyle Miller (Jan 29 2025 at 03:44):
Yes @Isak Colboubrani, for now this Zulip thread and those two PRs are the current state of looking into the issue.
Last updated: May 02 2025 at 03:31 UTC