在software foundations–Logic foudations–IndProp中,有一个证明pumping lemma
的练习,本文介绍了一种证明方法。
辅助引理
为了证明pumping lemma
,除了software foundations–Logic foudations–IndProp中已经给出的引理外(napp_star, pumping_constant_0_false ),还使用了下边两个新定义的引理。
le_plus_both
Lemma le_plus_both: forall (a b c d : nat), a <= c -> b <= d -> a + b <= c + d.
Proof.
intros a.
induction a.
-intros b c d H1 H2. simpl. apply le_trans with (n := d). apply H2.
rewrite -> plus_comm. apply le_plus_l.
-intros b c d H1 H2. destruct c.
--inversion H1.
--simpl. apply n_le_m__Sn_le_Sm. apply Sn_le_Sm__n_le_m in H1.
apply (IHa b c d H1) in H2. apply H2.
Qed.
le_false_rev
Lemma le_false_rev: forall (a b : nat), (a <=? b = false) -> b <= a.
Proof.
intros a.
induction a.
-intros b H. simpl in H. discriminate.
-intros b H. destruct b.
--simpl in H. apply O_le_n.
--simpl in H. apply n_le_m__Sn_le_Sm. apply IHa. apply H.
Qed.
证明思路
pumping lemma的表述如下
Lemma pumping : forall T (re : reg_exp T) s,
s =~ re ->
pumping_constant re <= length s ->
exists s1 s2 s3,
s = s1 ++ s2 ++ s3 /\
s2 <> [] /\
length s1 + length s2 <= pumping_constant re /\
forall m, s1 ++ napp m s2 ++ s3 =~ re.
可以看到,基本思路是对s =~ re
的可能情况进行归纳证明。
在归纳证明的过程中,MEmpty
, MChar
, MStar0
的可能情况证明很简单,MUinonL
和MUnionR
稍微困难一些,MAdd
和MStar
是最困难的部分。 我们首先讨论Madd
的证明思路。
MAdd
Madd子目标如下
pumping_constant re1 + pumping_constant re2 <= length (s1 ++ s2) ->
exists s0 s3 s4 : list T,
s1 ++ s2 = s0 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s0 + length s3 <= pumping_constant re1 + pumping_constant re2 /\
(forall m : nat, s0 ++ napp m s3 ++ s4 =~ App re1 re2)
可以看到,我们可以使用的假设为pumping_constant re1 + pumping_constant re2 <= length (s1 ++ s2)
, 然后存在两个归纳假设
Hmatch1 : s1 =~ re1
Hmatch2 : s2 =~ re2
IH1 : pumping_constant re1 <= length s1 ->
exists s2 s3 s4 : list T,
s1 = s2 ++ s3 ++ s4 /\ s3 <> [ ] /\ length s2 + length s3 <= pumping_constant re1 /\ (forall m : nat, s2 ++ napp m s3 ++ s4 =~ re1)
IH2 : pumping_constant re2 <= length s2 ->
exists s1 s3 s4 : list T,
s2 = s1 ++ s3 ++ s4 /\ s3 <> [ ] /\ length s1 + length s3 <= pumping_constant re2 /\ (forall m : nat, s1 ++ napp m s3 ++ s4 =~ re2)
为了利用归纳假设,我们需要构建出pumping_constant re1 <= length s1
或者pumping_constant re2 <= length s2
这样的假设来。根据pumping_constant re1 + pumping_constant re2 <= length (s1 ++ s2)
,以及本章前部分证明的add_le_case
引理,可以得到如下假设。
H : pumping_constant re1 <= length s1 \/ pumping_constant re2 <= length s2
之后进行分类讨论,分别在pumping_constant re1 <= length s1
和pumping_constant re2 <= length s2
的完成证明。
pumping_constant re1 <= length s1
根据该假设(pumping_constant re1 <= length s1
)以及归纳假设IH1
,可以得到归纳假设的结论部分(注意这里面只有s1
是一个具体的值,其他都是未确定的遍历),假设其为H3
.
H3: exists s2 s3 s4 : list T,
s1 = s2 ++ s3 ++ s4 /\ s3 <> [ ] /\ length s2 + length s3 <= pumping_constant re1 /\ (forall m : nat, s2 ++ napp m s3 ++ s4 =~ re1)
我们给其指定具体值x, x0, x1
,则得到
H3: s1 = x ++ x0 ++ x1 /\ x0 <> [ ] /\ length x + length x0 <= pumping_constant re1 /\ (forall m : nat, x ++ napp m x0 ++ x1 =~ re1)
我们基于这个结论来构造我们的目标
exists s0 s3 s4 : list T,
s1 ++ s2 = s0 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s0 + length s3 <= pumping_constant re1 + pumping_constant re2 /\
(forall m : nat, s0 ++ napp m s3 ++ s4 =~ App re1 re2)
首先根据MApp
的规则,s1 =~ re1, s2 =~ re2 -> s1 ++ s2 =~ App re1 re2
,在H3
中我们已经有x ++ napp m x0 ++ x1 =~ re1
, 并且s2=~ re2
. 因此我们指定目标中s0 = x, s3 = x0, s4 = x1 + s2
. 后续证明就较为简单了.
pumping_constant re2 <= length s2
根据该假设(pumping_constant re2 <= length s2
)以及归纳假设IH2
,同样可以得到归纳假设的结论部分(注意这里面只有s2
是一个具体的值,其他都是未确定的遍历),假设其为H4
.
H4 : exists s1 s3 s4 : list T,
s2 = s1 ++ s3 ++ s4 /\ s3 <> [ ] /\ length s1 + length s3 <= pumping_constant re2 /\ (forall m : nat, s1 ++ napp m s3 ++ s4 =~ re2)
同样给其指定具体值x, x0, x1
得到
H4: s2 = x ++ x0 ++ x1 /\ x0 <> [ ] /\ length x + length x0 <= pumping_constant re2 /\ (forall m : nat, x ++ napp m x0 ++ x1 =~ re2)
这里的x ++ napp m x0 ++ x1 =~ re2
,因此根据MApp
的规则,和pumping_constant re1 <= length s1
的情况不同, 我们需要指定目标中s0 = s1 ++ x, s3 = x0, s4 = x1
. 然而,后续的证明还是存在问题,因为我们难以证明
length (s1 ++ x) + length x0 <= pumping_constant re1 + pumping_constant re2
根据已有的结论length x + length x0 <= pumping_constant re2
, 我们可以将该目标转换为证明length s1 <= pumping_constant re1
. 但显然,这个结论是证明不了的,因此按照这种方式进行证明存在一定问题。
从上述分析中可以看到,length s1 <= pumping_constant re1
是否成立影响了证明过程,我们可以考虑对其进行分类讨论。
如果length s1 <= pumping_constant re1
成立,则我们可以继续上述证明过程,后续证明类似对pumping_constant re1 <= length s1
情况的证明。
如果length s1 <= pumping_constant re1
不成立,则必有pumping_constant re1 <= length s1
成立(根据辅助引理小节证明的le_false_rev引理可以得到)。在这种情况下,则可以按照和前一小节pumping_constant re1 <= length s1
完全一致的方法完成证明。
按照这个分类讨论的思路,即可在pumping_constant re2 <= length s2
成立的情况下完成证明。分类讨论的具体方法,则是使用assert
引入如下假设H
并进行证明,然后再使用destruct
对该假设进行分类讨论。
-- assert(H : (pumping_constant re1 <=? length s1) = true \/ (length s1 <=? pumping_constant re1) = true).
{
destruct (pumping_constant re1 <=? length s1) eqn:E.
* left. reflexivity.
* right. apply le_false_rev in E. rewrite -> leb_iff. apply E.
}
MStar
MStar子目标中存在如下假设
Hmatch1 : s1 =~ re
Hmatch2 : s2 =~ Star re
IH1 : pumping_constant re <= length s1 ->
exists s2 s3 s4 : list T,
s1 = s2 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s2 + length s3 <= pumping_constant re /\
(forall m : nat, s2 ++ napp m s3 ++ s4 =~ re)
IH2 : pumping_constant (Star re) <= length s2 ->
exists s1 s3 s4 : list T,
s2 = s1 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s1 + length s3 <= pumping_constant (Star re) /\
(forall m : nat, s1 ++ napp m s3 ++ s4 =~ Star re)
目标如下
pumping_constant (Star re) <= length (s1 ++ s2) ->
exists s0 s3 s4 : list T,
s1 ++ s2 = s0 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s0 + length s3 <= pumping_constant (Star re) /\
(forall m : nat, s0 ++ napp m s3 ++ s4 =~ Star re)
根据IndProp前边部分已经证明的引理napp_star
我们可以看到指定s0 = [], s3 = s1, s4 = s2
即可证明forall m : nat, s0 ++ napp m s3 ++ s4 =~ Star re
. 然而,这其中困难的地方在于我们难以证明s1 <> []
,因此需要对s1
进行分类讨论,讨论s1 = []
和s1 <> [](s1 = x :: t)
的情况。(使用destruct
)。
s1 = []
该情况下目标可以转换为
pumping_constant (Star re) <= length s2 ->
exists s0 s3 s4 : list T,
s2 = s0 ++ s3 ++ s4 /\
s3 <> [ ] /\
length s0 + length s3 <= pumping_constant (Star re) /\
(forall m : nat, s0 ++ napp m s3 ++ s4 =~ Star re)
这同归纳假设IH2
是完全一致的,因此使用apply IH2
即可证明。
s1 = x :: t
该情况可以继续上述证明思路,我们继续指定目标中s0 = [], s3 = s1, s4 = s2
. 此时很容易证明s1 <> []
,然而无法证明
length s1 <= pumping_constant (Star re)
上述目标化简得到
length s1 <= pumping_constant re
因此,我们同样考虑使用分类讨论的方法来完成证明,即分为length s1 <= pumping_constant re
成立和pumping_constant re <= length s1
成立两种情况来进行证明。
length s1 <= pumping_constant re
该情况成立时,可以继续按照指定目标中s0 = [], s3 = s1, s4 = s2
的方法来完成后续证明,后续证明较为容易。
pumping_constant re <= length s1
该情况成立时,不能按照指定目标中s0 = [], s3 = s1, s4 = s2
的方法来进行证明,因此,我们给目标中存在量词修饰的变量指定具体值的时机不能在对length s1 <= pumping_constant re
是否成立进行分类讨论之前。
该情况成立时的证明过程同上述MApp小节证明过程中pumping_constant re1 <= length s1
的情况几乎一致,唯一的区别是最后使用MStarApp
规则来构建x + napp m x0 + x1 + s2 = Start re
(因为x + napp m x0 + x1 =~ re
, 并且re2 =~ re
).
分类讨论的方法还是同MApp小节的pumping_constant re2 <= length s2
的情况中使用的方法一致,使用assert
引入新假设来并使用destruct
来实现。
assert(Hd : (pumping_constant re <=? length s) = true \/ (length s <=? pumping_constant re) = true).
{
destruct (pumping_constant re <=? length s) eqn:E.
*left. reflexivity.
*right. apply le_false_rev in E. rewrite -> leb_iff. apply E.
}
其他情况
其他子目标的证明可以在coq中运行如下证明过程来详细了解,记得将辅助引理部分的引理加入。
证明
Lemma pumping : forall T (re : reg_exp T) s,
s =~ re ->
pumping_constant re <= length s ->
exists s1 s2 s3,
s = s1 ++ s2 ++ s3 /\
s2 <> [] /\
length s1 + length s2 <= pumping_constant re /\
forall m, s1 ++ napp m s2 ++ s3 =~ re.
Proof.
intros T re s Hmatch.
induction Hmatch
as [ | x | s1 re1 s2 re2 Hmatch1 IH1 Hmatch2 IH2
| s1 re1 re2 Hmatch IH | re1 s2 re2 Hmatch IH
| re | s1 s2 re Hmatch1 IH1 Hmatch2 IH2 ].
(* MEmpty *)
- simpl. intros contra. inversion contra.
(* MChar *)
- simpl. intros contra. inversion contra. inversion H0.
(* MAdd *)
- simpl. intros H. rewrite -> app_length in H. apply add_le_cases in H.
destruct H as [H1 | H2].
(* MAdd-1 pumping_constant re1 <= length s1 *)
-- apply IH1 in H1. destruct H1 as (x & x0 & x1 & H). destruct H as (H1 & H2 & H3 & H4).
exists x. exists x0. exists (x1 ++ s2). split;[|split ;[|split]].
* rewrite -> app_assoc. rewrite -> app_assoc. rewrite <- (app_assoc T x x0 x1).
rewrite <- H1. reflexivity.
* apply H2.
* apply le_trans with (o := pumping_constant re1 + pumping_constant re2) in H3. apply H3.
apply le_plus_l.
* intros m. rewrite -> app_assoc. rewrite -> app_assoc.
rewrite <- (app_assoc T x (napp m x0) x1). apply MApp. apply H4. apply Hmatch2.
(* MAdd-2 pumping_constant re2 <= length s2 *)
(* 引入一个假设,进行分类讨论 *)
-- assert(H : (pumping_constant re1 <=? length s1) = true \/ (length s1 <=? pumping_constant re1) = true).
{
destruct (pumping_constant re1 <=? length s1) eqn:E.
* left. reflexivity.
* right. apply le_false_rev in E. rewrite -> leb_iff. apply E.
}
destruct H as [H3 | H4].
(* 和MAdd-1完全一致 pumping_constant re1 <= length s1 *)
* apply leb_iff in H3. apply IH1 in H3. destruct H3 as (x & x0 & x1 & H). destruct H as (H3 & H4 & H5 & H6).
exists x, x0, (x1 ++ s2). split;[|split ;[|split]].
** rewrite -> app_assoc. rewrite -> app_assoc. rewrite <- (app_assoc T x x0 x1).
rewrite <- H3. reflexivity.
** apply H4.
** apply le_trans with (o := pumping_constant re1 + pumping_constant re2) in H5. apply H5.
apply le_plus_l.
** intros m. rewrite -> app_assoc. rewrite -> app_assoc.
rewrite <- (app_assoc T x (napp m x0) x1). apply MApp. apply H6. apply Hmatch2.
(* length s1 <= pumping_constant re1 && pumping_constant re2 <= length s2 *)
* apply leb_iff in H4. apply IH2 in H2. destruct H2 as (x & x0 & x1 & H). destruct H as (H1 & H2 & H3 & H5).
exists (s1 ++ x), x0, x1. split;[|split ;[|split]].
** rewrite <- app_assoc. rewrite <- H1. reflexivity.
** apply H2.
** rewrite -> app_length. rewrite <- plus_assoc. apply le_plus_both.
apply H4. apply H3.
** intros m. rewrite <- app_assoc. apply MApp. apply Hmatch1. apply H5.
(* MUninoL *)
- simpl. intros H. apply plus_le in H. destruct H as [H1 H2]. apply IH in H1.
destruct H1. destruct H. destruct H. destruct H as [H3 [H4 H5]].
exists x. exists x0. exists x1. split. apply H3. split. apply H4. split. destruct H5 as [H6 H7].
* apply le_trans with (o := pumping_constant re1 + pumping_constant re2) in H6. apply H6. apply le_plus_l.
* intros m. apply MUnionL. apply H5.
(* MUninoR *)
- simpl. intros H. apply plus_le in H. destruct H as [H1 H2]. apply IH in H2.
destruct H2 as (x & x0 & x1 & H). destruct H as (H3 & H4 & H5 & H6).
exists x, x0, x1. split. apply H3. split. apply H4. split.
* apply le_trans with (o := pumping_constant re1 + pumping_constant re2) in H5. apply H5.
rewrite -> plus_comm. apply le_plus_l.
* intros m. apply MUnionR. apply H6.
(* MStar0 *)
- simpl. intros H. inversion H. apply pumping_constant_0_false in H1. destruct H1.
(* MStarApp *)
- simpl. destruct s1.
-- simpl. apply IH2.
-- intros H. remember (x::s1) as s. simpl in H.
(* 引入一个假设,进行分类讨论 *)
assert(Hd : (pumping_constant re <=? length s) = true \/ (length s <=? pumping_constant re) = true).
{
destruct (pumping_constant re <=? length s) eqn:E.
*left. reflexivity.
*right. apply le_false_rev in E. rewrite -> leb_iff. apply E.
}
destruct Hd as [Ha | Hb].
* apply leb_iff in Ha. apply IH1 in Ha. destruct Ha as (x0 & x1 & x2 & Ha & Hb & Hc & Hd).
exists x0, x1, (x2 ++ s2). split. rewrite -> app_assoc. rewrite -> app_assoc.
rewrite <- (app_assoc T x0 x1 x2). rewrite <- Ha. reflexivity. split. apply Hb.
split. apply Hc. intros m. rewrite -> app_assoc. rewrite -> app_assoc.
rewrite <- (app_assoc T x0 (napp m x1) x2). apply MStarApp. apply Hd. apply Hmatch2.
* apply leb_iff in Hb. exists nil, s, s2. simpl. split. reflexivity. split.
** unfold not. intros Hfalse. rewrite -> Heqs in Hfalse. discriminate.
** split. apply Hb. intros m. apply napp_star. apply Hmatch1. apply Hmatch2.
Qed.