【问题标题】:Selection Sort in DafnyDafny中的选择排序
【发布时间】:2014-08-26 19:38:35
【问题描述】:

我正在尝试在 Dafny 中实现选择排序。

我的sortedFindMin 函数确实有效,但selectionsort 本身包含Dafny 不会证明的断言,即使它们是正确的。

这是我的程序:

predicate sorted(a:array<int>,i:int)
  requires a != null;
  requires 0 <= i <= a.Length;
  reads a;
{
  forall k :: 0 < k < i ==> a[k-1] < a[k]
}
method FindMin(a:array<int>,i:int) returns(m:int)
  requires a != null;
  requires 0 <= i < a.Length;
  ensures i <= m < a.Length;
  ensures forall k :: i <= k < a.Length ==> a[k] >= a[m];
{
  var j := i;
  m := i;
  while(j < a.Length)
    decreases a.Length - j;
    invariant i <= j <= a.Length;
    invariant i <= m < a.Length;
    invariant forall k :: i <= k < j ==> a[k] >= a[m];
  {
    if(a[j] < a[m]){m := j;}
    j := j + 1;
  }
}
method selectionsort(a:array<int>) returns(s:array<int>)
  requires a != null;
  modifies a;
  ensures s != null;
  ensures sorted(s,s.Length);
{
  var c,m := 0,0;
  var t;
  s := a;
  assert s != null;
  assert s.Length == a.Length;
  while(c<s.Length)
    decreases s.Length-c;
    invariant 0 <= c <= s.Length;
    invariant c-1 <= m <= s.Length;
    invariant sorted(s,c);
  {
    m := FindMin(s,c);
    assert forall k :: c <= k < s.Length ==> s[k] >= s[m];
    assert forall k :: 0 <= k < c ==> s[k] <= s[m];
    assert s[c] >= s[m];
    t := s[c];
    s[m] := t;
    s[c] := s[m];
    assert s[m] >= s[c];
    assert forall k :: c <= k < s.Length ==> s[k] >= s[c];
    c := c+1;
    assert  c+1 < s.Length ==> s[c-1] <= s[c];
  }
}

为什么会这样? “后条件可能不成立”是什么意思? Dafny 可以举个反例吗?

【问题讨论】:

    标签: sorting assertions selection-sort dafny


    【解决方案1】:

    您似乎了解循环不变量背后的基本思想,这是使用 Dafny 验证程序所必需的。

    您的程序不正确。发现这一点的一种方法是在 Visual Studio 的 Dafny IDE 中使用验证调试器。单击最后报告的错误(c 递增前行上的断言),您将看到数组的上半部分包含一个小于s[c]s[m] 的元素。然后选择围绕你的 3 语句交换操作的程序点,你会注意到你的交换实际上并没有交换。

    要修复交换,交换三语句交换的第二个和第三个语句。更好的是,利用 Dafny 的多重赋值语句,这使得代码更容易正确:

    s[c], s[m] := s[m], s[c];
    

    还有另外两个问题。一是循环内的第二个断言没有验证:

    assert forall k :: 0 <= k < c ==> s[k] <= s[m];
    

    虽然s[m] 是数组上部的最小元素,但循环不变量需要证明数组下部的元素不大于上部的元素——这是一个基本属性的选择排序算法。以下循环不变量可以解决问题:

    invariant forall k, l :: 0 <= k < c <= l < a.Length ==> s[k] <= s[l];
    

    最后,关于属性sorted(s,c) 没有被循环维护的抱怨源于您将sorted 定义为严格增加,除非数组的元素是最初都是不同的。因此,Dafny 指出了您必须对分类程序做出的设计决策。您可以决定您的 selectionsort 方法仅适用于没有重复元素的数组,您可以通过添加

    forall k, l :: 0 <= k < l < a.Length ==> a[k] != a[l];
    

    作为selectionsort 的前提条件(和循环不变)。或者,更传统的做法是,您可以修正您对 sorted 的定义,将 a[k] &gt; a[m] 替换为 a[k] &gt;= a[m]

    为了稍微清理一下您的代码,您现在可以删除所有断言语句和t 的声明。由于m 仅在循环内部使用,您可以将m 的声明移动到调用FindMin 的语句中,这也表明不需要循环不变量c-1 &lt;= m &lt;= s.Length。两个decreases 子句可以省略;对于您的程序,Dafny 会自动提供这些。最后,您的selectionsort 方法修改了给定的数组,因此没有真正的理由在输出参数s 中返回引用a;相反,您可以省略 out-parameter 并将 s 替换为 a

    【讨论】:

    • 谢谢!我和我的小组实际上在不久前解决了这个问题,但我忘了我有这个问题。我真的很感激你回答了这个问题,尽管它已经很老了,因为我过去有几个问题被埋没了。我的解决方案在rise4fun.com/Dafny/fibD,看着它我发现我已经完成了你建议的一切。除了使用 Visual Studio,我相信这会有所帮助,但我的小组使用的是在线验证器(而且我还是使用 Linux)。我的小组还实现了冒泡排序、插入排序、带和不带序列的快速排序以及 Kadane 算法。
    • 太棒了。我将尝试更频繁地检查 StackOverflow 是否有 Dafny 问题(除了 dafny.codeplex.com 上的“讨论”选项卡)。
    猜你喜欢
    • 1970-01-01
    • 2013-04-25
    • 2020-03-12
    • 1970-01-01
    • 2016-08-28
    • 2013-01-19
    • 2018-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多