You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dafny中filter函数相关引理filterLemmaExtra与filterLemmaSizes证明受阻

解决Dafny中filterLemmaExtra和filterLemmaSizes引理的证明问题

基础函数定义

先明确我们用到的filter和统计满足谓词元素数量的count辅助函数(匹配常见快速排序场景的定义):

function filter<T>(s: seq<T>, p: T -> bool): seq<T> {
  if s == [] then []
  else if p(s[0]) then [s[0]] + filter(s[1..], p)
  else filter(s[1..], p)
}

function count<T>(s: seq<T>, p: T -> bool): nat {
  if s == [] then 0
  else if p(s[0]) then 1 + count(s[1..], p)
  else count(s[1..], p)
}

一、filterLemmaExtra的证明

假设你的引理目标是:当i < |filter(s, p)|时,filter(s + t, p)[i] = filter(s, p)[i](拼接序列filter结果的索引对应关系),以下是可行的证明方案:

证明思路:结构归纳+分情况拆解

对序列s做结构归纳,分基例和归纳步骤,再针对首元素是否满足谓词拆分场景,把复杂断言拆成Dafny可自动验证的小步骤。

实现代码

lemma filterLemmaExtra<T>(s: seq<T>, t: seq<T>, p: T -> bool)
  ensures forall i: nat :: i < |filter(s, p)| ==> filter(s + t, p)[i] == filter(s, p)[i]
{
  if s == [] {
    // 基例:空序列无有效i,断言 vacuously true
  } else {
    filterLemmaExtra(s[1..], t, p); // 先应用归纳假设
    if p(s[0]) {
      // 验证i=0的场景
      assert filter(s + t, p)[0] == s[0];
      assert filter(s, p)[0] == s[0];
      // 验证i>0的场景
      forall i: nat where 0 < i < |filter(s, p)| {
        let j := i - 1;
        assert j < |filter(s[1..], p)|;
        assert filter(s + t, p)[i] == filter(s[1..] + t, p)[j];
        assert filter(s, p)[i] == filter(s[1..], p)[j];
        calc {
          filter(s + t, p)[i];
          == filter(s[1..] + t, p)[j];
          == filter(s[1..], p)[j];  // 应用归纳假设
          == filter(s, p)[i];
        }
      }
    } else {
      assert filter(s + t, p) == filter(s[1..] + t, p);
      assert filter(s, p) == filter(s[1..], p);
      // 直接复用归纳假设
      forall i: nat where i < |filter(s, p)| {
        calc {
          filter(s + t, p)[i];
          == filter(s[1..] + t, p)[i];
          == filter(s[1..], p)[i];  // 归纳假设
          == filter(s, p)[i];
        }
      }
    }
  }
}

二、filterLemmaSizes的证明

假设你的引理目标是:|filter(s, p)| = count(s, p)(filter结果长度等于原序列中满足谓词的元素数),以下是直接有效的证明方案:

证明思路:结构归纳+谓词分情况

这类等式证明用结构归纳最直接,无需反证法。对s归纳后,按首元素是否满足谓词拆分,复用归纳假设即可完成推导。

实现代码

lemma filterLemmaSizes<T>(s: seq<T>, p: T -> bool)
  ensures |filter(s, p)| == count(s, p)
{
  if s == [] {
    // 基例:空序列长度均为0
    assert filter(s, p) == [];
    assert count(s, p) == 0;
  } else {
    filterLemmaSizes(s[1..], p); // 应用归纳假设
    if p(s[0]) {
      assert filter(s, p) == [s[0]] + filter(s[1..], p);
      assert |filter(s, p)| == 1 + |filter(s[1..], p)|;
      assert count(s, p) == 1 + count(s[1..], p);
      calc {
        |filter(s, p)|;
        1 + |filter(s[1..], p)|;
        1 + count(s[1..], p);  // 归纳假设
        count(s, p);
      }
    } else {
      assert filter(s, p) == filter(s[1..], p);
      assert |filter(s, p)| == |filter(s[1..], p)|;
      assert count(s, p) == count(s[1..], p);
      calc {
        |filter(s, p)|;
        |filter(s[1..], p)|;
        count(s[1..], p);  // 归纳假设
        count(s, p);
      }
    }
  }
}

额外提示

如果你的引理断言和上述假设不同,比如涉及多序列组合或更复杂的索引范围,可以调整归纳对象(比如对t归纳),或者拆分更多子断言降低验证复杂度——核心是避免让Dafny一次性处理复杂逻辑,把大目标拆成可逐步验证的小步骤。

内容的提问来源于stack exchange,提问作者Hath995

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 15:15:34