Lean , 66 bytes
def s:_->nat->nat|(m+1)(n+1):=(n+1)*(s m n+s m(n+1))|0 0:=1|_ _:=0
Pruébalo en línea!
Prueba de corrección
Pruébalo en línea!
Explicación
Dejemos de ungolf la función:
def s : nat->nat->nat
| (m+1) (n+1) := (n+1)*(s m n + s m (n+1))
| 0 0 := 1
| _ _ := 0
La función se define por la coincidencia de patrones y la recursividad, que tienen soporte incorporado.
Definimos s(m+1, n+1) = (n+1) * (s(m, n) + s(m, n+1)
y s(0, 0) = 1
, que deja abierto s(m+1, 0)
y s(0, n+1)
, los cuales se definen como 0
el último caso.
Lean utiliza la sintaxis de cálculo lamdba, por lo que s m n
es así s(m, n)
.
Ahora, la prueba de corrección: lo dije de dos maneras:
def correctness : ∀ m n, fin (s m n) ≃ { f : fin m → fin n // function.surjective f } :=
λ m, nat.rec_on m (λ n, nat.cases_on n s_zero_zero (λ n, s_zero_succ n)) $
λ m ih n, nat.cases_on n (s_succ_zero m) $ λ n,
calc fin (s (nat.succ m) (nat.succ n))
≃ (fin (n + 1) × (fin (s m n + s m (n + 1)))) :
(fin_prod _ _).symm
... ≃ (fin (n + 1) × (fin (s m n) ⊕ fin (s m (n + 1)))) :
equiv.prod_congr (equiv.refl _) (fin_sum _ _).symm
... ≃ (fin (n + 1) × ({f : fin m → fin n // function.surjective f} ⊕
{f : fin m → fin (n + 1) // function.surjective f})) :
equiv.prod_congr (equiv.refl _) (equiv.sum_congr (ih n) (ih (n + 1)))
... ≃ {f // function.surjective f} : s_aux m n
def correctness_2 (m n : nat) : s m n = fintype.card { f : fin m → fin n // function.surjective f } :=
by rw fintype.of_equiv_card (correctness m n); simp
El primero es lo que realmente está sucediendo: una biyección entre [0 ... s(m, n)-1]
y las sobrejeturas de [0 ... m-1]
sobre [0 ... n-1]
.
El segundo es cómo se suele decir, que s(m, n)
es la cardinalidad de las sobreyecciones de [0 ... m-1]
sobre [0 ... n-1]
.
Lean utiliza la teoría de tipos como base (en lugar de la teoría de conjuntos). En la teoría de tipos, cada objeto tiene un tipo inherente. nat
es el tipo de números naturales, y la declaración que 0
es un número natural se expresa como 0 : nat
. Decimos que 0
es de tipo nat
, y quenat
tiene0
como habitante.
Las proposiciones (declaraciones / aserciones) también son tipos: su habitante es una prueba de la proposición.
def
: Vamos a introducir una definición (porque una biyección es realmente una función, no solo una proposición).
correctness
: el nombre de la definición
∀ m n
: por cada m
y n
(Lean infiere automáticamente que su tipo es nat
, debido a lo que sigue).
fin (s m n)
es el tipo de números naturales que es menor que s m n
. Para hacer un habitante, uno proporciona un número natural y una prueba de que es más pequeño que s m n
.
A ≃ B
: biyección entre el tipo A
y el tipo B
. Decir bijection es engañoso, ya que uno realmente tiene que proporcionar la función inversa.
{ f : fin m → fin n // function.surjective f }
El tipo de sobrejeciones de fin m
a fin n
. Esta sintaxis crea un subtipo a partir del tipo fin m → fin n
, es decir, el tipo de funciones desde fin m
hasta fin n
. La sintaxis es la siguiente { var : base type // proposition about var }
.
λ m
: ∀ var, proposition / type involving var
es realmente una función que toma var
como entrada, por lo que λ m
introduce la entrada. ∀ m n,
es abreviatura de∀ m, ∀ n,
nat.rec_on m
: hacer recursividad en m
. Para definir algo para m
, definir la cosa para 0
y luego dar la cosa para k
, construir la cosa para k+1
. Uno notaría que esto es similar a la inducción, y de hecho esto es el resultado de la correspondencia entre la Iglesia y Howard . La sintaxis es la siguiente nat.rec_on var (thing when var is 0) (for all k, given "thing when k is k", build thing when var is "k+1")
.
Je, esto se está haciendo largo y solo estoy en la tercera línea de correctness
...