我们正在尝试使用Dafny编写二进制搜索算法,看来Dafny不能证明程序的正确性。
有人可以帮忙吗? 这些是我们得到的错误:
在INV上:循环可能不维护此循环不变性。DafnyVSCode
在Guard1上:减少表达式可能不会减少Dafny VSCode
predicate Sorted(q: seq<int>)
{
forall i,j :: 0 <= i <= j < |q| ==> q[i] <= q[j]
}
method BinarySearch(q: seq<int>, key: int) returns (j: nat)
requires Sorted(q) && key in q
ensures j < |q| && q[j] == key
{
var i: nat, k: nat;
i,j,k := Init(q,key);
while Guard1(q,key,j)
invariant Inv(q,key,i,j,k)
decreases V(i,k)
{
if Guard2(q,key,j)
{
i := UpdateI(q,key,i,j,k);
}
else
{
k := UpdateK(q,key,i,j,k);
}
j := (i+k)/2;
}
}
predicate Inv(q: seq<int>, key: int, i: nat, j: nat, k: nat)
{
i <= j <= k < |q| &&
key in q[i..k+1]
}
predicate method Guard1(q: seq<int>, key: int, j: nat)
requires Sorted(q) && key in q
{
0 <= j < |q| && q[j] != key
}
method Init(q: seq<int>, key: int) returns (i: nat, j: nat, k: nat)
requires Sorted(q) && key in q
ensures 0 <= i <= j <= k < |q| && key in q[i..k+1]
{
i, k := 0, |q|-1;
j := (k+i) / 2;
}
function V(i: nat, k: nat): int
{
if (k > i) then k-i
else 0
}
predicate method Guard2(q: seq<int>, key: int, j: nat)
{
0 <= j < |q| && q[j] < key
}
method UpdateI(q: seq<int>, key: int, i0: nat, j: nat, k: nat) returns (i: nat)
requires Guard2(q,key,j) && Inv(q,key,i0,j,k)
ensures i0 <= i
{
if(j < |q|-1 ){
i:= j + 1;
}
else {
i:= j;
}
}
method UpdateK(q: seq<int>, key: int, i: nat, j: nat, k0: nat) returns (k: nat)
requires (!Guard2(q,key,j)) && Inv(q,key,i,j,k0)
ensures k <= k0
{
if(j > 0){
k:= j - 1;
}
else {
k:= j;
}
}
答案 0 :(得分:0)
Dafny验证程序仅根据规格说明才对方法UpdateI
和UpdateK
进行调用。您为这两种方法提供的后置条件不足以证明终止。特别是,UpdateI(..., i, ...)
可能返回i
,而UpdateK(..., k, ...)
可能返回k
,在这种情况下,循环将不会继续进行。
我还有两个建议。
也许是一个品味问题,但我发现它简化了表达方式。使用k
作为未使用的最低索引,而不是使用的最高索引。因此,请将k
初始化为|q|
,而不是|q|-1
。这样,循环的每次迭代都会查看从索引k-i
开始的k-i+1
(不是i
)个元素。也就是说,它查看的是q[i..k]
(而不是q[i..k+1]
)。
另一个是您的程序确实很难阅读,因为您不需要为许多事情使用单独的函数和方法。此外,这些事物具有无意义的名称,例如Guard1
和V
和UpdateI
。我认为您最好直接在方法BinarySearch
中编写这些表达式和语句。
最后一句话。也许您会发现以下验证角的文章很有帮助:https://www.youtube.com/watch?v=-_tx3lk7yn4
Rustan