这是一个用 Dafny 编写的简单排序算法:
predicate perm(a:array<int>, b:array<int>)
requires a != null && b != null
reads a,b
{
multiset(a[..]) == multiset(b[..])
}
predicate sorted(a:array<int>, min:int, max:int)
requires a != null
requires 0 <= min <= max <= a.Length
reads a
{
forall i,j | min <= i < j < max :: a[i] <= a[j]
}
method sort(a:array<int>)
requires a != null
requires a.Length >= 1
modifies a
ensures perm(a,old(a))
ensures sorted(a, 0, a.Length)
{
var i := 1;
while i < a.Length
invariant perm(a,old(a))
invariant 1 <= i <= a.Length
invariant sorted(a, 0, i)
decreases a.Length-i
{
var j := i;
while j > 0 && a[j-1] > a[j]
invariant perm(a,old(a))
invariant 1 <= i <= a.Length-1
invariant 0 <= j <= i
invariant sorted(a,0,j)
invariant sorted(a,j,i+1) //MIGHT NOT BE MAINTAINED IF I REMOVE THE NEXT INVARIANT
invariant forall m,n | 0 <= m < j < n <= i :: a[m] <= a[n]
decreases j
{
a[j], a[j-1] := a[j-1], a[j];
j := j-1;
}
i := i+1;
}
}
代码无错误。但是,如果我从内循环中删除不变forall m,n | 0 <= m < j < n <= i :: a[m] <= a[n]
, Dafny 会告诉我循环可能不会保留不变sorted(a,j,i+1)
。
为什么?
如何首先猜测是否需要不变的 forall m,n | 0 <= m < j < n <= i :: a[m] <= a[n]
?
我试图在纸上证明这个程序,但在构造内环不变量时我不需要这个不变量。
答案 0 :(得分:1)
sorted(a,j,i+1)
无法维持?记住验证循环不变量需要什么。它需要在满足循环不变量的任意状态下启动,执行循环体,然后证明你以满足循环不变量的状态结束。换句话说,可以假定除了循环不变量之外的任何东西。
如果没有额外的循环不变量,则区域a[0..j]
不会阻止包含大于a[j..i+1]
的元素。特别是,当循环执行时,我们知道a[j-1] > a[j]
,所以说a[j-1]
并不比a[k]
k >= j
大a
}}?
考虑插入排序的内部循环的一种方法是它保持一种不变的类似&#34; a[j]
除了在a
&#34;之外被排序。事实上,它保持了一些稍微强一些的东西,即&#34; a[j]
被排序,除了a[k]
可能与任何k < j
sorted
失序(即,更小) {1}}。
我们可以将其表达为invariant forall m,n | 0 <= m < n < i+1 && n != j :: a[m] <= a[n]
谓词的修改版本:
a
表示对于第二个索引为j
的那些<{1}},除之外的所有索引对其进行排序。
事实上,内部循环仅使用此不变量进行验证(即,没有sorted
不变量或&#34;额外&#34;不变量。
我们可以看看你的&#34;额外&#34;不变量是与sorted(a,0,j)
和sorted(a,j,i+1)
结合使用时表达这种更简洁的不变量的另一种方式。对于任何一对指数,如果它们都低于j
,那么sorted(a,0,j)
适用。如果它们都高于j
,则适用sorted(a,j,i+1)
。如果一个低于j
且一个严格高于j
,则应用额外的不变量。唯一剩下的情况是较大的索引恰好是j
,但这是由简明陈述排除的情况。