下面是第一次证明各种简单定理的尝试,在这种情况下是关于奇偶校验的。达夫尼/诉。 1.9.9.40414/验证将2加到偶数会产生偶数,但不接受任何一个注释掉的条件。
function IsEven(a : int) : bool
requires a >= 0
{
if a == 0 then true
else if a == 1 then false
else IsEven(a - 2)
}
method Check1(a : int)
requires a >= 0
ensures IsEven(a) ==> IsEven(a + 2)
//ensures IsEven(a) ==> IsEven(a + a)
//ensures IsEven(a) ==> IsEven(a * a)
{
}
由于我刚刚开始研究这个奇妙的工具,我的方法或实现可能是不正确的。任何建议将不胜感激。
答案 0 :(得分:1)
这里几乎没有什么不同的东西。我将依次讨论三个后置条件中的每一个。
由于IsEven
是一个递归定义的谓词,一般而言,关于它的事实将需要通过归纳证明。你的第一个帖子条件很简单,不需要归纳,这就是它通过的原因。
你的第二个后置条件确实要求归纳证明。 Dafny具有自动执行归纳的启发式方法,但这些启发式方法仅在某些情况下被调用。特别是,Dafny只会尝试对“幽灵方法”(也称为“lemmas”)进行归纳。
如果您在ghost
method
前面添加关键字Check1
(或将method
更改为lemma
,这相当于),您会看到第二个后置条件goes through。这是因为Dafny的归纳启发式被调用并设法完成证明。
第三个后置条件更复杂,因为它涉及非线性算法。 (换句话说,它涉及将两个变量相乘的非平凡推理。)Dafny的底层求解器难以推理这类事物,因此感应的启发式证明不会通过。
a * a
即使a
是偶数证明它的一种方法是here。我已将IsEven(a) ==> IsEven(a * a)
分解为自己的引理,称为EvenSquare
。我也把它改为要求IsEven(a)
作为前提条件,而不是在后置条件中加以暗示。 (类似的证据也反过来暗示,但是使用像这样的引理而不是含义的先决条件是惯用的Dafny。)
EvenSquare
的证明是a
的(手动)归纳。基本案例是自动处理的。在归纳的情况下(if
语句的主体),我调用归纳假设(即,我对EvenSquare
进行递归方法调用以确定(a - 2) * (a - 2)
是偶数)。然后我断言a * a
可以写成(a - 2) * (a - 2)
和一些偏移的总和。断言自动发送。如果我可以证明这种平等的右手是均匀的,那么将会进行证明。
要做到这一点,我已经知道(a - 2) * (a - 2)
是偶数,所以我首先调用另一个引理来证明偏移是偶数,因为它是其他两倍。最后,我引用最后一个引理来表明两个偶数之和是偶数。
这样就完成了证明,假设有两个引理。
仍然表明两次都是偶数,两个偶数之和是偶数。虽然不是完全无足轻重,但也不像EvenSquare
那么复杂。
引理EvenDouble
证明了两次都是偶数。 (这实际上是你的第二个后置条件的更强版本。你的第二个后置条件是将任何偶数数加倍。事实上,任何(非负的,在你的均匀性定义下)数量加倍一切都是均匀的。)EvenDouble
的证据通过({1}}上的(手动)归纳进行。基本案例是自动处理的。归纳案例只需要明确地引用归纳假设。
引理a
几乎是由Dafny的归纳启发式自动证明的,除了它跳过一个错误或其他问题导致求解器中的循环。经过一些调试后,我确定注释EvenPlus
(或{:induction x}
,就此而言)使证明不循环。这些注释告诉Dafny的启发式试图导入哪些变量。默认情况下,在这种情况下,Dafny尝试引入 {:induction y}
和x
,这由于某种原因导致求解器循环。但是引入任何一个变量都是有效的。我正在进一步研究这个问题,但目前的解决方案是有效的。