我在dafny的代码有什么问题?

时间:2018-04-11 00:54:09

标签: dafny

我尝试使用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);
      }  
    }

错误是:

  1. 断言违规

    assert a[i] <= a[i+1];
    
  2. 循环可能无法维护此循环不变量。

    invariant left <= storeIndex < right + 1
    
  3. 未能减少终止措施

    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的后置条件可能不成立,我该怎么做才能纠正它?

  4. 我最终验证的代码:

        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;
            }
        }
    

1 个答案:

答案 0 :(得分:0)

您的代码可能是正确的,但Dafny通常需要一些帮助才能证明这一点。

  1. Dafny只会通过后置条件(ensures子句)推理方法调用。由于你的qsort方法没有后置条件,Dafny会认为它可以做任何事情。这解释了为什么Testing方法无法证明断言的原因。如果您将后置条件添加到qsort,那么您必须证明它!这将是一个很好的练习!

  2. Dafny根据不变量的原因推断循环。如果在循环不变量中未提及由循环体修改的变量,则Dafny假定其值是任意的。如果storeIndex小于i,则storeIndex的不变量不为真。您可以通过向循环添加其他不变storeIndex <= i来解决此问题。然后两个不变量都会通过。

  3. 上一个修复程序还修复了终止问题。

  4. 为了更好地理解Dafny如何使用前/后条件和循环不变量来解决验证问题,我建议您阅读the guide。然后,您可以向qsort添加合适的后置条件,并尝试使用其他循环不变量来证明它。如果你遇到困难,请随时提出更多问题!

    在你的代码的第二个版本中,后置条件似乎太弱,因为qsort是一个排序方法,似乎后置条件应该是数组已排序。 (因为它是递归的,实际上后置条件应该只讨论leftright之间的数组区域。)如果这样做,那么Testing中的断言应该通过。然后,您仍然需要做一些工作来证明qsort的后置条件,方法是将不变量添加到其中的while循环中。