ひとり勉強会

ひとり楽しく勉強会

自然数とリスト: exists (3.6)

ここまで forall, ->, and, or, not のCoqでの使い方を見ました。Wikipedia一階述語論理のページなどを見てみると、もう一種類、この手の論理結合子があります。∃(存在する)。Coq では、exists と書きます。

Theorem even_or_odd:
  forall (n:nat), exists m, (n=2*m \/ n=2*m+1).
Proof.
  induction n.

「どんな自然数 n に対しても、自然数 m が存在して、(n=2*m \/ n=2*m+1) を満たす」。こういう「これこれこういう値が存在します」という主張の定理を書くために使います。

証明してみましょう。帰納法でやります。induction n。

0の場合とそれ以外の場合にゴールが分かれますが、まずは0の方から。nを0に置き換えた

-------------------------------
exists m:nat, 0=2*m \/ 0=2*m+1

これを証明しないといけません。どうしましょう。existの正体を見てみると Coq.Init.Logic.ex:

(* Remark: exists x, Q denotes ex (fun x => Q) ... *)
Inductive ex (A:Type) (P:A -> Prop) : Prop :=
  ex_intro : forall x:A, P x -> ex (A:=A) P.

これも Inductive な命題です。exists x, P を証明するための公理は「"具体的な値 x" と "P x の証明" があれば、ex P が証明できる」という ex_intro。要するに、「Pであるようなxが存在する」を証明するには、具体的にそのxの値を人間が思いついて、Coqさんに教えてあげないといけません。

今回の場合、0=2*m \/ 0=2*m+1 を満たすような m は… m=0 とすれば成り立ってますね。これを Coq に教える tactic は、命題の記述と同じなのでちょっとややこしいですが、exists です。

 exists 0.

これで、ゴールが 0=2*0 \/ 0=2*0+1 に変わります。あとは簡単。

 left.
 simpl.
 reflexivity.

exists 0. の代わりに、Inductiveなゴールの証明の仕方の基本通り、"apply コンストラクタ" で書くこともできます: apply (ex_intro _ 0). が、やや面倒なのでexists tacticを使うのがよいと思います。

帰納法のもう一つのケースはこちら。

n : nat
IHn : exists m : nat, n = 2 * m \/ n = 2 * m + 1
--------------------------------------------------
exists m : nat, S n = 2 * m \/ S n = 2 * m + 1

今度は仮定の方にもexistsが出てきますが、Inductiveなものが仮定にでききたらdestruct IHn. こう変わります。

n : nat
x : nat
H : n = 2 * x \/ n = 2 * x + 1
--------------------------------------------------
exists m : nat, S n = 2 * m \/ S n = 2 * m + 1

「ほにゃららな自然数 m が存在する」という仮定を、「自然数 x がある」「x はほにゃららを満たす」と分解してくれます。

仮定 H がまだ \/ の形なのでさらに分解 destruct H. すると、ゴールが2つに分裂して、1つめはこう。

H : n = 2 * x
--------------------------------------------------
exists m : nat, S n = 2 * m \/ S n = 2 * m + 1

さて、n=2*x という仮定の下で、m をどうすれば S n = 2 * m \/ S n = 2 * m + 1 が成り立つようにできるか。ここはうんうん唸って考えましょう。m=x とすれば、∨の右側が成り立ちます。

 exists x.
 right.
 omega.

もう一つのゴールは

H : n = 2 * x + 1
--------------------------------------------------
exists m : nat, S n = 2 * m \/ S n = 2 * m + 1

こうですが、今度は、m=x+1 とすると、左が成り立ちます。

 exists (x+1).
 left.
 omega.
Qed.

まとめ。

exists e.
exists x, P という形のゴールを示すときに、x=e とすれば示せますよ!と具体的な値をCoqさんに教える方向で証明する。
仮定にexistsが出てきた場合は、destructが使えます。


たぶん、Coqを使った証明で一番直接的に難しいのは、exists の証明です。これを証明するには、具体的にどの値を当てはめれば成り立つのか、完全に人間が考えてあげないといけません。今回だと、こっちの場合はm=xで行ける、こっちだとm=x+1…と言った具合に。数学の教科書などでは「十分大きな x を取れば P x が成り立つから exists x, P x」のような言い方をしたりしますが、こういう時でも、十分大きい x とは具体的にいくつなのか、100なのか65536なのか無量大数なのか、中身が実際に証明できる大きさを明確に書く必要があります。

などは、直感的には対して難しくない定理なはずなんですが、この難しさのせいで結構な難問になってるように思います。

適度な練習問題としては、さきほども紹介しましたが、

などなど。

Setバージョン

前回、A∨B には、よく似た意味の {A}+{B} というものがある、という紹介をしました。違いは、前者が Prop で後者が Set なことですが、証明の仕方はだいたい同じです。exists x, P にも同じ関係の {x | P} という Set バージョンがあります(Coq.Init.Specif.sig)。

などに登場します。この問題の

Definition tails {A:Set} (ys: list A) : {xss | all_suffixes xss ys}.

は、だいたい

Theorem tails:
 forall (A:Set) (ys: list A), exists xss, all_suffixes xss ys.

と同じ意味です。証明も全く同じく、exists と destruct でできます。違いは、{_}+{_} の場合と同じく、「ゴールがSetのときはPropをdestructできない」という制約のみ。この制約の意味するところについては、次回ご紹介したいと思います。