Zulip Chat Archive
Stream: new members
Topic: How to improve my proof?
Viliam Vadocz (Nov 30 2024 at 19:46):
I am trying to get some practice with Lean so I proved that two functions are equivalent. This took me quite a while and it still looks very messy (to my eyes). Surely there is some tactic that would simplify all the algebra so I can just focus on the main ideas. I tried using the ring
tactic but I could not get it to work. I also found myself just replacing rw
by simp
and removing things by trial and error to remove steps.
- How could I prove this more elegantly?
- When and how to use
simp
? - Is there a way to
nth_rewrite
inside the square brackets of somerw
?
import Mathlib.Algebra.BigOperators.Group.List
def f n := List.sum (List.range n)
def g n := n * (n - 1) / 2
lemma square_gt (n : Nat): n ≤ n * n := by
induction n with
| zero => decide
| succ k ih => simp [Nat.mul_succ]
theorem f_equals_g (n : Nat): f n = g n := by
induction n with
| zero => rfl
| succ k ih => calc f (k + 1)
_ = f k + k := by simp [f, List.range_succ, List.sum_append]
_ = g k + k := by rw [ih]
_ = k * (k - 1) / 2 + k := by rw [g]
_ = (k * (k - 1) + 2 * k) / 2 := by rw [← Nat.add_mul_div_left _ _ (by decide)]
_ = (k + 1) * k / 2 := by rw [Nat.mul_sub, Nat.add_mul,
Nat.two_mul, Nat.mul_one, Nat.one_mul,
← Nat.add_assoc, Nat.sub_add_cancel (square_gt k)]
_ = g (k + 1) := by simp [g]
Kevin Buzzard (Nov 30 2024 at 19:52):
Your definition of g
is pretty horrible because it is using Nat.sub
and Nat.div
which are not mathematical subtraction or division (e.g. 1 - 0 = 0
, 5 / 2 = 2
). Hence you can fully expect some pain. I would even go so far as to argue that the definition of g
is not actually what you mean it to be. If you coerce to the rationals then your definition agrees with what a mathematician would mean by the definition:
import Mathlib.Tactic
def f n := List.sum (List.range n)
def g (n : ℕ) : ℚ := n * (n - 1) / 2
and the proof will be much simpler (and automation will be more helpful).
Viliam Vadocz (Nov 30 2024 at 19:57):
That is a good point. I'll try with that definition to see if it is easier.
Kevin Buzzard (Nov 30 2024 at 20:04):
by simp [f, List.range_succ] -- `List.sum_append` is already a `simp` lemma
is pretty much the only comment I have on your code; that's a fine time to use simp
. As for the gnarly bit at the end, it's hard to get automation to help you because your goal has this Nat.div
in it, I think in your case I would have bashed it out like you did. Great effort getting it out by the way!
Viliam Vadocz (Nov 30 2024 at 20:12):
Wow that was much easier (or maybe I improved since my last attempt?):
import Mathlib.Algebra.BigOperators.Group.List
import Mathlib.Tactic
def f n := List.sum (List.range n)
def g (n : ℕ) : ℚ := n * (n - 1) / 2
theorem f_equals_g (n : Nat): f n = g n := by
induction n with
| zero => simp [f, g]
| succ k ih => calc ↑(f (k + 1))
_ = ↑(f k + k) := by simp [f, List.range_succ]
_ = g k + k := by simp [ih]
_ = k * (k - 1) / 2 + k := by rw [g]
_ = (k + 1) * k / 2 := by ring
_ = g (k + 1) := by simp [g]
Ilmārs Cīrulis (Nov 30 2024 at 20:25):
My attempt at original statement with natural numbers only (with n * (n - 1) / 2
changed to (n * n - n) / 2
).
import Mathlib
def f n := List.sum (List.range n)
def g (n: ℕ) := (n * n - n) / 2
lemma aux (n : Nat): 2 * f n + n = n * n := by
induction n with
| zero => rfl
| succ k ih =>
simp [f, List.range_succ] at *
ring_nf at *
omega
theorem f_equals_g (n : Nat): f n = g n := by
have H := aux n
have H0 := Nat.le_mul_self n
simp [g]
omega
Viliam Vadocz (Nov 30 2024 at 20:34):
omega
seems pretty powerful! Definitely something to keep in mind for future problems.
Kevin Buzzard (Nov 30 2024 at 23:42):
theorem f_equals_g (n : Nat): f n = g n := by -- this is with g Rat-valued
induction n with
| zero => simp [f, g]
| succ k ih =>
simp [f,g] at ih
simp [f,g, List.range_succ, ih]
ring
Last updated: May 02 2025 at 03:31 UTC