试图在 Dafny 中证明非重复元素序列的问题

Problems in trying to prove non-duplicate-element sequence in Dafny

提问人:MogicFrog 提问时间:9/6/2023 最后编辑:MogicFrog 更新时间:9/7/2023 访问量:114

问:

假设我们有一个序列 s,并且所有元素都与其他元素不同。我这样描述这个属性

predicate all_element_different(s: seq<int>)
{
  forall i,j :: 0 <= i < j < |s| ==> s[i] != s[j]
}

当然,另一种等效的方法是

forall i,j :: 0 <= i < |s| && 0 <= j < |s| && i != j ==> s[i] != s[j]

所以我在这里写了一个引理

lemma different_properties(s: seq<int>)
  requires all_element_different(s)
  ensures forall i,j :: 0 <= i < |s| && 0 <= j < |s| && i != j ==> s[i] != s[j]
{
}

出于某种原因,我需要对这个序列进行排序,我想得到另一个新序列,而不是直接修改旧序列。类似的东西

function insertionSort(s: seq<int>): (r: seq<int>)
  ensures multiset(s) == multiset(r)
  ensures sorted(r)
  ensures forall x :: x in s ==> x in r
  ensures |s| == |r|

只需忽略 的实现。无论如何,最后我们得到另一个序列,其属性如下:insertionSortr

  var r := insertionSort(s);
  assert multiset(s) == multiset(r);
  assert |s| == |r|;
  //assert sorted(r); This property is not used here
  assert forall x :: x in s ==> x in r;
  assert all_element_different(s);

显然,谓词应该成立,但我无法证明。我试图提取该过程:all_element_different(r)

lemma sequence_properties(s: seq<int>, r: seq<int>)
  requires multiset(s) == multiset(r)
  requires |s| == |r|
  requires all_element_different(s)
  ensures all_element_different(r)
{
}

有人对这个引理的证明有一些想法吗?

================================================

受 @Divyanshu Ranjan 启发的新方法

在我问这个问题之前,我一直在探索使用多重性来完成证明。微不足道的是,这个引理成立

lemma equivalence(s: seq<int>)
  ensures UniqueElements(s) <==> forall k :: k in multiset(s) ==> multiset(s)[k] == 1

但我在证明方面有一些问题。现在使用矛盾,我完成了它。

lemma UniqueMultiSet(s: seq<int>, r: seq<int>)
  requires UniqueElements(s)
  requires |s| == |r|
  requires multiset(s) == multiset(r)
  ensures UniqueElements(r)
{
  equivalence(s);
  equivalence(r);
}

lemma unique_elements_imply_multiplicity_equals_1(s: seq<int>)
  requires UniqueElements(s)
  ensures forall k :: k in multiset(s) ==> multiset(s)[k] == 1
{
  var ms := multiset(s);
  if !(forall k :: k in ms ==> ms[k] == 1)
  {
    var k :| k in ms && ms[k] > 1;
    var i :| 0 < i < |s|;
    var left := s[..i];
    var right := s[i..];
    assert s == left+right;
    // assert multiset(s) == multiset(left) + multiset(right);
    // assert multiset(s)[k] == multiset(left)[k] + multiset(right)[k];
    if k in left && k in right
    {
      assert !UniqueElements(s);
    } else if k in left && k !in right
    {
      unique_elements_imply_multiplicity_equals_1(left);
    } else if k !in left && k in right
    {
      unique_elements_imply_multiplicity_equals_1(right);
    }else
    {
      assert k !in multiset(s);
    }
  }
}

lemma multiplicity_equals_1_imply_unique_elements(s: seq<int>)
  requires forall k :: k in multiset(s) ==> multiset(s)[k] == 1
  ensures UniqueElements(s)
{
  if(!UniqueElements(s))
  {
    var i, j, k :| 0 <= i < j < |s| && s[i] == s[j] == k;
    var left := s[..i+1];
    var right := s[i+1..];
    assert s == left + right;
    assert j >= i+1;
    assert k in left && k in right;
    assert multiset(s)[k] >= 2;
  }
}

