-- 目标: a + (b + 0) + (c + 0) = a + b + c
rw [add_zero] -- 按照加法定义第一条重写得: a + b + (c + 0) = a + b + c
rw [add_zero] -- 按照加法定义第一条重写得: a + b + c = a + b + c
rfl -- 由等式的反身性,证明完毕!
第6/8关:介绍了rw的进阶用法
Text Only
1234
-- 目标: a + (b + 0) + (c + 0) = a + b + c
rw [add_zero c] -- 按照加法定义第一条重写c+0=c得: a + (b + 0) + c = a + b + c
rw [add_zero] -- 按照加法定义第一条重写得: a + b + c = a + b + c
rfl -- 由等式的反身性,证明完毕!
Oh no! On the way to add_comm, a wild succ_add appears.
succ_add a b is the proof that (succ a) + b = succ (a + b) for a and b numbers.
This result is what's standing in the way of x + y = y + x.
Again we have the problem that we are adding b to things,
so we need to use induction to split into the cases where b = 0 and b is a successor.
You can make your own tactics in Lean. This code here
Text Only
123
macro "simp_add" : tactic => `(tactic|(
simp only [add_assoc, add_left_comm, add_comm]))
was used to create a new tactic simp_add, which runs simp only [add_assoc, add_left_comm, add_comm]. Try running simp_add to solve this level!
Text Only
12345
apply Subset.antisymm
intro x
intro h
rewrite[mem_compl_iff] at h
push_neg at h
Text Only
123456
apply Subset.antisymm
intro x
intro h
rewrite[mem_compl_iff] at h
by_contra h1
exact h h1
Text Only
12
constructor
exact compl_subset_compl_of_subset
Text Only
1234
apply Iff.intro
intro h
apply compl_subset_compl_of_subset at h
exact h
Text Only
12345
intro x
intro h3
have h4 : x ∈ B := h1 h3
have h5 : x ∈ C := h2 h3
apply And.intro h4 h5
Text Only
1234
intro x
intro h
rewrite[mem_inter_iff] at h
exact And.intro h.right h.left
Text Only
12345
apply Iff.intro
intro h1
rewrite[mem_inter_iff] at h1
rewrite[mem_inter_iff] at h1
rewrite[mem_inter_iff]
Text Only
1 2 3 4 5 6 7 8 910
Objects:
U: Type
x: U
AB: Set U
Assumptions:
h1: x ∈ A
h2: x ∈ B
Goal:
x ∈ A ∩ B
apply And.intro h1 h2
Text Only
12
h: x ∈ A ∩ B ∧ x ∈ C
have h1 : x ∈ C := h
Text Only
1 2 3 4 5 6 7 8 910
Objects:
U: Type
ABC: Set U
x: U
Assumptions:
h1: x ∈ C
h2: x ∈ A
h3: x ∈ B
Goal:
x ∈ A ∧ x ∈ B ∧ x ∈ C
Text Only
1 2 3 4 5 6 7 8 9101112
ext x
apply Iff.intro
intro h
rewrite[mem_inter_iff] at h
have h1 : x ∈ C := h.right
rewrite[mem_inter_iff] at h
have h2 : x ∈ A := h.left.left
have h3 : x ∈ B := h.left.right
rewrite[mem_inter_iff]
rewrite[mem_inter_iff]
have hAB : x ∈ A ∧ x ∈ B := And.intro h2 h3
exact And.intro h2 (And.intro h3 h1)
Text Only
1 2 3 4 5 6 7 8 91011
ext x
apply Iff.intro
intro h
rewrite[mem_inter_iff] at h
rewrite[mem_inter_iff] at h
rewrite[mem_inter_iff]
rewrite[mem_inter_iff]
have h1 : x ∈ A := h.left.left
have h2 : x ∈ B := h.left.right
have h3 : x ∈ C := h.right
exact And.intro h1 (And.intro h2 h3)
Text Only
123456789
intro h
rewrite[mem_inter_iff] at h
rewrite[mem_inter_iff] at h
rewrite[mem_inter_iff]
rewrite[mem_inter_iff]
have h1 : x ∈ A := h.left
have h2 : x ∈ B := h.right.left
have h3 : x ∈ C := h.right.right
exact And.intro (And.intro h1 h2) h3
Text Only
12345678
Objects:
U: Type
AB: Set U
x: U
Assumptions:
h: x ∈ B
Goal:
x ∈ A ∨ x ∈ B
Text Only
1 2 3 4 5 6 7 8 910
ext x
rewrite [mem_compl_iff, mem_union]
rewrite [mem_inter_iff, mem_compl_iff, mem_compl_iff]
apply Iff.intro
intro h
push_neg at h
exact h
intro h
push_neg
exact h
Text Only
1 2 3 4 5 6 7 8 91011121314151617
ext x
apply Iff.intro
intro h
rewrite[mem_compl_iff] at h
rewrite[mem_union] at h
rewrite[mem_inter_iff]
rewrite[mem_compl_iff]
rewrite[mem_compl_iff]
push_neg at h
exact h
intro h
rewrite[mem_inter_iff] at h
rewrite[mem_compl_iff] at h
rewrite[mem_compl_iff]
rewrite[mem_union]
push_neg
exact h
Text Only
1234567
ext x
rewrite[mem_inter_iff]
rewrite[mem_union]
rewrite[mem_union]
rewrite[mem_inter_iff]
apply Iff.intro
intro h
Text Only
12345678
ext x
apply Iff.intro
intro h
rewrite [mem_inter_iff] at h
rewrite [mem_union]
rewrite [mem_union] at hBC
rcases hBC with hB | hC
apply Or.inl
ext x
apply Iff.intro
intro h
rewrite [mem_inter_iff] at h
rewrite [mem_union]
have hBC : x ∈ B ∪ C := h.right
rewrite [mem_union] at hBC
rcases hBC with hB | hC
apply Or.inl
exact And.intro h.left hB
apply Or.inr
exact And.intro h.left hC
intro h
rewrite[mem_union] at h
rewrite[mem_inter_iff]
rcases h with hB | hC
rewrite[mem_inter_iff] at hB
apply And.intro
exact hB.left
rewrite[mem_union]
exact Or.inl hB.right
rewrite [mem_inter_iff] at hC
apply And.intro
exact hC.left
rewrite[mem_union]
exact Or.inr hC.right
intro x
intro h3
have h4 : x ∈ A ∪ C
rewrite[mem_union]
exact Or.inl h3
have h5 : x ∈ B ∪ C := h1 h4
rewrite [mem_union] at h5
rcases h5 with h5a | h5b
exact h5a
have h6 : x ∈ A ∩ C
rewrite [mem_inter_iff]
exact And.intro h3 h5b
have h7 : x ∈ B ∩ C := h2 h6
rewrite [mem_inter_iff] at h7
exact h7.left
Text Only
1234
intro x
rewrite[mem_sInter]
intro h2
exact h2 A h1
Text Only
12345678
intro x
rewrite[mem_sInter]
rewrite[mem_sInter]
intro h2
intro t
intro h3
have h4 : t ∈ G := h1 h3
exact h2 t h4
ext x
rewrite [mem_inter_iff]
rewrite [mem_sInter]
apply Iff.intro
intro h
intro t
rewrite[mem_pair]
intro h1
rcases h1 with h1a | h1b
rewrite[h1a]
exact h.left
rewrite[h1b]
exact h.right
intro h
constructor
have h1 : A ∈ {A, B}
rewrite [mem_pair]
apply Or.inl
rfl
exact h A h1
have h1 : B ∈ {A, B}
rewrite [mem_pair]
apply Or.inr
rfl
exact h B h1
Text Only
12345678
ext x
rewrite[mem_sInter]
rewrite[mem_inter_iff]
rewrite[mem_sInter]
rewrite[mem_sInter]
constructor
intro h
constructor
Text Only
1 2 3 4 5 6 7 8 9101112131415161718192021222324
ext x
rewrite[mem_sInter]
rewrite[mem_inter_iff]
rewrite[mem_sInter]
rewrite[mem_sInter]
constructor
intro h
constructor
intro t
intro ht
have htf : t ∈ F ∪ G
apply Or.inl ht
exact h t htf
intro t
intro ht
have htf : t ∈ F ∪ G
apply Or.inr ht
exact h t htf
intro h
intro t
intro ht
rcases ht with ht1 | ht2
exact h.left t ht1
exact h.right t ht2
Text Only
1 2 3 4 5 6 7 8 91011121314151617
constructor
intro h
intro s
intro hs
intro x
intro hx
have x_in_sInter : x ∈ ⋂₀ F := h hx
rewrite[mem_sInter] at x_in_sInter
exact x_in_sInter s hs
intro h
intro x
intro hx
rewrite[mem_sInter]
intro t
intro ht
have ha : A ⊆ t := h t ht
exact ha hx
Text Only
1 2 3 4 5 6 7 8 9101112131415161718
intro x
rewrite[mem_sInter]
intro h2
rewrite[mem_union]
by_cases hA : x ∈ A
exact Or.inl hA
right
rewrite[mem_sInter]
intro s
intro hs
have hAs : A ∪ s ∈ G := h1 s hs
have hx_As : x ∈ A ∪ s := h2 (A ∪ s) hAs
rewrite[mem_union] at hx_As
rcases hx_As with hx1 | hx2
by_contra h3
exact hA hx1
exact hx2
ext x
rewrite[mem_sUnion]
rewrite[mem_union]
rewrite[mem_sUnion]
rewrite[mem_sUnion]
constructor
intro h
obtain ⟨s, hs⟩ := h
obtain ⟨hsFG, hxs⟩ := hs
rcases hsFG with hF | hG
left
use s
right
use s
intro h
rcases h with hF | hG
obtain ⟨s, hs⟩ := hF
obtain ⟨hsF, hxs⟩ := hs
use s
rewrite[mem_union]
constructor
left
exact hsF
exact hxs
obtain ⟨s, hsG⟩ := hG
use s
constructor
rewrite[mem_union]
exact Or.inr hsG.left
exact hsG.right
Text Only
123456789
ext x
constructor
intro h
obtain ⟨hxA, hxF⟩ := h
obtain ⟨s, hsF⟩ :=hxF
use A ∩ s
have h1 : ∃ u ∈ F, A ∩ s = A ∩ u
use s
constructor
ext x
apply Iff.intro
intro h1
rewrite [mem_sInter]
intro t
intro h2
rewrite [mem_setOf] at h2
rewrite [mem_compl_iff] at h1
rewrite [mem_sUnion] at h1
push_neg at h1
have h3 := h1 tᶜ h2
rewrite [mem_compl_iff] at h3
push_neg at h3
exact h3
rewrite [mem_sInter] at h1
rewrite [mem_compl_iff]
rewrite [mem_sUnion]
push_neg
intro t
intro h2
have h3 := h1 tᶜ
have h3a : tᶜ ∈ {A | Aᶜ ∈ F}
rewrite [mem_setOf]
rewrite [compl_compl]
exact h2
have h4 := h3 h3a
rewrite [mem_compl_iff] at h4
Text Only
12
unfold Not at hnP
exact hnP hP
Text Only
1
rw[not_and_or] at h1
Text Only
12345
Objects:
A B: Prop
Assumptions:
h: B ↔ A
Goal: A ↔ B
Text Only
12345
Objects:
A: Prop
Assumptions:
h4: A ↔ ¬A
Goal:False
Text Only
123456789
Assumptions:
hAKn: A said A.isKnave
h: A.isKnight
cases isKnight_or_isKnave A
have hKnave : A.isKnave := knight_said hAKn h
exact not_isKnight_and_isKnave h hKnave
have hKnave : ¬A.isKnave := knave_said hAKn h
Text Only
12345
cases isKnight_or_isKnave A
have hKnave : A.isKnave := knight_said hAKn h
exact not_isKnight_and_isKnave h hKnave
have hKnave : ¬A.isKnave := knave_said hAKn h
exact hKnave h
Text Only
12345
have hA_said : A said A.isKnave := knight_said hB h1
have hA_said : ¬A.isKnave := dsl_iamknave(knight_said hB h1 )
have hKnave : A.isKnave := knight_said (knight_said hB h1) h2
have h3 : ¬A.isKnave := knave_said (knight_said hB h1) hA_said
Text Only
1 2 3 4 5 6 7 8 9101112
have h1: B.isKnave
knave_to_knight
by_contra h1
have hA_said :A.isKnave
knave_to_knight
by_contra h2
have hKnave : A.isKnave := knight_said (knight_said hB h1) h2
exact not_isKnight_and_isKnave h2 hKnave
have h3 : ¬A.isKnave := knave_said (knight_said hB h1) hA_said
exact h3 hA_said
have h2: C.isKnight
by_contra h3
Text Only
12345678
That's better, but you'd better send out those invites so you can get some responses!
have a := h1.left
have u := h2.right
exact and_intro a u
exact and_intro h1.left h2.right
exact and_intro (and_left h1) (and_right h2)
exact ⟨h1.left, h2.right⟩
Text Only
1 2 3 4 5 6 7 8 91011
have ha := h1.left
have hu := h2.right
have hau := and_intro ha hu
exact and_intro ha hu
have a := h1.left
have u := h2.right
exact and_intro a u
exact and_intro h1.left h2.right
exact and_intro (and_left h1) (and_right h2)
exact ⟨h1.left, h2.right⟩
Text Only
1234567
exact λ r : R => ⟨λ s : S => r, λ ns : ¬S => r⟩
have so : S ∨ O := or_inl s
have h : (S ∨ O) := or_inl s
have h := or_inl s
have h := (or_inl p : P ∨ Q)
have h1 : C → O ∨ C
have h2 : O → O ∨ C
Text Only
12
have h1 : C → O ∨ C := fun c : C => or_inr c
have h2 : O → O ∨ C := fun o : O => or_inl o
Text Only
1234
have h1 : C → O ∨ C := fun c : C => or_inr c
have h2 : O → O ∨ C := fun o : O => or_inl o
have oc : O ∨ C := or_elim h h1 h2
exact oc
Text Only
1234567
exact or_elim h (fun g : G => or_inl g) (fun hu : H ∧ U => or_inr hu.left)
exact or_elim h.right (fun hh : H => or_inl ⟨h.left, hh⟩) (fun uu : U => or_inr ⟨h.left, uu⟩) -- or_elim on h.right: 左分支 or_inl ⟨G, H⟩,右分支 or_inr ⟨G, U⟩
have g := h.left
exact or_elim h.right
(and_intro g ≫ or_inl)
(and_intro g ≫ or_inr)
exact or_comm K I
Text Only
123456
have h1 : K ∨ I → I ∨ K := GameLogic.or_comm.mp
have h2 : I ∨ K → I ∨ P := fun hk => or_elim hk (fun hi => or_inl hi) (fun hj => or_inr (h hj))
have h1 : K ∨ I → I ∨ K := GameLogic.or_comm.mp
have h2 : I ∨ K → I ∨ P := fun hk => or_elim hk (fun hi => or_inl hi) (fun hj => or_inr (h hj))
exact imp_trans h1 h2
Text Only
1234567
Thinking of h₂ as Q → False, you can actually use your imp_trans theorem here.
exact λp ↦ h₂ (h₁ p)
exact imp_trans h₁ h₂
For the math-inclined, because the expression for an implication is a function, you can also use function composition.
exact h₂ ∘ h₁
Text Only
1 2 3 4 5 6 7 8 910
exact fun (f : P ) => fun (a:A) => h(and_intro f a)
exact fun (f : P) => fun (a : A) => h (And.intro f a)
exact fun (f : P) => fun (a : A) => h (and_intro f a)
exact fun (f : P) => fun (a : A) => h (and_intro f a)
exact fun (f : P) => fun (a:A) => h(and_intro f a)
exact fun (f:P) (a:A) => h (and_intro f a)
exact iff_intro hsj hjs
exact ⟨hsj, hjs⟩
rw [← h1] at h2
exact h₂
cases h1
cases h2
constructor
intro h3
constructor
apply and_left
apply mp_1
intro x
apply h3
intro ⟨pqnr,rpnq⟩ r
apply x
constructor
intro p
cases pqnr p
left
assumption
right
intro s
apply h
apply mpr
assumption
intro
intro q
apply rpnq
left
repeat assumption
apply mp
assumption
constructor
apply and_left
apply and_right
apply mp_1
intro x
apply h3
intro ⟨pqnr,rpnq⟩ r
apply x
constructor
intro p
cases pqnr p
left
assumption
right
intro s
apply h
apply mpr
assumption
intro sp
apply rpnq
cases sp
left
apply mpr
assumption
right
assumption
apply mp
assumption
apply modus_tollens
assumption
apply and_right
apply and_right
apply mp_1
intro x
apply h3
intro ⟨pqnr, rpnq⟩ r
apply x
constructor
intro p
cases pqnr p
left
assumption
right
intro
apply h
assumption
intro
apply rpnq
left
assumption
apply mp
assumption
intro ⟨p, q, nr⟩
intro
apply mpr_1
constructor
assumption
constructor
assumption
apply modus_tollens
repeat assumption
intro
intro
assumption
Text Only
1
Robo
Lean4+Robo+Piazza+Level 2 / 13简单测试
Text Only
12
simp
decide
Lean4+Robo+Piazza+Level 2 / 13简单测试
Text Only
12
simp
decide
Lean4+Robo+Piazza+Level 3 / 13简单测试
Text Only
12345678
Set: 如果那太简单了——你们认识这个陈述吗?
Du: A B C : Set ℕ 这里到底是什么意思?
Robo: 这简单地说,就是 A、B 和 C 是 ℕ 的子集。
Du: Set 意思是“subset”?
Robo: 如果你这么想,是的。
Du: 那么我大概认识这个陈述了。但不知道怎么在这里证明它。
Ext: 我可以告诉你!那里有个魔咒,名字和我一样!!
Robo: 啊,对了——ext x 将集合等式 A = B 替换为 x ∈ A ↔ x ∈ B。
Text Only
123
ext x
simp
tauto
Lean4+Robo+Piazza+Level 4 / 13简单测试
Text Only
1 2 3 4 5 6 7 8 910
Sub: 我也学到了一些东西:
Du: univ 到底是什么?
Robo: univ 是最大子集:所有自然数。
Du: 所以就是简单的 ℕ?
Robo: 嗯,是又不是。univ : Set ℕ 是“整个 ℕ”,但被视为 ℕ 的子集。
Ext、Fin、Set、Sub 和 Mem 睁大眼睛看着你们。
Set: 这怎么可能混淆呢!这里是一个蓝莓,这个是装满所有蓝莓的篮子,这个浆果就在这个篮子里。
Mem: 同样,5 是一个自然数 (5 : ℕ),univ : Set ℕ 是所有自然数的集合,5 在这个集合中 (5 ∈ univ)。这有什么令人困惑的?
Robo(对你):别为此烦恼。我建议你从 rw [eq_univ_iff_forall] 开始,这样你就能清楚看到到底在问什么。
Robo: 现在用 simp。你甚至可以直接用 simp [eq_univ_iff_forall]。
Text Only
1 2 3 4 5 6 7 8 910
generalize
使用 generalize,你可以泛化一个证明目标——希望更高的抽象度允许更简单的证明。更精确地说,generalize h : a = b 将证明目标中所有 a 的出现替换为 b(并添加假设 h : a = b)。
示例
一个形式的目标
Even x ∨ ¬Even x
可以使用
generalize h : (Even x) = A
转换为
A ∨ ¬A
(然后简单地用 tauto 证明)。
Text Only
12345
rw [eq_univ_iff_forall]
simp
intro
generalize h : (Even x) = A
tauto
·\·
对于两个子集 (A B : Set T),A\B 是 A 和 B 的差集,由 A 中不在 B 中的所有元素组成。
Text Only
123
ext
simp
tauto
Lean4+Robo+Piazza+Level 7 / 13简单测试
Text Only
12345
Set: 我也喜欢这个等价。
Du: 是的,我觉得我曾经学过这样——两个集合相等,如果它们相互包含。
Robo: 我不确定,但我觉得从 constructor 开始。
Robo: 把 A 替换成 B 试试。
Robo: 从这里开始,应该又能套用之前的模式了。
Text Only
123456
⊆
对于 A B : Set T,A ⊆ B 意味着 A 包含在 B 中。
使用 rw [subset_iff],你可以将 A ⊆ B 重写为 ∀ x, x ∈ A → x ∈ B。
如果 A ⊆ B 是证明目标,你也可以直接使用 intro a ha 来选择一个元素 a,带有 ha : a ∈ A(然后证明 a ∈ B)。
如果 h : A ⊆ B 是一个假设,并且给定了元素 a 带有 ha : a ∈ A,你可以使用 have hb := h ha 来获得 hb : a ∈ B。
你将 ⊆ 写作为 \subset。
Text Only
1 2 3 4 5 6 7 8 9101112131415
constructor
intro h
rw[h]
tauto
intro h
ext
constructor
intro h1
obtain ⟨ha, hb⟩ := h
have h2 := ha h1
assumption
intro h1
obtain ⟨k1, k2⟩ := h
have h2 := k2 h1
assumption
Sub: 哦,哦。只是一个定义!如果你们现在要用这样的包含关系工作呢?
Du: 我在这里也能用 ext 来论证吗?
Robo: 不,简单多了。简单地用 intro a 给你一个来自 A 的任意元素,然后证明它在 C 中。
但是也许你先用 rw [subset_iff] at * 将所有包含关系展开,这样你就能看到会发生什么。
Text Only
123456
intro a
rw [subset_iff] at *
intro h
have hb:= h₁ h
have hc:= h₂ hb
assumption
intro
intro h
simp at h
obtain h | h := h
tauto
simp
right
rw[h]
rw[<-odd_iff_not_even]
decide
Or
Text Only
123456789
intro
intro h
simp at h
obtain h | h := h
tauto
simp
right
rw[h]
decide
Lean4+Robo+Piazza+Level 11 / 13简单测试
Text Only
1234567
Mem: 嘿,Fin,你在那儿干嘛呢?
Fin 是圈子里最小的,到目前为止还没说过话。而且现在他好像刚从旁边的摊位偷了一个开心果。
Fin: 这不过是个小练习而已。
Mem: 什么练习?
Fin 这样解释道。
Du: 这里 Finset 到底是什么意思?
Robo: 这意味着 A 属于 ℕ 的有限子集。但这对问题其实没什么区别。左边是没 a 的 A,右边也是没 a 的 A。
Text Only
1234
翻译(中文):
erase
对于一个有限子集 A : Finset T 和一个元素 a : T,erase A a 是 A \ {a} 的另一种写法。
如果 a 根本不在 A 中,显然 erase A a = A。
Text Only
123
ext x
simp
tauto
Lean4+Robo+Piazza+Level 12 / 13简单测试
Text Only
1
Fin: 没错。现在我把我的开心果放回去了。
Text Only
123
ext x
simp
tauto
Lean4+Robo+Piazza+Level 13 / 13简单测试
Text Only
123
Fin: 那么,你们觉得呢——现在所有开心果都回家了吗?
Fin: 为什么不做个情况区分,看 x = a 还是不是。
Fin: 是的,这样可以处理。
Text Only
1234
翻译(中文):
erase
对于一个有限子集 A : Finset T 和一个元素 a : T,erase A a 是 A \ {a} 的另一种写法。
如果 a 根本不在 A 中,显然 erase A a = A。
Text Only
1 2 3 4 5 6 7 8 9101112131415
ext b
simp
constructor
intro k
obtain h1 | ⟨ h2, h3 ⟩ := k (obtain h1 | < h2, h3 > := k)
rw[h1]
assumption
assumption
intro hb
by_cases heq: b = a
left
assumption
constructor
assumption
assumption
Lean4+Robo+Luna+Level 1 / 10简单测试
Text Only
1234
你感觉有点措手不及,但还是试图开始对话。
你:很好,我们努力不搞乱。这里保持秩序很难吗?
Lina:例如,必须知道 n ≤ n 是真的。
Robo:rfl?
Text Only
1
tauto
or
Text Only
1
rfl
Lean4+Robo+Luna+Level 2 / 10简单测试
Text Only
1234
你感觉有点措手不及,但还是试图开始对话。
你:很好,我们努力不搞乱。这里保持秩序很难吗?
Lina:例如,必须知道 n ≤ n 是真的。
Robo:rfl?
linarith
linarith 策略可以证明,一个线性方程或不等式可以从给定的方程或不等式中推出。
它非常灵活,在 ℕ 和 ℝ 中都同样有效。但是,(不)等式必须是单独给出的,不能逻辑连接。
例如,一个假设形式为
h : m ≤ x → n < x
必须先用
rw [imp_iff_or_not] at h
改写成
h : n < x ∨ ¬m ≤ x
这样 linarith 才能处理它。
Text Only
1 2 3 4 5 6 7 8 91011
intro a b
by_cases h_leq : a ≤ b
by_cases h_lt : a < b
left
assumption
right
left
linarith
right
right
linarith
丽娜(Lina):现在又轮到我了。
你(Du):嗯,已经很清楚这里可以用哪个 b 了。
罗博(Robo):如果你觉得这么清楚,那就先从 use … 开始吧。
然后你就能好好用上 lt_trichotomy 了。比如这样:
obtain h | h | h := lt_trichotomy a c
关卡完成!🎉
丽娜(Lina):你们做得很好!可惜你们得继续往前飞了。
但如果你们再多留一会儿,就会把我们的作息时间完全打乱。
Text Only
1 2 3 4 5 6 7 8 91011
use (a + c) / 2
obtain k | k | h := lt_trichotomy a c
left
constructor
linarith
linarith
contradiction
right
constructor
linarith
linarith
∀(全称量词)
全称量词:如果 P : A → Prop 是一个谓词,
那么 ∀ a : A, P a 就是这样一个陈述:对于 A 中的所有 a(更精确地说,对于所有类型为 A 的 a),陈述 P a 都是真的。
∀ 作为证明目标
要证明一个形如 ∀ a : A, … 的陈述,你首先用 intro a 选择一个任意的元素 a。
∀ 作为假设
如果 h : ∀ a : A, P a 是一个假设,并且 a₀ : A 是一个具体的元素,
那么 h a₀ 就是 P a₀ 的记法。你也可以用 specialize h a₀ 将给定的假设(关于所有可能的 a)限制为一个关于这个具体 a₀ 的假设 h : P a₀。
Text Only
123456789
rw[subset_iff]
simp
intro h
constructor
apply a at h
have : a₁ ≤ a₁ := by rfl
apply a at this
omega
omega
你(Du,对罗博):再给我点更有趣的吧!
罗博(Robo):这个怎么样?
你(Du):by_contra?
罗博(Robo):这可能行得通。而且你很可能需要用引理 lt_of_mul_lt_mul_left。
对于 a b c : ℕ,它从假设 a * b < a * c 得出结论 b < c。
关卡完成!🎉
Text Only
123456
by_contra h
choose b hb using h
rw[hb] at h1 h2
apply lt_of_mul_lt_mul_left at h1
apply lt_of_mul_lt_mul_left at h2
omega
Lean4+Robo+Prado+Level 6 / 10简单测试
Text Only
1234567
你(Du):好吧。现在你能给我展示一下如何处理素数吗?
罗博(Robo):让我看看,我有没有关于素数的任务……这个怎么样?
罗博(Robo):这里 (hp : Prime p) 当然是假设 p 是一个素数。
为了使用这个假设,最好总是应用 rw [prime_def] at hp。
你(Du):啊哈。那么一个素数就是一个大于等于 2 的自然数,只被 1 和它本身整除。这听起来很熟悉。
你(Du):我现在肯定要将 hp 应用到 a 上,对吧?
罗博(Robo):那就说 have hp' := hp a 吧。或者更优雅一点:specialize hp a。
Text Only
1 2 3 4 5 6 7 8 91011
specialize
specialize h a₁ a₂ 等价于 have h := h a₁ a₂:
这个策略将一个假设 h : ∀ m₁ m₂, P m₁ m₂ 替换为特殊情况 h : P a₁ a₂。
如果你想多次特化,应该使用 have 而不是 specialize,因为 specialize h … 会覆盖旧的假设 h。
例如,从上面的假设 h 中,你可以用
have ha := h a₁ a₂
have hb := h b₁ b₂
得到以下三个假设:
h : ∀ m₁ m₂, P m₁ m₂
ha : P a₁ a₂
hb : P b₁ b₂
Text Only
1 2 3 4 5 6 7 8 91011
rw [prime_def] at hp
obtain ⟨hp1, hp⟩ := hp
have hb : a ∣ p → a = 1 ∨ a = p
apply hp
specialize hp a
have hc : a = 1 ∨ a = p
apply hb
assumption
obtain h1 | h2 := hc
linarith
assumption
or
Text Only
1234567
rw [prime_def] at hp
obtain ⟨hp1, hp⟩ := hp
have _hp := hp a ha
specialize hp a ha
obtain hp | hp := hp
linarith
assumption
constructor
rw[Prime.dvd_mul]
tauto
decide
intro h
obtain h1 | h2 := h
rw[dvd_iff_exists_eq_mul_left] at h1
choose c hc using h1
rw[dvd_iff_exists_eq_mul_left]
use c*b
rw[hc]
linarith
rw[dvd_iff_exists_eq_mul_left] at h2
choose c hc using h2
rw[dvd_iff_exists_eq_mul_left]
use a * c
rw[hc]
linarith
罗博(Robo):不过这也没那么难。来,看看这个任务。
你(Du):我看出来了 – ∃! m, P(m) 就是“存在唯一一个 m,使得 P(m) 成立”的记法。
罗博(Robo):没错。而且它简单定义为“存在一个 m,使得 (1) P(m) 成立,并且
(2) 任何其他满足 P(m') 成立的元素 m' 都等于 m”。
所以第一步是找到一个合适的 m,然后用 use _。
罗博(Robo):实际上,对 ∃! 应用 use 总是会产生一点混乱。最好马上跟一个 simp,这样它就会变得可读了。
罗博(Robo):现在如我所说,你有两个陈述要证明:(1) w 满足 a * w = b,(2) w 是唯一具有这个性质的元素。
罗博(Robo):太好了。现在来证明唯一性。我觉得这里可以用引理 mul_eq_mul_left_iff 帮忙:
a * b = a * c ↔ b = c ∨ a = 0
Text Only
1 2 3 4 5 6 7 8 91011121314
obtain ⟨m, hm⟩ := h
use m
simp
constructor
symm
assumption
intro g
rw[hm]
rw[mul_eq_mul_left_iff]
intro h
obtain h | h := h
assumption
rw [h] at ha
contradiction
Or
Text Only
1 2 3 4 5 6 7 8 91011
obtain ⟨w, hw⟩ := h
use w
simp
constructor
rw [hw]
intro y hy
rw [hw] at hy
rw [mul_eq_mul_left_iff] at hy
obtain h | h := hy
assumption
linarith
use 2
simp
constructor
· decide
· intro p hp h
rw [even_iff_two_dvd] at h
rw [prime_def] at hp
obtain ⟨h2, hprime ⟩ := hp
apply (hprime 2) at h
obtain h | h:= h
· contradiction
· symm
assumption
card
对于一个有限子集 A : Finset T,card A : ℕ 是 A 的基数,即 A 中元素的个数。
Text Only
1
simp
Lean4+Robo+Babylon+Level 2 / 9简单测试
Text Only
123
你们一起看向下一座塔。
你说:还是很简单?
关卡完成!🎉
Text Only
12
simp
ring
Lean4+Robo+Babylon+Level 3 / 9简单测试
Text Only
1234
接着,你们来到一块空荡荡的建筑工地,这里似乎已经很久没有任何动静了。建筑牌上写着:
你说:这个假设看起来像是 I⊆{−1,0,1} 的一种拐弯抹角的版本。不管怎样,总和都不会太大。
机器人:是的。但我们似乎需要一个中间步骤,才能得到给定的结果。
我建议:trans ∑ i ∈ I, 0。你可以用 \sum 来表示求和符号。
Text Only
12345
trans ∑ i ∈ I, 0
apply sum_congr
rfl
assumption
simp
Lean4+Robo+Babylon+Level 4 / 9简单测试
Text Only
1 2 3 4 5 6 7 8 91011121314151617181920
你们继续从一个塔走向另一个塔。终于,你在一座塔前停了下来,它让你感觉有些古怪。绕着它走了一圈之后,你明白了原因:这座塔没有入口。好在你们找到一块地砖,上面刻着如下文字:
你说:慢着。要证明的是:
我猜,求和里的表达式在前三个 i 值上干脆就是 0……没错,正是这样。那我现在该怎么写?
机器人:你可以用 sum_subset:如果 I₁ ⊆ I₂,并且求和表达式在 I₂ 中但不在 I₁ 的那些元素上统统为 0,那么 I₁ 上的和就等于 I₂ 上的和。
你说:
“我敢打赌,求和式里的表达式在前三个 i 值上干脆就是 0……没错,正是这样。那我现在该怎么写?”
机器人:
“你可以用 sum_subset:只要 I₁ ⊆ I₂,并且求和式在 I₂ 比 I₁ 多出来的那些元素上统统为 0,那么 I₁ 上的和就等于 I₂ 上的和。”
机器人:
“好。现在就用 apply sum_subset。”
机器人:
“这里 Icc_subset_Icc_iff 肯定能派上用场。”
机器人:
“太棒了!现在你只需要证明你刚才说的:求和式里的表达式在前三个索引上等于 0。”
机器人:
“我建议你先一口气把所有前提都引进来,直到剩下的证明目标只剩
i ^ 3 - 3 * i ^ 2 + 2 * i = 0
为止。”
机器人:
“从这些假设里总得能推出 i = 0 或 i = 1 或 i = 2。你不妨先用 have 把这一点显式地写出来。”
Text Only
1 2 3 4 5 6 7 8 910111213141516
symm
apply sum_subset
rw[Icc_subset_Icc_iff]
tauto
tauto
intro i h0 h3
have h : i = 0 ∨ i = 1 ∨ i = 2
simp at h0 h3
omega
obtain h | h | h := h
rw[h]
ring
rw[h]
ring
rw[h]
ring
Lean4+Robo+Babylon+Level 5 / 9简单测试
Text Only
1 2 3 4 5 6 7 8 910
你:现在需要证明的是,
∑_{i∈I} ((-1)^{i+1})
与 I 中偶数个数的两倍是相同的。对吗?
Robo:对。
你:这是因为……求和中的表达式对于奇数 i 会消失,而对于偶数 i 等于 2。嗯……
Robo:为什么不用 trans 再做几个中间步骤呢。首先,你想将求和限制到偶数索引的集合上,即:
∑_{i ∈ {i ∈ I | i 为偶数}}, ((-1)^i + 1)
然后你大概想用
∑_{i ∈ {i ∈ I | i 为偶数}}, 2
作为中间步骤。
Text Only
1 2 3 4 5 6 7 8 9101112131415161718192021222324
trans ∑ i ∈ { i ∈ I | Even i}, ((-1)^i + 1)
symm
rw [sum_subset]
simp
simp
intro i h hI
apply hI at h
rw [Odd.neg_pow]
ring
rw[<-odd_iff_not_even] at h
assumption
trans ∑ i ∈ { i ∈ I | Even i}, 2
have : ∀ i ∈ { i ∈ I | Even i}, (-1 : ℤ)^i + 1 = 2
intro i hi
simp at hi
obtain ⟨hI, heven⟩ := hi
rw [Even.neg_pow]
ring
assumption
apply sum_congr
simp
assumption
simp
ring
Lean4+Robo+Babylon+Level 6 / 9简单测试
Text Only
1 2 3 4 5 6 7 8 910111213
巴比伦人:来吧,我给你们看看我们最漂亮的塔之一!
道路陡峭地向上延伸。山顶上等待着你们的这座塔确实非常宏伟。
Robo:这肯定是著名的巴比伦高斯塔!我以前读到过一些关于它的东西。
巴比伦人:没错。高斯是个巴比伦人!
你:这个求和我也见过。
∑_{i=0}^n i = 1/2 ⋅ n ⋅ (n+1)
不是有那个小高斯的故事吗,他有一个非常简单的论证?
Robo:我对历史不熟。我会简单地对 n 做归纳。那在 Lean 中就是:induction n with d hd!
你:为什么用 with …?
Robo:这个附加部分当然是可选的。你可以用它来指定归纳变量(d)和假设(hd)的名称。
你:首先是归纳基……
Robo:这个你可以用 simp 简化!
Robo:现在你想把求和区间 [0, d+1] 分解成 [0, d] 和 d+1。为此,你可以用我们之前见过的 Lemma insert_Icc_eq_Icc_add_one_right。
Text Only
1 2 3 4 5 6 7 8 910
induction n with d hd
simp
rw [← insert_Icc_eq_Icc_add_one_right]
rw [sum_insert]
rw [hd]
simp
ring
simp
ring
linarith
对于一个子集 A : Set T,Set.Finite A 表示 A 只有有限多个元素。
如果给定假设 h : Set.Finite A,
那么 h.toFinset : Finset T 就是相同的子集 A,但现在明确地被视为有限子集。
Text Only
1 2 3 4 5 6 7 8 91011121314151617
let all_primes := hf.toFinset
use ∏ p ∈ all_primes, p
constructor
apply Finset.prod_pos
intro p
simp [all_primes]
intro h
rw [prime_def] at h
linarith
intro p hp
have hk : p ∈ all_primes
simp [all_primes]
assumption
rw [← insert_erase hk]
rw [prod_insert]
use ∏ x ∈ all_primes.erase p, x
simp
by_contra hf
let all_primes := hf.toFinset
let prod : ℕ := ∏ p ∈ all_primes, p
let new_prime : ℕ := prod + 1
have h_exists_prime_factor : ∃ p : ℕ, Prime p ∧ p ∣ new_prime
have : 1 < new_prime
have : 0 < prod
apply Finset.prod_pos
intro p
simp[all_primes]
intro h
rw [prime_def] at h
linarith
simp[new_prime]
assumption
apply exists_prime_and_dvd
linarith
have h_no_prime_divides : ∀ p : ℕ, Prime p → ¬ p ∣ new_prime
intro p hp
let q := ∏ p' ∈ (all_primes.erase p), (p' : ℕ)
have h : prod = p * q
simp[prod]
have : p ∈ all_primes
simp[all_primes]
assumption
rw[← Finset.insert_erase this]
apply Finset.prod_insert
simp
simp[new_prime]
rw [h]
apply not_dvd_of_between_consec_multiples (n := p) (k:=q) (m := p*q+1)
linarith
simp [prime_def] at hp
linarith
obtain ⟨p, hp, h_dvd⟩ := h_exists_prime_factor
specialize h_no_prime_divides p hp
contradiction
又一支箭。还有一个任务。
Robo:像往常一样,你用 use … 来处理一个 ∃。
或者你先用 let f : ℤ → ℤ := fun … 定义一个你想用的映射,就像你刚才看到的。
顺便说一下,↦ 这个箭头你可以用 \maps 或 \mapsto 来写。但你也可以用 => 代替。
Text Only
123
use fun x ↦ x - 1
intro x
linarith
or
Text Only
1234
let f : ℤ → ℤ := fun x ↦ x -1
use f
intro x
simp [f]
Lean4+Robo+Vieta+Level 4 / 10简单测试
Text Only
1 2 3 4 5 6 7 8 910
维埃塔:现在我们得往这边走一小段。
他小心地把你们推了几米远。一会儿后,你们刚才站的地方射下了三支箭,钉在了地上。
维埃塔:别慌,我对这里很熟。瞧,我还有更多给你们。
你:g ∘ f 是函数复合吗?
Robo:没错!在 Lean 中用 \comp 来写。
你:那我在这里又可以用 let g : ℤ → ℤ := fun x ↦ _ 来定义吗?
Robo:是的,或者直接 use fun (x : ℤ) ↦ _?
Robo:现在你可以用 use g 来使用这个。
Robo:(g ∘ f) x 根据定义就是 g (f x)。simp 也会知道这个引理,但这里试试直接用 rw [comp_apply]。
Robo:ring 可以看透像 f 和 g 这样的局部定义,所以你可以直接使用它。
Text Only
1234
let g : ℤ → ℤ := fun x ↦ x-3
use g
rw [comp_apply]
ring
Lean4+Robo+Vieta+Level 5 / 10简单测试
Text Only
1234
维埃塔小心地环顾四周,但还是停了下来。他平静地递给你们下一张纸。
你:根据定义,两个映射相等,当它们应用于每个元素时给出相同的值……
Robo:对于这个原理,我有 funext 战术可以用。
用 funext x 你选择一个任意的 x,并将证明目标从 f = g 改为 f x = g x。
Text Only
12
funext x
ring
Lean4+Robo+Vieta+Level 6 / 10简单测试
Text Only
1 2 3 4 5 6 7 8 9101112131415
维埃塔:我们还是再跑一段。这里走!
他匆匆离去,你们尽力跟上。当你们到达他最终停下来的地方时,你已经气喘吁吁。维埃塔笑了。
维埃塔:纯粹是预防措施!我得照顾好我的访客。我可不常有访客!
他递给你们下一张纸。
Robo:现在我们有两个映射,其中一个是分段定义的。
你:所以,我需要证明它们是可交换的?
Robo:没错,最好用 funext x 选择一个任意元素,然后针对这个元素证明。
你:啊,现在我可以先把 (g ∘ f) x 重写成 g (f x)?
Robo:用 simp 就能行。
Robo:现在你可以做一个分支情况,by_cases h : 0 ≤ x。
你:这样我就能得到 0 ≤ x 和 0 > x 的情况?
Robo:没错!或者更精确地说,0 ≤ x 和 ¬(0 ≤ x)。
这不是完全一样的,你可以用引理 not_le 在 ¬(0 ≤ x) 和 0 > x 之间切换。
你:现在我大概还是得用用那些定义。
Robo:那就用 simp [f, g] 试试!
Text Only
1 2 3 4 5 6 7 8 910
funext x
simp
by_cases h : 0 ≤ x
simp [f, g]
rw [if_pos h]
rw [if_pos h]
ring
simp [f, g]
rw [if_neg h]
rw [if_neg h]
Lean4+Robo+Vieta+Level 7 / 10简单测试
Text Only
1234
你们从远处听到战斗声。维埃塔似乎依然不担心。他又给你们一个任务。
Robo:这里你可能需要 toNat:如果 n : ℤ 是一个整数,那么 n.toNat : ℕ 就是同一个数,但被视为自然数。
你:怎么?比如 (-1).toNat 是什么??
Robo:不知道。我的意思当然是:如果 n ≥ 0,那么 n.toNat 还是“同一个”数。
Text Only
123456
if
使用 if … then … else,你可以定义有两个定义分支的映射。
例如,fun x ↦ if 0 ≤ x then x else -x 定义了绝对值函数。
相关技巧
如果你有假设 h : A 可用,你可以用 rw [if_pos h] 将表达式 if A then B else C 简化为 B。
如果你有假设 h : ¬ A 可用,你可以类似地用 rw [if_neg h] 将表达式 if A then B else C 简化为 C。
Text Only
1234
let g : ℤ → A := fun n ↦ if (0 ≤ n) then f n.toNat else f 0
use g
intro n
simp [g]
Lean4+Robo+Vieta+Level 8 / 10简单测试
Text Only
123
战斗声越来越近。维埃塔又给你们两张纸。
Robo:哦,这是个 congr_arg 的案例。
如果你已经知道 x = y,用 apply congr_arg 就能得到 f x = f y。
Text Only
12
apply congr_arg
rfl
Lean4+Robo+Vieta+Level 9 / 10简单测试
Text Only
12345
Robo:这是 congr_fun 的一个案例。如果你有假设 h : f = g,
你可以用 apply congr_fun at h 将它改写成 h : ∀ x, f x = g x。
你:但是我不能在这里更简单地用 rw [h] 吗?
Robo:嗯,好吧,在这个简单例子中可以。但如果 f 是一个更复杂的表达式,
并且没有完全匹配证明目标中的形式,那就不能了。试试我刚才说的方法。
Text Only
12
apply congr_fun at h
apply h
Lean4+Robo+Vieta+Level 10 / 10简单测试
Text Only
1234567
战斗声现在听起来威胁地近了。你们清楚地听到大炮射击的声音。而且又一支箭呼啸着从你们身边掠过。
你:呃,我们是不是应该……
维埃塔:别担心,还有时间完成一个任务。
你:这里 succ 是什么?
Robo:succ : ℕ → ℕ 是将一个自然数映射到它的后继者(successor)的函数。换句话说:n ↦ n + 1。
你:哦,这样啊。从映射 f 来看,如果我没读错的话,我基本上需要证明它是满射的。
Robo:看起来是这样!
Text Only
1 2 3 4 5 6 7 8 910
intro n
induction n with n hn
assumption
obtain ⟨b, hb⟩ := hn
use g b
apply congr_fun at hs
specialize hs b
simp at hs
rw [hs]
rw [hb]
Lean4+Robo+Robotswana+Level 1 / 11简单测试
Text Only
1 2 3 4 5 6 7 8 91011121314
在下面有一些随意的涂鸦,但明显以一条清晰的线开始:
你:不管是什么生物在这里留下痕迹——我看,它喜欢矩阵。反正 Mat[n,n] 看起来很像 (n×n)-矩阵。我只是不记得 Fin n 是什么了。
Robo:Fin n 是集合 {0,...,n−1}。所以这里的行和列索引从 0 开始,而不是从 1 开始。而且我碰巧知道 stdBasisMatrix i j a。那是位置 (i, j) 有条目 a,其余地方都是 0 的矩阵。
你:那些 E 只是 a = 1 的情况的缩写吗?
Robo:看起来是这样。而且 A i j 就是矩阵 A 在位置 (i, j) 的条目。
你:啊,明白了。所以这里不是矩阵乘积,而是纯标量乘法。类似于……
你在纸上涂鸦:
$ A_{i,j} \cdot \begin{pmatrix} 0 & 1 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{pmatrix} = \begin{pmatrix} 0 & A_{i,j} & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{pmatrix} $
你:那这又一次……显而易见!?
Robo:是的。我觉得如果你从 unfold E 开始,剩下的就顺理成章了
关卡完成!🎉
你:我们现在用这个“认识”做什么?
Robo:不知道。我反正把它保存为 Matrix.smul_ebasis,以防我们还需要。
于是你们继续跟着线索走。
你们来到一个地方,那里一大片方形的草地被踩平了。痕迹纵横交错,向各个方向延伸。
你们有点漫无目的地搜查这个地方,找到各种羊皮纸碎片。大多数是空的或看不懂的,但有一张你能辨认出来。
你:这似乎只是说,这些 E_{i j} 构成了矩阵空间的生成系。
Robo:那里你肯定能立即应用我们已经找到的结果!
Robo:先看看表达式 (A i j) • E i j。在求和下,需要用 simp_rw。
Robo:嗯,好吧,你也可以简单重复第一张羊皮纸的证明。来吧,熟能生巧。
你:好了,我可没有你那样的机械大脑。
Robo:啊,对了!就现在这样,我从我的库里认识这个陈述。这正好是 apply matrix_eq_sum_std_basis。
你:太棒了!那我们就不用在这里耽搁了。
关卡完成!🎉
你决定跟着一条特别明显的痕迹走。Robo 跟在你后面,走着走着还从地上捡起一张随意的羊皮纸碎片。
Text Only
123
unfold E
simp
apply matrix_eq_sum_std_basis
Lean4+Robo+Robotswana+Level 5 / 11简单测试
Text Only
1 2 3 4 5 6 7 8 9101112
你:给我看看,你捡到什么了?什么关于单位矩阵的?适合我们的收藏吗?
Robo:是的——最右边的 1 是单位矩阵。我觉得你可以直接从 matrix_eq_sum_ebasis 开始。
你:我在想,我们是不是在广场上留下了什么重要的东西?
Robo:没关系,现在我们已经走了一大段路了。现在试试这里!
你:我觉得这两个求和是相同的,因为每个求和项都是相同的。
你:现在呢?
Robo:用 funext r s 你可以专注于矩阵在位置 (r,s) 的条目。
你:1 这里是单位矩阵,对吧?
Robo:是的。
你:那么 1 i j 对于所有 j ≠ i 都是零。所以除了 j = i 的求和项,其他所有求和项都会消失。
Robo:是这样吗?那让我想想……你能先 have h : {i} ⊆ univ 证明一下吗?
你:谢谢,这有帮助!这个步骤应该很简单:一个单元素的求和,在这个元素上,1 i i 又是1,而且 1 • _ 也能简化!
Text Only
1 2 3 4 5 6 7 8 91011121314151617
rw [matrix_eq_sum_ebasis 1]
apply sum_congr
rfl
intro i hi
funext r s
have h : {i} ⊆ univ
simp
rw [← sum_subset h]
simp
intro x h₁ h₂
have h₃ : i ≠ x
simp at h₂
symm
assumption
rw [Matrix.one_apply]
rw [if_neg h₃]
simp
Lean4+Robo+Robotswana+Level 6 / 11简单测试
Text Only
1 2 3 4 5 6 7 8 91011
沿着痕迹,你们来到一棵大树。在树荫下,你们发现一个一动不动的什么东西:
[A,B]=AB−BA
Robo:啊,对了,一个交换子!
你:但它看起来相当被消灭了。我觉得它渴死了。
Robo:看,这里树上还刻着什么。
你:换句话说:如果 f 消灭交换子,那么它在所有 E_{i i} 上的值都是一致的。这对吗??
Robo:让我们来查查!
你:但是我该怎么用我们的假设 h₁呢!我甚至首先需要一个乘法。
Robo:你需要找到一个矩阵乘积 A * B,使得 f (E i i) = f (A * B) = f (E j j)。
然后你可以用 trans f (A * B) 来写,得到两个证明目标——f (E i i) = f (A * B) 和 f (A * B) = f (E j j)——在这些目标中 h₁ 也许适用。
Robo:我们不是在那些纸条上有一个 E i k = (E i j) * (E j k) 吗?
Text Only
1234
intro i j
specialize h₁ (E i j) (E j i)
simp [E.mul_same] at h₁
assumption
nth_rw 1 [matrix_eq_sum_ebasis A]
rw [map_sum]
simp
trans ∑ i, ∑ j, if i = j then (A i j) * f (E i j) else 0
apply congr_arg
ext i
apply congr_arg
ext j
by_cases h₂ : i = j
rw [if_pos h₂]
rw [if_neg h₂]
rw [zero_on_offDiag_ebasis]
simp
assumption
assumption
simp