Dos primeras hojas
Some checks failed
Lean Action CI / build (push) Has been cancelled

This commit is contained in:
Miguel Marco 2026-10-26 12:49:31 +01:00
parent b51a19ee04
commit 12d608d061
Signed by: mmarco
GPG Key ID: DD0114EDB7410592
2 changed files with 474 additions and 0 deletions

312
Curso2026/Conjuntos.lean Normal file
View File

@ -0,0 +1,312 @@
import Mathlib.Tactic
/-!
# Conjuntos en Lean
En Lean los conjuntos son siempre homogéneos, es decir: todos sus elementos tienen el mismo tipo.
Para un tipo `X : Type`, el tipo de los subconjuntos de `X` se llama `Set X`.
Un objeto del tipo `Set X` se puede interpretar de tres formas distintas
1) Un conjunto cuyos elementos tienen tipo `X`
2) Un subconjunto de `X`;
3) Una función de `X` a `Prop` (enviando los elementos del conjunto a `True` y los demás a `False`)
Notar que `X` es un tipo, pero un conjunto `A` es un término de tipo `Set X`.
Es decir `a : A` no tiene sentido, ya que el tipo de `a` sería `X`.
Lo que tiene sentido es `a : X` y `a ∈ A`.
`a ∈ A` es cierto o falso, así que el tipo de `a ∈ A` es `Prop`.
-/
open Set -- así podemos usar cosas como `univ` en lugar de `Set.univ`
-- por definición, siempre es falso que un elemento pertenezca al vacío
lemma vacio_defin {X : Type} : (∅ : Set X) = {x : X | False} := by
rfl -- la táctica `rfl` comprueba que una igualdad se cumple por definición
-- el subconjunto total, es decir, el formado por todos los elementos de `X` se llama `univ`.
-- por definición, todos los elementos de tipo `X` pertenecen a `univ`
lemma total_defin {X : Type} : (univ : Set X) = {x : X | True} := by
rfl
-- por definición, un elemento está en la intersección de dos conjuntos si y sólo si
-- está en ambos
lemma inter_defin {X : Type} (A B : Set X) : A ∩ B = {x: X | x ∈ A ∧ x ∈ B} := by
rfl
-- por definición, el complementario de un conjunto está formado por los elementos que no
-- le pertenecen ( el símbolo ᶜ se consigue con \^c )
lemma compl_defin {X : Type} (A : Set X) : Aᶜ = {x : X | x ∉ A} := by
rfl
-- el complemento del total es el vacío
lemma compl_total {X : Type} : (univ : Set X)ᶜ = ∅ := by
simp -- la táctica `simp` simplifica (usando resultados anteriores o en la biblioteca)
-- un conjunto `A` es el vacío si y solo si es falso que cualquier
-- elemento le pertenezca
lemma conjunto_vacio_sii_todo_elemento_no {X : Type} (A : Set X) : A = ∅ ↔ ∀ x : X, ¬(x ∈ A) := by
constructor
· intro h x
rw [h]
change ¬False -- la táctica `change` cambia el objetivo por algo que sea igual por definición
simp
· intro h
ext x
constructor
· intro hx
specialize h x
simp
exact h hx
· intro h
cases h
-- dos conjuntos son disjuntos si y solo si uno está contenido en el complemento del otro:
lemma disjuntos_sii_contenido_en_complemento {X : Type} (A B : Set X):
A ∩ B = ∅ ↔ A ⊆ Bᶜ := by
rw [conjunto_vacio_sii_todo_elemento_no]
constructor
· intro h x hxA
simp
intro hxB
apply h x
constructor
· exact hxA
· exact hxB
· intro h x hxAB
choose hxA hxB using hxAB
specialize h hxA
apply h
exact hxB
/-
## Uniones e intersecciones de familias
Fijado un tipo `X`, si tenemos dos conjuntos `U` y `V`, las operaciones `∪` y `∩` (\cup y \cap
o \union y \inter respectivamente) operan entre ellos y producen un nuevo conjunto en el mismo tipo.
Sin embargo, a veces queremos tomar la unión o intersección de una familia más amplia de conjuntos.
Es decir, dada una familia `F : Set (Set X)` podemos tomar `⋃₀ F` (\Union\0) la unión de todos
sus elementos. También podemos tomar `⋂₀ F` ( \Inter\0) la intersección de todos ellos.
Los símbolos `⋃₀` y `⋂₀` son notaciones para las operaciones `sUnion` y `sInter`.
-/
example (X : Type) (F : Set (Set X)) : ⋃₀ F = { x : X | ∃ U ∈ F, x ∈ U} := by
rfl
example (X : Type) (F : Set (Set X)) (x : X) : x ∈ ⋂₀ F ↔ ∀ U ∈ F, x ∈ U := by
rfl
-- la union de de una familia es el complementario de las intersecciones de los complementarios
lemma union_familia_es_complementario_de_interseccion_complementarios
{X : Type} (F : Set (Set X)) :
⋃₀ F = (⋂₀ (compl '' F))ᶜ := by
-- veremos luego por qué `compl '' F` es el conjunto de los complementarios
-- por doble contenido. tomamos un elemento arbitrario
ext x
constructor
· intro h
choose S hSF hxS using h
intro hneg
specialize hneg Sᶜ
have h1 : Sᶜ ∈ compl '' F
· use S
specialize hneg h1
apply hneg
exact hxS
· intro h
simp at h
choose S hSF hxS using h
use S
/-
Se pueden definir también conjuntos por extensión, es decir, listando sus elementos.
Esto se hace con la sintaxis `{x1, x2, x3}`. Internamente, estos conjuntos están creados
empezando con un `singleton` (un conjunto que tiene un único elemento), y sobre
ese conjunto ir realizando la operación `insert` (agregar un elemento a un conjunto)
-/
lemma union_familia_dos_conjuntos (X : Type) (A B : Set X) : A ∪ B = ⋃₀ {A,B} := by
ext x
constructor
· intro h
cases' h with h1 h2
· use A
constructor
· left
rfl
· exact h1
· use B
constructor
· right
rfl
· exact h2
· intro h
choose C hCAB hxC using h
cases' hCAB with hCA hCB
· left
rw [← hCA]
exact hxC
· right
simp at hCB
rw [hCB] at hxC
exact hxC
/-
## Conjuntos y funciones
Si tenemos una función entre dos tipos `f : X → Y`, actúa también sobre los conjuntos.
En particular:
- Dado un `V : Set Y`, la preimagen es `f ⁻¹' V`
- Dado un `U : Set Y`, la imagen es `f '' U`
- La imagen de todo el conjunto es `range f`
Las uniones e interseciones parametrizadas se dan como funciones que devuelven conjuntos.
Es decir, si tenemos alguna función `f : X → Set Y`, tomar todos la unión de todos los conjuntos
de la forma `f x` se hace mediante la expresión `⋃ (x : X), (f x)`. Análogamente para la intersección.
También se puede hacer recorriendo sólo un subconjunto `S` de `X`, escribiendo `⋂ (x : X) (hx : x ∈ S), f x`
-/
example (X Y : Type) (f : X → Y) (V : Set Y) : f ⁻¹' V = {x : X | f x ∈ V} := by
rfl
example (X Y : Type) (f : X → Y) (U : Set X) : f '' U = {y : Y | ∃ x : X, x ∈ U ∧ f x = y} := by
rfl
example (X Y : Type) (f : X → Y) (U : Set X) : f '' U = { (f x) | x ∈ U } := by
rfl
example (X Y : Type) (f : X → Y) : range f = {y : Y | ∃ x : X, f x = y} := by
rfl
example (X Y : Type) (f : X → Y) : range f = f '' univ := by
simp
example (X Y : Type) (f : X → Set Y) : ⋃₀ range f = ⋃ (x : X), f x := by
rfl
example (X Y : Type) (f : X → Set Y) (S : Set X) : (⋂ (x : X) (hx : x ∈ S), f x) = {y : Y | ∀ x ∈ S, y ∈ f x } := by
ext y
simp
-- ### Ejercicios
-- La unión de las imágenes es la imagen de la unión
example (X Y : Type) (f : X → Y) (U V : Set X) : f '' (U ∪ V ) = (f '' U) ∪ (f '' V ) := by
sorry
example (X Y : Type) (f : X → Y) (F : Set (Set X)) : f '' ⋃₀ F = ⋃₀ {V : Set Y | ∃ U ∈ F, V = f '' U} := by
sorry
-- La imagén de la intersección está contenida en intersección de las imágenes
example (X Y : Type) (f : X → Y) (U V : Set X) : f '' (U ∩ V ) ⊆ (f '' U) ∩ (f '' V ) := by
sorry
example (X Y : Type) (f : X → Y) (F : Set (Set X)) : f '' ⋂₀ F ⊆ ⋂₀ {V : Set Y | ∃ U ∈ F, V = f '' U} := by
sorry
-- La intersección de las preimagenes es la preimagen de la intersección
example (X Y : Type) (f : X → Y) (U V : Set Y) : f ⁻¹' (U ∩ V ) = (f ⁻¹' U) ∩ (f ⁻¹' V ) := by
sorry
example (X Y : Type) (f : X → Y) (F : Set (Set Y)) : f ⁻¹' (⋂₀ F ) = ⋂ (V : Set Y) (hV: V ∈ F), f ⁻¹' V := by
sorry
-- La unión de las preimagenes es la imagen de la unión
example (X Y : Type) (f : X → Y) (U V : Set Y) : f ⁻¹' (U ∪ V ) = (f ⁻¹' U) ∪ (f ⁻¹' V ) := by
sorry
example (X Y : Type) (f : X → Y) (F : Set (Set Y)) : f ⁻¹' (⋃₀ F ) = ⋃ (V : Set Y) (hV: V ∈ F), f ⁻¹' V := by
sorry
/-
Algunas fórmulas con uniones e intersecciones
-/
variables (X : Type) (A B C : Set X)
example : A ∪ A = A := by
sorry
example : A ∩ A = A := by
sorry
example : A ∩ ∅ = ∅ := by
sorry
example : A ∪ univ = univ := by
sorry
example : A ⊆ B → B ⊆ A → A = B := by
sorry
example : A ∩ B = B ∩ A := by
sorry
example : A ∩ (B ∩ C) = (A ∩ B) ∩ C := by
sorry
example : A ∪ (B ∪ C) = (A ∪ B) ∪ C := by
sorry
example : A ∪ (B ∩ C) = (A ∪ B) ∩ (A ∪ C) := by
sorry
example : A ∩ (B ∪ C) = (A ∩ B) ∪ (A ∩ C) := by
sorry
/-
# Subconjuntos como tipos
Hemos visto que lo que en matemáticas habitualmente llamamos conjuntos, en Lean corresponden a tiposy los subconjuntos de estos tipos son de una naturaleza distinta. Sim embargo, en ocasiones
podemos querer considerar un suconjunto de un tipo, como un tipo en sí mismo (por ejemplo,
para definir una función en él). La forma de hacerlo es definiendo un subconjunto `S`
a un tipo `↥S` (el símbolo ↥ se consigue tecleando \u-| )
Si `S` es un subconjunto del tipo `X`, los objetos de tipo `↥S` constan de dos partes:
- un objeto `x` de tipo `X`
- un objeto `hx` de tipo `x ∈ S` (es decir, una demostración, o garantía de que `x` es un elemento de `S`)
y se escribe de la forma `⟨x,h⟩` (los símbolos `⟨` y `⟩` se escriben con \< y \> )
Si tenemos un objeto `y` de tipo `↥S`, sus dos componentes (el objeto del tipo ambiente, y la
demostración de que está en el subconjunto), podemos recuperarlas usando `y.1`y `y.2` o con la
táctica `cases'`.
Además hay automáticamente una función de `↥S` a `X` (la inclusión). Dado un elemento `x : ↥S` el correspondiente elemento de `X` se denota con `↑x` (el símbolo ↑ se obtiene con \u- )
-/
-- Ejemplo: construimos la aplicación inclusión entre dos conjuntos encajados.
example (X : Type) (U V : Set X) (h : U ⊆ V) : ∃ i : ↥U → ↥V , ∀ x : U, (x : X) = (i x) := by
let i : ↥U → ↥V
· intro x
cases' x with x1 x2
fconstructor
· use x1
· apply h x2
· use i
intro x
cases' x with xv xp
rfl
/-
Así como los conjuntos se pueden definir usando la notación `{x : X | P x}`, los correspondientes
tipos se pueden construir usando `{x : X // P x}`
-/
example (X : Type) (P : X → Prop) : ↥{x : X | P x} = {x : X // P x} := by
rfl

View File

@ -0,0 +1,162 @@
import Mathlib.Tactic
/-!
# Proposiciones en Lean
Una proposición representa una afirmación que puede ser cierta o falsa.
Por ejemplo, `(P : Prop)` quiere decir que con la letra `P` vamos a representar una
cierta afirmación.
Para una proposición `P`, podemos tener (o no) la certeza de que es cierta, (porque contamos
con una demostración de la misma, o porque la asumimos como hipótesis).
Esto se escribe como `(h : P)`. Ahí estamos diciendo que h es una demostración de `P`.
# Implicaciones
Una implicación `P → Q` (el símbolo → se puede obtener tecleando \to \-> o \r )
representa el hecho de que "si `P` es cierta, entonces `Q` también lo es".
También puede interpretarse como una función, que dada una demostración para `P`
produce una demostración para `Q`.
-/
-- Ejemplo: vamos a demostrar un ejemplo muy básico: si sabemos que `P` es cierta
-- podemos demostrar que `P` es cierta:
-- para ello se puede usar la táctica `exact`
example (P : Prop) (h : P) : P := by
exact h
-- Otro ejemplo, si queremos demostrar una implicación, podemos tomar la premisa
-- como hipótesis, y demostrar la tesis. Para ello se usa la táctica `intro`
example (P Q : Prop) (hq : Q) : P → Q := by
intro hp
exact hq
-- La táctica `apply`se puede usar cuando tenemos como hipótesis una implicación `h`
-- que implica nuestro objetivo. Al usarla, el objetivo cambia y pasamos a tener que
-- demostrar la premisa de `h`
example (P Q : Prop) (hI : P → Q) (hp : P) : Q := by
apply hI
exact hp
-- La táctica `constructor` divide un objetivo que consiste de dos afirmaciones, en dos
-- objetivos separados (hay una variante más potente llamada `fconstructor`.)
example (P : Prop) : P ↔ P := by
constructor
· intro h
exact h
· intro h
exact h
-- Sirve tanto para dobles implicaciones como para conjunciones
example (P : Prop) : P → (P ∧ P) := by
intro h
constructor
· exact h
· exact h
-- Si tenemos que demostrar una disyunción, tenemos que usar `left` o `right` para decidir
-- cual de los dos lados vamos a demostrar
example (P Q : Prop) : P → P ∨ Q := by
intro h
left
exact h
example (P Q : Prop) : Q → P ∨ Q := by
intro h
right
exact h
-- Si tenemos una hipótesis que consiste de varias afirmaciones, `cases'` nos la separa en varias
-- hipótesis. Si la hipótesis es una disyunción, nos separa el objetivo en varios casos
example (P Q : Prop) (h : P ∧ Q) : Q := by
cases' h with h1 h2
exact h2
example (P Q : Prop) (h : P ∨ Q ) : P ∨ Q := by
cases' h with h h
· left
exact h
· right
exact h
/-
# Ejercicios
-/
variable (p q r s : Prop)
/-
## Demuestra la conmutatividad de ∧ y ∨
-/
example : p ∧ q ↔ q ∧ p := by
sorry
example : p ∨ q ↔ q ∨ p := by
sorry
/-
## Demuestra la asociatividad de ∧ y ∨
-/
example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) := by
sorry
example : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) := by
sorry
/-
## Demuestra la distributividad de ∧ respecto de ∨ y viceversa
p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r)
p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r)
-/
/-
## Demuestra las siguientes propiedades
(p → (q → r)) ↔ (p ∧ q → r)
((p ∨ q) → r) ↔ (p → r) ∧ (q → r)
¬(p ∨ q) ↔ ¬p ∧ ¬q
¬p ∨ ¬q → ¬(p ∧ q)
¬(p ∧ ¬p)
p ∧ ¬q → ¬(p → q)
¬p → (p → q)
(¬p ∨ q) → (p → q)
p ∨ false ↔ p
p ∧ false ↔ false
(p → q) → (¬q → ¬p)
-/
/-
## Algunas demostraciones que requieren razonamiento clásico
Estas demostraciones requieren usar algunas tácticas nuevas:
- `by_contra` asume la negación del objetivo, y tenemos que demostrar una contradicción
- `by_cases` h : P crea dos nuevos contextos: uno en el que tenemos que demostrar el objetivo
suponiendo P, y otro en el que tenemos que demostrarlo suponiendo ¬ P
- `push_neg` "mete las negaciones" en una expresión. Por ejemplo cambia ¬ (P ∧ Q) por ¬ P ∨ ¬ Q
(p → r ∨ s) → ((p → r) ∨ (p → s))
¬(p ∧ q) → ¬p ∨ ¬q
¬(p → q) → p ∧ ¬q
(p → q) → (¬p ∨ q)
(¬q → ¬p) → (p → q)
p ∨ ¬p
(((p → q) → p) → p)
-/