lemma equivalence(s: seq<int>)
  ensures UniqueElements(s) <==> forall k :: k in multiset(s) ==> multiset(s)[k] == 1
{
  if(UniqueElements(s))
  {
    unique_elements_imply_multiplicity_equals_1(s);
  }
  if(forall k :: k in multiset(s) ==> multiset(s)[k] == 1)
  {
    multiplicity_equals_1_imply_unique_elements(s);
  }
}
序列 Dafny

评论


答:

2赞 Divyanshu Ranjan 9/6/2023 #1

以下证明背后的想法如下

  1. 如果不是这样,序列的所有元素 r 是唯一的,那么应该存在索引 m < n 使得 r[m] == r[n]。
  2. 接下来,我们尝试将序列 s(左)的元素与 序列 R(右)的元素相等。
  3. 为此,我们在 r 中找到索引 ridx,例如 r[ridx] 相等 对于我们正在配对的 S 的当前元素。
  4. 现在,如果 ridx 恰好是 n,那么我们可以将电流配对 其中 r[m] 为 r[m] == r[n] 的 s 元素
  5. 实际上,我们正在将 n 个不同的元素与 n-1 个元素。
  6. 我们做了一些指数转移来说服 dafny 我们 确实与右侧的 n-1 元素配对 匹配。
  7. 通过鸽子洞,左边的两个元素应该与 右边的相同元素。
  8. 这意味着这两个元素是相等的,但事实并非如此 可能,因为所有元素在左侧都是不同的。
  9. 我们的假设是错误的,所有元素都是正确的 是不同的。

predicate UniqueElements(s: seq<int>)
{
  forall i, j :: 0 <= i < j < |s| ==> s[i] != s[j]
}

lemma pigeonhole(m : int, n: int, f : int -> int)
  requires 0 <= m < n
  requires forall i :: 0 <= i < n ==> 0 <= f(i) < m
  ensures exists j, k :: (0 <= j < k < n) && f(j) == f(k)
{
  if m <= 0 {
    assert 0 <= f(0) < 0;
  }
  else {
    var i : int := 0;
    while i < n
      invariant forall k :: 0 <= k < i ==> 0 <= f(k) < m - 1
      invariant i <= n
    {
      if f(i) == m-1 {
        var j : int := i + 1;
        while j < n
          invariant forall k :: i < k < j ==> 0 <= f(k) < m - 1
        {
          if f(j) == m-1 { return; }
            j := j + 1;
        }
        var g : int -> int := x => if x < i then f(x) else f(x+1);
        pigeonhole(m-1, n-1, g);
        return;
      }
      i := i + 1;
    }
    pigeonhole(m-1, n, f);
  }
}


lemma UniqueMultiSet(s: seq<int>, r: seq<int>)
  requires UniqueElements(s)
  requires |s| == |r|
  requires multiset(s) == multiset(r)
  ensures UniqueElements(r)
{
  assert |multiset(s)| == |multiset(r)|;
  if !UniqueElements(r){
    var m, n :| 0 <= m < n < |r| && r[m] == r[n];
    forall idx | 0 <= idx < |s| ensures exists ridx :: 0 <= ridx < |r| && r[ridx] == s[idx]
    {
      assert s[idx] in multiset(r);
    }
    var f : int -> int :=
      x => if 0 <= x < |s|
           then
              var idx :| 0 <= idx < |r| && r[idx] == s[x];
              if idx < n then idx else if idx == n then m else idx - 1
           else -1;
    pigeonhole(|s|-1, |s|, f);
    var k, l :| 0 <= k < l < |s| && f(k) == f(l);
    assert s[k] == s[l];
  }
}

评论

0赞 MogicFrog 9/6/2023
非常感谢这个答案!真是太棒了!我测试了它,完全正确。但我认为作为一个新手,我应该首先花一些时间去理解它。
1赞 MogicFrog 9/7/2023
我找到了一种新的直观方法来证明引理,灵感来自你的想法。顺便说一句,鸽子洞的使用相当精致
0赞 Divyanshu Ranjan 9/7/2023
我现在看到你的新证据了。感谢您的发布