我正在尝试是否有可能从https://softwarefoundations.cis.upenn.edu/lf-current/Logic.html中证明evenb n = true <-> exists k, n = double k
而根本不涉及奇数。我尝试了以下操作:
Theorem evenb_double_k : forall n,
evenb n = true -> exists k, n = double k.
Proof.
intros n H. induction n as [|n' IHn'].
- exists 0. reflexivity.
- (* stuck *)
但是,归纳法一次只能处理一个自然数,exists k : nat, S n' = double k
显然是不可证明的。
n' : nat
H : evenb (S n') = true
IHn' : evenb n' = true -> exists k : nat, n' = double k
______________________________________(1/1)
exists k : nat, S n' = double k
有没有办法使感应从n变到n + 2?
答案 0 :(得分:7)
是的,绝对!让我们使用doc中的归纳原理。
From Coq Require Import Arith.
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 easy.
induction n; intuition.
Qed.
现在,我们可以像这样使用新原理(我将非标准函数与其stdlib对应项进行了交换,以便所有内容都能编译):
Theorem evenb_double_k : forall n,
Nat.even n = true -> exists k, n = Nat.double k.
Proof.
intros n Ev.
induction n as [| |n IHn _] using pair_induction.
(* the rest of the proof has been removed to not spoil the fun *)
Qed.
答案 1 :(得分:7)
有一种名为fix
的策略。我会尝试从较高的角度解释正在发生的事情,因为我认为这是一个很酷的技巧,但请注意,fix
是一把双刃剑,通常不建议使用:它取决于真正的Coq的详细级别,使证明非常脆弱,并且当它们损坏时,错误消息也很难理解。
fix foo i
,其中foo
是一个新鲜变量,而i
是一个整数,是一种策略,适用于至少有i
个自变量的目标(例如,forall n, evenb n = true -> ...
有两个:n
和证明evenb n = true
),并且假定您要证明的目标,并命名新的假设{{1 }}。 (是的,您没看错。)
foo
当然有一个陷阱:必须将假设应用于Theorem evenb_double_k : forall n,
evenb n = true -> exists k, n = double k.
Proof.
fix self 1.
(*
1 subgoal (ID 17)
self : forall n : nat,
evenb n = true -> exists k : nat, n = double k
============================
forall n : nat, evenb n = true -> exists k : nat, n = double k
*)
的适当子项(这是目标的 first 自变量,这就是n
的number参数的含义,我们说第一个参数是递减参数)。 fix self 1
的适当子项是什么?您只能通过破坏n
至少一次来获得该值。
请注意,如果您仍然决定直接应用假设n
(self
本身不是适当的子术语),Coq不会立即提出投诉。 Coq仅检查n
上的“ subterm”要求。 EDIT :您也可以随时使用命令Qed
进行检查。
Guarded
您可以近似地将 apply self. (* seems fine, all goals done. *)
Qed. (* ERROR! *)
想象为强归纳的一种形式,其中归纳假设(fix
)用于所有小于当前归纳的术语,而不仅是直接的前任。但是,此“子项”关系实际上并未出现在self
的语句中。 (这种特殊性使self
成为一种低级的危险策略。)
在fix
之后,您通常需要fix
递减参数。这是destruct
允许您的证明遵循fix
的结构的地方。下面,我们在evenb
情况下立即再次销毁。因此我们得到三种情况:S
,n = O
,n = S O
。
第一种情况很容易,第二种情况是虚无,而第三种情况是您需要在n = S (S n')
处使用“归纳假设” n' : nat
。
self
其中的某些推理是相当通用的,甚至可以将其引入自定义的归纳原理,甚至n'
,具体来说就是另一个Proof.
fix self 1.
intros n.
destruct n as [| [| n']].
- exists 0; reflexivity.
- discriminate.
- simpl. intro H.
apply self in H.
destruct H as [k Hk].
exists (S k).
rewrite Hk; reflexivity.
Qed.
。 / p>
nat
将其与Theorem
的标准归纳原理进行比较,它实际上也是一个定理,名为Theorem even_ind :
forall (P : nat -> Prop),
P O ->
(forall n, evenb n = true -> P n -> P (S (S n))) ->
forall n, evenb n = true -> P n.
。这就是nat
策略在幕后使用的方式。
nat_ind
induction
中的归纳步骤从About nat_ind.
(* nat_ind :
forall P : nat -> Prop,
P 0 ->
(forall n : nat, P n -> P (S n)) ->
forall n : nat, P n
*)
到nat_ind
,而n
的归纳步骤从S n
到even_ind
,并且有一个额外的假设说我们的数字是偶数。
n
的证明与S (S n)
相似,但它更为抽象,因为它可以概括even_ind
上的所有谓词{{1}}。
evenb_double_k
这里的另一种方法是根本不使用P
(因为应该避免使用),而是将nat
用作证明替代Proof.
intros P HO HSS.
fix self 1.
intros n.
destruct n as [| [| n']].
- intro; apply HO.
- discriminate.
- intros H. apply HSS.
+ apply H.
+ apply self.
apply H.
Qed.
原理的原始方法。这对于fix
很好,但是对于某些复杂的归纳类型,默认归纳原理太弱了,手写induction
是唯一的方法。
最后,回到even_ind
,我们可以对nat
使用新的归纳原理,而不是fix
或evenb_double_k
。现在,我们只得到两个有意义的情况,apply even_ind
和fix
,其中induction
是偶数。
O
此答案中使用的定义:
S (S n')