Lean , 66 octets
def s:_->nat->nat|(m+1)(n+1):=(n+1)*(s m n+s m(n+1))|0 0:=1|_ _:=0
Essayez-le en ligne!
Preuve d'exactitude
Essayez-le en ligne!
Explication
Libérons la fonction:
def s : nat->nat->nat
| (m+1) (n+1) := (n+1)*(s m n + s m (n+1))
| 0 0 := 1
| _ _ := 0
La fonction est définie par la correspondance de motifs et la récursivité, qui ont toutes deux un support intégré.
Nous définissons s(m+1, n+1) = (n+1) * (s(m, n) + s(m, n+1)
et s(0, 0) = 1
, qui laisse ouvert s(m+1, 0)
et s(0, n+1)
, qui sont tous deux définis pour être0
par le dernier cas.
Lean utilise la syntaxe de calcul lamdba, tout s m n
comme s(m, n)
.
Maintenant, la preuve d'exactitude: je l'ai dit de deux manières:
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
Le premier est ce qui se passe vraiment: une bijection entre [0 ... s(m, n)-1]
et les surjections de [0 ... m-1]
sur[0 ... n-1]
.
Le second est la façon dont il est généralement indiqué, s(m, n)
c'est la cardinalité des surjections de [0 ... m-1]
sur [0 ... n-1]
.
Lean utilise la théorie des types comme fondement (au lieu de la théorie des ensembles). En théorie des types, chaque objet a un type qui lui est inhérent. nat
est le type de nombres naturels, et la déclaration qui 0
est un nombre naturel est exprimée par 0 : nat
. Nous disons que 0
c'est de type nat
, et cela nat
a0
comme habitant.
Les propositions (déclarations / assertions) sont également des types: leur habitant est une preuve de la proposition.
def
: Nous allons introduire une définition (car une bijection est vraiment une fonction, pas seulement une proposition).
correctness
: le nom de la définition
∀ m n
: pour chaque m
et n
(Lean déduit automatiquement que leur type est nat
, à cause de ce qui suit).
fin (s m n)
est le type de nombres naturels inférieur à s m n
. Pour faire un habitant, on fournit un nombre naturel et une preuve qu'il est plus petit que s m n
.
A ≃ B
: bijection entre le type A
et le type B
. Dire la bijection est trompeur, car il faut en fait fournir la fonction inverse.
{ f : fin m → fin n // function.surjective f }
le type de surjections de fin m
à fin n
. Cette syntaxe construit un sous-type à partir du type fin m → fin n
, c'est-à-dire le type de fonctions de fin m
à fin n
. La syntaxe est { var : base type // proposition about var }
.
λ m
: ∀ var, proposition / type involving var
est vraiment une fonction qui prend var
en entrée, donc λ m
introduit l'entrée. ∀ m n,
est raccourci pour∀ m, ∀ n,
nat.rec_on m
: faire récursivité sur m
. Pour définir quelque chose pour m
, définir la chose pour 0
et puis donner la chose pour k
, construire la chose pour k+1
. On remarquerait que cela est similaire à l'induction, et c'est en fait le résultat de la correspondance Church-Howard . La syntaxe est 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")
.
Hé, ça devient long et je ne suis qu'en troisième ligne de correctness
...