我有两个Fibonacci实现,如下所示,我想要证明它们在功能上是等价的。
我已经证明了关于自然数的属性,但是这个练习需要另一种我无法弄清楚的方法。
我正在使用的教科书介绍了Coq的以下语法,因此应该可以使用这种表示法证明相等:
<definition> ::= <keyword> <identifier> : <statement> <proof>
<keyword> ::= Proposition | Lemma | Theorem | Corollary
<statement> ::= {<quantifier>,}* <expression>
<quantifier> ::= forall {<identifier>}+ : <type>
| forall {({<identifier>}+ : <type>)}+
<proof> ::= Proof. {<tactic>.}* <end-of-proof>
<end-of-proof> ::= Qed. | Admitted. | Abort.
以下是两个实现:
Fixpoint fib_v1 (n : nat) : nat :=
match n with
| 0 => O
| S n' => match n' with
| O => 1
| S n'' => (fib_v1 n') + (fib_v1 n'')
end
end.
Fixpoint visit_fib_v2 (n a1 a2 : nat) : nat :=
match n with
| 0 => a1
| S n' => visit_fib_v2 n' a2 (a1 + a2)
end.
很明显,这些函数为基本案例n = 0
计算相同的值,但是归纳案例要难得多吗?
我已经尝试过证明以下引理,但我陷入了归纳的情况:
Lemma about_visit_fib_v2 :
forall i j : nat,
visit_fib_v2 i (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j))) = (fib_v1 (add_v1 i (S j))).
Proof.
induction i as [| i' IHi'].
intro j.
rewrite -> (unfold_visit_fib_v2_0 (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j)))).
rewrite -> (add_v1_0_n (S j)).
reflexivity.
intro j.
rewrite -> (unfold_visit_fib_v2_S i' (fib_v1 (S j)) ((fib_v1 j) + (fib_v1 (S j)))).
Admitted.
其中:
Fixpoint add_v1 (i j : nat) : nat :=
match i with
| O => j
| S i' => S (add_v1 i' j)
end.
答案 0 :(得分:4)
@ larsr&#39; s answer启发了这个替代答案。
首先,让我们定义fib_v2
:
Require Import Coq.Arith.Arith.
Definition fib_v2 n := visit_fib_v2 n 0 1.
然后,我们需要一个引理,这与@ larsr的答案中的fib_v2_lemma
相同。我将此处包括在内以保持一致性并显示替代证据。
Lemma visit_fib_v2_main_property n: forall a0 a1,
visit_fib_v2 (S (S n)) a0 a1 =
visit_fib_v2 (S n) a0 a1 + visit_fib_v2 n a0 a1.
Proof.
induction n; intros a0 a1; auto with arith.
change (visit_fib_v2 (S (S (S n))) a0 a1) with
(visit_fib_v2 (S (S n)) a1 (a0 + a1)).
apply IHn.
Qed.
根据larsr的评论中的建议,visit_fib_v2_main_property
引理也可以通过以下令人印象深刻的单行证明:
now induction n; firstorder.
由于Fibonacci系列中数字的性质,定义替代归纳原理非常方便:
Lemma pair_induction (P : nat -> Prop) :
P 0 ->
P 1 ->
(forall n, P n -> P (S n) -> P (S (S n))) ->
forall n, P n.
Proof.
intros H0 H1 Hstep n.
enough (P n /\ P (S n)) by tauto.
induction n; intuition.
Qed.
pair_induction
原则基本上说,如果我们可以为P
和0
证明某些属性1
,并且对于每个自然数k > 1
,我们可以证明P k
在P (k - 1)
和P (k - 2)
成立的假设下成立,然后我们可以证明forall n, P n
。
使用我们的自定义归纳原理,我们得到如下证据:
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
induction n using pair_induction.
- reflexivity.
- reflexivity.
- unfold fib_v2.
rewrite visit_fib_v2_main_property.
simpl; auto.
Qed.
答案 1 :(得分:3)
警告提示:在接下来的内容中,我将尝试展示这种证据的主要概念,因此我不会坚持使用Coq的某些子集而我不会手动算术。相反,我会使用一些证明自动化,即。 ring
战术。Require Import Arith. (* for `ring` tactic *)
Lemma fib_v1_eq_fib2_generalized n : forall a0 a1,
visit_fib_v2 (S n) a0 a1 = a0 * fib_v1 n + a1 * fib_v1 (S n).
Proof.
induction n; intros a0 a1.
- simpl; ring.
- change (visit_fib_v2 (S (S n)) a0 a1) with
(visit_fib_v2 (S n) a1 (a0 + a1)).
rewrite IHn. simpl; ring.
Qed.
。但是,您可以随意提出其他问题,这样您就可以将证明转换为适合您目的的证据。
我认为从一些概括开始更容易:
ring
如果使用rewrite
并不适合您的需求,您可以使用Arith
模块的词条执行多个Definition fib_v2 n := visit_fib_v2 n 0 1.
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
destruct n.
- reflexivity.
- unfold fib_v2. rewrite fib_v1_eq_fib2_generalized.
ring.
Qed.
步骤。
现在,让我们实现目标:
y
答案 2 :(得分:3)
安东的证据非常漂亮,比我的好,但无论如何它可能都很有用。
我没有提出泛化引理,而是加强了归纳假设。
说原始目标是Q n
。然后我用
cut (Q n /\ Q (S n))
来自
Q n
到
Q n /\ Q (S n)
这个新目标通常意味着最初的目标,但随之而来的是归纳假设变得更强,因此可以重写更多。
IHn : Q n /\ Q (S n)
=========================
Q (S n) /\ Q (S (S n))
这个想法在软件基础中解释,其中一个证明了偶数。
因为这个提议通常很长,所以我制定了一个Ltac
策略来命名这个漫长而混乱的术语。
Ltac nameit Q :=
match goal with [ _:_ |- ?P ?n] => let X := fresh Q in remember P as X end.
Require Import Ring Arith.
(顺便说一下,我将vistit_fib_v2
重命名为fib_v2
。)
我需要一个关于fib_v2的一步的引理。
Lemma fib_v2_lemma: forall n a b, fib_v2 (S (S n)) a b = fib_v2 (S n) a b + fib_v2 n a b.
intro n.
pattern n.
nameit Q.
cut (Q n /\ Q (S n)).
tauto. (* Q n /\ Q (S n) -> Q n *)
induction n.
split; subst; simpl; intros; ring. (* Q 0 /\ Q 1 *)
split; try tauto. (* Q (S n) *)
subst Q. (* Q (S (S n)) *)
destruct IHn as [H1 H2].
assert (L1: forall n a b, fib_v2 (S n) a b = fib_v2 n b (a+b)) by reflexivity.
congruence.
Qed.
congruence
策略处理一系列A = B
假设和重写后的目标。
证明定理非常相似。
Theorem fib_v1_fib_v2 : forall n, fib_v1 n = fib_v2 n 0 1.
intro n.
pattern n.
nameit Q.
cut (Q n /\ Q (S n)).
tauto. (* Q n /\ Q (S n) -> Q n *)
induction n.
split; subst; simpl; intros; ring. (* Q 0 /\ Q 1 *)
split; try tauto. (* Q (S n) *)
subst Q. (* Q (S (S n)) *)
destruct IHn as [H1 H2].
assert (fib_v1 (S (S n)) = fib_v1 (S n) + fib_v1 n) by reflexivity.
assert (fib_v2 (S (S n)) 0 1 = fib_v2 (S n) 0 1 + fib_v2 n 0 1) by
(pose fib_v2_lemma; congruence).
congruence.
Qed.
所有锅炉板代码都可以采用策略,但我不想对Ltac
发疯,因为那不是问题所在。
答案 3 :(得分:2)
此证明脚本仅显示证明结构。解释证据的概念可能很有用。
Require Import Ring Arith Psatz. (* Psatz required by firstorder *)
Theorem fibfib: forall n, fib_v2 n 0 1 = fib_v1 n.
Proof with (intros; simpl in *; ring || firstorder).
assert (H: forall n a0 a1, fib_v2 (S n) a0 a1 = a0 * (fib_v1 n) + a1 * (fib_v1 (S n))).
{ induction n... rewrite IHn; destruct n... }
destruct n; try rewrite H...
Qed.
答案 4 :(得分:2)
有一个非常强大的库 - math-comp用Ssreflect正式证明语言编写,而该语言又基于Coq。在这个答案中,我提出了一个使用其设施的版本。这只是this开发的一个简化部分。所有功劳都归原作者所有。
让我们做一些导入和两个函数的定义,math-comp(ssreflect)样式:
From mathcomp
Require Import ssreflect ssrnat ssrfun eqtype ssrbool.
Fixpoint fib_rec (n : nat) {struct n} : nat :=
if n is n1.+1 then
if n1 is n2.+1 then fib_rec n1 + fib_rec n2
else 1
else 0.
Fixpoint fib_iter (a b n : nat) {struct n} : nat :=
if n is n1.+1 then
if n1 is n2.+1
then fib_iter b (b + a) n1
else b
else a.
表达斐波那契数字基本性质的辅助引理:
Lemma fib_iter_property : forall n a b,
fib_iter a b n.+2 = fib_iter a b n.+1 + fib_iter a b n.
Proof.
case=>//; elim => [//|n IHn] a b; apply: IHn.
Qed.
现在,让我们来解决两个实现的等价问题。
这里的主要思想,即将以下证明与其他证据区分开来,截至本文撰写时,我们已经表现出来
类似complete induction,使用elim: n {-2}n (leqnn n)
。这给了我们以下(强)归纳假设:
IHn : forall n0 : nat, n0 <= n -> fib_rec n0 = fib_iter 0 1 n0
这是主要的引理及其证明:
Lemma fib_rec_eq_fib_iter : fib_rec =1 fib_iter 0 1.
Proof.
move=>n; elim: n {-2}n (leqnn n)=> [n|n IHn].
by rewrite leqn0; move/eqP=>->.
case=>//; case=>// n0; rewrite ltnS=> ltn0n.
rewrite fib_iter_property.
by rewrite <- (IHn _ ltn0n), <- (IHn _ (ltnW ltn0n)).
Qed.
答案 5 :(得分:0)
这是另一个答案,类似于one using mathcomp,但是这个使用&#34; vanilla&#34; COQ。
首先,我们需要一些导入,其他定义和一些帮助引理:
Require Import Coq.Arith.Arith.
Definition fib_v2 n := visit_fib_v2 n 0 1.
Lemma visit_fib_v2_property n: forall a0 a1,
visit_fib_v2 (S (S n)) a0 a1 =
visit_fib_v2 (S n) a0 a1 + visit_fib_v2 n a0 a1.
Proof. now induction n; firstorder. Qed.
Lemma fib_v2_property n:
fib_v2 (S (S n)) = fib_v2 (S n) + fib_v2 n.
Proof. apply visit_fib_v2_property. Qed.
为证明主要引理,我们将使用标准有根据的归纳lt_wf_ind
原则来表示<
关系的自然数(a.k.a.完全归纳):
这次我们只需证明一个子目标,因为n = 0
完全归纳的情况总是空洞的。不出所料,我们的归纳假设看起来像这样:
IH : forall m : nat, m < n -> fib_v1 m = fib_v2 m
以下是证据:
Lemma fib_v1_eq_fib2 n :
fib_v1 n = fib_v2 n.
Proof.
pattern n; apply lt_wf_ind; clear n; intros n IH.
do 2 (destruct n; trivial).
rewrite fib_v2_property.
rewrite <- !IH; auto.
Qed.