Lean , 66 bytes
def s:_->nat->nat|(m+1)(n+1):=(n+1)*(s m n+s m(n+1))|0 0:=1|_ _:=0
Experimente online!
Prova de correção
Experimente online!
Explicação
Vamos desfazer a função:
def s : nat->nat->nat
| (m+1) (n+1) := (n+1)*(s m n + s m (n+1))
| 0 0 := 1
| _ _ := 0
A função é definida pela correspondência e recursão de padrão, ambas com suporte interno.
Definimos s(m+1, n+1) = (n+1) * (s(m, n) + s(m, n+1)
e s(0, 0) = 1
, que deixa em aberto s(m+1, 0)
e s(0, n+1)
, os quais são definidos como sendo 0
no último caso.
O Lean usa a sintaxe do cálculo lamdba, assim s m n
é s(m, n)
.
Agora, a prova da correção: afirmei de duas maneiras:
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
O primeiro é o que realmente está acontecendo: uma bijeção entre [0 ... s(m, n)-1]
e as exceções de [0 ... m-1]
para [0 ... n-1]
.
O segundo é como é geralmente afirmado, que s(m, n)
é a cardinalidade das sobras de [0 ... m-1]
para [0 ... n-1]
.
O Lean usa a teoria dos tipos como base (em vez da teoria dos conjuntos). Na teoria dos tipos, todo objeto tem um tipo que é inerente a ele. nat
é o tipo de números naturais e a declaração que 0
é um número natural é expressa como 0 : nat
. Dizemos que 0
é do tipo nat
, e que nat
tem 0
como habitante.
Proposições (declarações / asserções) também são tipos: seu habitante é uma prova da proposição.
def
: Vamos introduzir uma definição (porque uma bijeção é realmente uma função, não apenas uma proposição).
correctness
: o nome da definição
∀ m n
: for every m
e n
(o Lean deduz automaticamente o seu tipo nat
, pelo que se segue).
fin (s m n)
é o tipo de números naturais menor que s m n
. Para formar um habitante, fornece-se um número natural e uma prova de que é menor que s m n
.
A ≃ B
: bijeção entre o tipo A
e o tipo B
. Dizer bijeção é enganoso, pois é preciso fornecer a função inversa.
{ f : fin m → fin n // function.surjective f }
o tipo de rejeições de fin m
para fin n
. Essa sintaxe cria um subtipo a partir do tipo fin m → fin n
, ou seja, o tipo de funções de fin m
para fin n
. A sintaxe é { var : base type // proposition about var }
.
λ m
: ∀ var, proposition / type involving var
é realmente uma função que recebe var
como entrada, então λ m
introduz a entrada. ∀ m n,
é uma mão curta para∀ m, ∀ n,
nat.rec_on m
: faça recursão em m
. Para definir algo para m
, defina a coisa para 0
e, em seguida, dê a coisa para k
, construa a coisa para k+1
. Alguém poderia notar que isso é semelhante à indução e, de fato, isso é resultado da correspondência entre Igreja e Howard . A sintaxe é 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")
.
Heh, isso está ficando longo e eu estou apenas na terceira linha de correctness
...