我尝试使用dafny来验证我的qsort函数的正确性,但是idk为什么有关于我的代码的验证失败。 这是我的代码:
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length-1);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
method qsort(a: array<int>,left: int,right: int)
requires left>=0 && right < a.Length
modifies a
decreases right - left
{
if (right > left)
{
var pivotValue: int := a[left];
var t: int := a[left];
a[left] := a[right-1];
a[right-1] := t;
var storeIndex: int := left;
var i :int := left;
while i < right - 1
invariant left <= storeIndex < right
decreases right - i
{
if a[i] < pivotValue
{
t := a[storeIndex];
a[storeIndex] := a[i];
a[i] := t;
storeIndex := storeIndex+1;
}
i := i+1;
}
t := a[right-1];
a[right-1] := a[storeIndex];
a[storeIndex] := t;
qsort(a,left,storeIndex);
qsort(a,storeIndex+1,right);
}
}
错误是:
断言违规
assert a[i] <= a[i+1];
循环可能无法维护此循环不变量。
invariant left <= storeIndex < right + 1
未能减少终止措施
qsort(a,left,storeIndex);
感谢@James Wilcox的回答,我将我的代码重写为:
method qsort(a: array<int>,left: int,right: int)
requires left>=0 && right <= a.Length
ensures (exists p | left<=p<right :: (forall k: int :: left < k < p ==> a[k] <= a[p]) && (forall j: int :: p < j < right ==> a[j] >= a[p]))
modifies a
decreases right - left
{
if (right > left)
{
var pivotValue: int := a[left];
var t: int := a[left];
a[left] := a[right-1];
a[right-1] := t;
var storeIndex: int := left;
var i :int := left;
while i < right - 1
invariant left <= storeIndex < right
invariant storeIndex <= i
decreases right - i
{
if a[i] < pivotValue
{
t := a[storeIndex];
a[storeIndex] := a[i];
a[i] := t;
storeIndex := storeIndex+1;
}
i := i+1;
}
t := a[right-1];
a[right-1] := a[storeIndex];
a[storeIndex] := t;
qsort(a,left,storeIndex);
qsort(a,storeIndex+1,right);
}
}
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
但是qsort的后置条件可能不成立,我该怎么做才能纠正它?
我最终验证的代码:
method qsort(a: array<int>,left: int,right: int)
requires 0<= left <= right <= a.Length
requires 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
requires 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
ensures forall j,k:: left <= j < k < right ==> a[j] <= a[k]
ensures forall j:: (0<= j < left) || (right <= j < a.Length) ==> old(a[j])==a[j]
ensures 0<= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
ensures 0< left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
modifies a
decreases right - left
{
if (right > left)
{
var pivot := left;
var i :int := left + 1;
while i < right
invariant left <= pivot < i <= right
invariant forall j::left <= j < pivot ==> a[j] < a[pivot]
invariant forall j::pivot < j < i ==> a[pivot] <= a[j]
invariant forall j::0 <= j < left || right <= j < a.Length ==> old(a[j])==a[j]
invariant 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
invariant 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
decreases right - i
{
if a[i] < a[pivot]
{
var count :=i -1;
var tmp:=a[i];
a[i] := a[count];
while (count>pivot)
invariant a[pivot] > tmp
invariant forall j::left <= j < pivot ==> a[j]<a[pivot]
invariant forall j::pivot< j < i+1 ==> a[pivot]<=a[j]
invariant forall j::0<=j<left || right <= j <a.Length ==> old(a[j])==a[j]
invariant 0 <= left <= right < a.Length ==> forall j:: left <= j < right ==> a[j] < a[right]
invariant 0 < left <= right <= a.Length ==> forall j:: left <= j < right ==> a[left-1] <= a[j]
{
a[count+1]:=a[count];
count:=count-1;
}
a[pivot+1]:=a[pivot];
pivot:=pivot+1;
a[pivot-1]:=tmp;
}
i := i+1;
}
qsort(a,left,pivot);
qsort(a,pivot+1,right);
}
}
method Testing (a: array<int>)
requires a.Length > 0
modifies a
{
qsort(a,0,a.Length);
var i :int := 0;
while(i<a.Length-1)
decreases a.Length - 1 - i
{
assert a[i] <= a[i+1];
i := i+1;
}
}
答案 0 :(得分:0)
您的代码可能是正确的,但Dafny通常需要一些帮助才能证明这一点。
Dafny只会通过后置条件(ensures
子句)推理方法调用。由于你的qsort
方法没有后置条件,Dafny会认为它可以做任何事情。这解释了为什么Testing
方法无法证明断言的原因。如果您将后置条件添加到qsort
,那么您必须证明它!这将是一个很好的练习!
Dafny根据不变量的原因推断循环。如果在循环不变量中未提及由循环体修改的变量,则Dafny假定其值是任意的。如果storeIndex
小于i
,则storeIndex
的不变量不为真。您可以通过向循环添加其他不变storeIndex <= i
来解决此问题。然后两个不变量都会通过。
上一个修复程序还修复了终止问题。
为了更好地理解Dafny如何使用前/后条件和循环不变量来解决验证问题,我建议您阅读the guide。然后,您可以向qsort
添加合适的后置条件,并尝试使用其他循环不变量来证明它。如果你遇到困难,请随时提出更多问题!
在你的代码的第二个版本中,后置条件似乎太弱,因为qsort
是一个排序方法,似乎后置条件应该是数组已排序。 (因为它是递归的,实际上后置条件应该只讨论left
和right
之间的数组区域。)如果这样做,那么Testing
中的断言应该通过。然后,您仍然需要做一些工作来证明qsort
的后置条件,方法是将不变量添加到其中的while循环中。