Dafny程序无法证明此二进制搜索实现?

时间:2018-12-17 14:54:20

标签: dafny

我们正在尝试使用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;
    }
}

1 个答案:

答案 0 :(得分:0)

Dafny验证程序仅根据规格说明才对方法UpdateIUpdateK进行调用。您为这两种方法提供的后置条件不足以证明终止。特别是,UpdateI(..., i, ...)可能返回i,而UpdateK(..., k, ...)可能返回k,在这种情况下,循环将不会继续进行。

我还有两个建议。

也许是一个品味问题,但我发现它简化了表达方式。使用k作为未使用的最低索引,而不是使用的最高索引。因此,请将k初始化为|q|,而不是|q|-1。这样,循环的每次迭代都会查看从索引k-i开始的k-i+1(不是i)个元素。也就是说,它查看的是q[i..k](而不是q[i..k+1])。

另一个是您的程序确实很难阅读,因为您不需要为许多事情使用单独的函数和方法。此外,这些事物具有无意义的名称,例如Guard1VUpdateI。我认为您最好直接在方法BinarySearch中编写这些表达式和语句。

最后一句话。也许您会发现以下验证角的文章很有帮助:https://www.youtube.com/watch?v=-_tx3lk7yn4

Rustan