Aioga
AI资讯 / 行业动态
返回 AI资讯

面向软件工程师的 Lean 证明剖析:用 Lean 形式化验证 DFA 加法器识别二进制加法语言

Hacker News 热门Aioga 编辑团队2026-10-03T05:52:49.000Z热度 58

一位软件工程师用 Lean 及其 Mathlib 形式化验证了 Sipser《计算理论导引》习题 1.32:证明由三比特列组成的语言 B(底行等于上两行之和)是正则语言。作者通...

行业动态Hacker News 热门

今日 AI 情报摘要

一位软件工程师用 Lean 及其 Mathlib 形式化验证了 Sipser《计算理论导引》习题 1.32:证明由三比特列组成的语言 B(底行等于上两行之和)是正则语言。

作者通过构造加法器 DFA 并证明其恰好接受 B 的逆 BR,再借助正则语言反转封闭性完成证明,旨在向软件工程师展示形式化验证系统属性的完整过程。

中文正文 · AI 翻译

我最近研究了一本计算理论教材中的一个问题,要求我使用有限状态自动机证明一个语言的性质。非正式的证明是一个简单的构造性证明,你建立一个自动机并展示它可以识别该语言。这有点类似于程序验证,所以我觉得把证明形式化看看需要什么会很有趣。Lean 是一个不错的选择,因为它的 Mathlib 包含了这个问题所需的所有定理。

在完成形式化证明后,我决定把它写出来,因为我认为这能为软件工程师提供有关形式化证明系统性质所需工作的良好见解。

我尽量让这篇文章易于理解。如果你熟悉现代静态类型编程语言(如 TypeScript 或 Rust)、二进制运算、基本的命题逻辑以及归纳证明,你应该能够跟上。

如果你对 DFA 和正规语言感到熟悉,可以直接跳到下一部分。

有限状态自动机提供了一个固定内存的理论计算模型。除了理论应用之外,有限状态自动机还有重要的实际应用。例如,有限状态自动机与解析器和正则表达式相关,其中一个错误曾导致互联网的很大一部分瘫痪。

确定性有限自动机(DFA)是一种具有固定、有限状态集的机器,它从左到右一次读取一个输入符号。每读一个符号,它通过确定性的状态转移函数更新其状态。在读取最后一个符号后,机器要么处于接受状态(输入被接受),要么不处于接受状态(输入被拒绝)。

如果你曾经写过一个简单的正则表达式,比如 -?[0-9]+,那么你已经构造了一个 DFA。这个正则表达式匹配像 12 和 -123 这样的整数字面值,对应的 DFA 看起来像这样(箭头上标注了引导到下一个状态的符号):

机器的输入符号集由集合 Σ \Sigma Σ 定义。在我们的正则表达式示例中,Σ = { − , 0 , 1 , 2 , … , 9 } \Sigma = \left\{-, 0, 1, 2, \ldots, 9\right\} Σ = { − , 0 , 1 , 2 , … , 9 }。

语言只是一组字符串,也称为单词,如果某个DFA恰好接受其中的字符串,则称该语言为正规语言。识别正则语言是一类可以用不随输入增长的内存量来解决的决策问题。

正则语言具有有用的闭包性质:两个正规语言的并集和交集是正规的,补文也是正规的,且(对我们来说很重要)正则语言的反转也是正规的。

证明语言是正则语言的标准方法是构建一个DFA,并证明它完全接受该语言。

我们可以用集合构建符号描述语言 A A:

Σ ∗ \Sigma^{*} Σ ∗ 指由 Σ \Sigma Σ 和 P 中所有可能的符号串接所生成的字符串集合 (w ) P(w) P (w ) 是字符串 w w w 必须满足的条件,才能属于该语言。

让我们将这个符号应用到正则表达式示例:-?[0-9]+ .则 Σ ∗ \Sigma^{*} Σ ∗包含字符串如“” 、 “123” 、 “-111” 、 “2-625-” 等,而 P ( w ) P(w) P(w) P (w ) 可以定义为“w w w 不是空的,且无负号,除非其第一个字符可能是负号,且 w w 至少有两个字符。”

我们将要解决的问题来自Michael Sipser的《计算理论导论》第三版:

Σ 3 \Sigma_3 Σ 3 是所有高度为 3 的列 0 和 1 的集合,因此 Σ 3 \ Sigma_3 Σ 3 上的字符串决定了三行比特。将每行读作二进制数,定义

其中 P ( w ) P(w) P (w ) 是命题:w w 的底行等于顶两行之和。

证明 B B B 是正则的。(提示:使用 B R B^{\mathcal{R}} B R 更容易。)

该问题定义了一个不寻常的字母表。字母表由三位列组成,而不是像 [a-z] 这样的字符串。因此,语言由像“apple”、“banana”等字符串组成,而是由二维位字符串组成,如

其中第一列是第一个“字符”,依此类推。

判断字符串是否属于该语言的规则是将字符串的前两行相加,并检查它们是否与第三行匹配。

例如,以下字符串属于该语言:

但以下字符串不属于该语言:

虽然这样的语言乍一看可能很奇怪,但实际上很容易识别:我们只需要检查方程

以确定一个字符串是否属于该语言。挑战在于,我们需要用固定数量的内存处理任意长度的字符串。

技巧是记住你手动加数字的方式:你从最低有效位到最高有效位进行运算。唯一从一列传到下一列的东西是进位。

但 DFA 是从左到右读取,而问题给出的数字是最高有效位优先。因此,我们不能直接识别 B B B。相反,我们构建一个 DFA 来识别其倒序 B R B^{\mathcal{R}} B R,它由 B B B 倒写的字符串组成,所以机器首先看到最低有效列。

如果我们能够构建一个 DFA 来识别 B R B^{\mathcal{R}} B R,那么我们可以断定 B R B^{\mathcal{R}} B R 是正规语言。由于 B R B^{\mathcal{R}} B R 的倒序是 B B B,我们可以利用正规语言的倒序封闭性来断定 B B B 也是正规语言,从而完成解答。

在按列逐步进行算术运算时,我们在每一步使用加法器方程计算和位:

其中 x_i, y_i 是加数位,z_i 是和位,i 表示当前列的索引,而 c_{\mathrm{in}} 是前一步的输入进位。我们按如下方法计算下一步的输出进位,记作 c_{\mathrm{out}}:

这意味着只有当两个加数都是 1 或者存在输入进位且正好有一个加数为 1 时才会产生进位。注意,计算 c_{\mathrm{out}} 的更简单方法是检查 x_i, y_i 和 c_{\mathrm{in}} 中至少两个为 1(我们将在 Lean 证明中使用这一点)。

考虑到这一点,以下是识别 B R B^{\mathcal{R}} B R 的 DFA:

箭头标注了从输入状态到输出状态的列。如果图形一开始看起来让人困惑,以下示例希望能让它更清楚。

让我们通过 DFA 跟踪问题中的第一个示例:

DFA 识别 B R B^{\mathcal{R}} B R,因此它按列从后向前读取。展开运行得到一条直线,每步一个状态。

运行以进位 0(接受状态)结束,所以反向的单词在 B R B^{\mathcal{R}} B R 中。根据 B R B^{\mathcal{R}} B R 的定义,原始单词也在 B B B 中。

注意该机器两次经过非接受的进位 1 状态。如果单词在前两列中的任意一列后停止,它将被拒绝。1 + 1 = 0 \mathtt{1} + \mathtt{1} = \mathtt{0} 以及 11 + 01 = 00 \mathtt{11} + \mathtt{01} = \mathtt{00} 都是不正确的,因为没有地方放置进位。

现在看第二个示例,它应该被拒绝:

第一列本身是正确的(1 + 0 \mathtt{1} + \mathtt{0} 1 + 0 确实是 1 \mathtt{1} 1),所以机器暂时无法判断有什么错误。但是第二列失败了:由于没有待处理进位,0 + 0 \mathtt{0} + \mathtt{0} 0 + 0 必须产生 0 \mathtt{0} 0,但底行声称是 1 \mathtt{1} 1。运行在非接受状态结束,因此第二个示例被拒绝。

注意由于死状态是汇聚状态,即使在第二列之后有更多有效列,字符串也会被拒绝。

我们的目标是展示问题中的语言 B B B 是正则语言。如前所述,为了展示一个语言是正则的,我们需要构建 DFA 并展示它接受该语言。

Lean 证明将由三部分组成:

Lean 的 Mathlib 对形式语言和 DFA 提供了一流支持,因此我们只需要在库中的结构上构建规范和实现。

对于证明,我们需要做更多工作,但 Mathlib 在这里也会有帮助。它包含了正则语言在反转下封闭的定理,这将节省大量工作。证明中会包含一些不熟悉的语法,但在底层它只是一个程序。实际上,只要程序能编译,证明就被接受。

下面是一个展示程序组件的图。完整代码可以在 GitHub 上找到。

问题中的字母表由三位二进制列组成。我们可以用 Lean 中的三个布尔值元组来表示一列:

然后我们使用 Mathlib.Computability.Language 来定义 B B B:

Language 是形式语言的通用实现,带有标准操作和相关定理。我们用字母表 Sigma3 和 BBB 的成员谓词来构建它(记住语言是一组字符串)。

wBE 是一个候选单词,以大端模式读取,是一个 Sigma3 值的列表,即一个具有三行的二维二进制值列表。rowNBE 是一个函数,从上方选择二维列表的第 n 行,并使用大端解释将其转化为自然数。因此谓词就是

如果你使用过带集合推导的编程语言,集合构造语法可能看起来很熟悉,但我们这里并不是在构建一个集合。Language 在底层只是一个 Set,Lean 中的 Set 是一个测试元素是否属于集合的函数。因此我们对 BBB 的定义在底层会展开为一个函数定义:

该函数有一个类型为 List Sigma3 的参数,这是一个通用列表,用于保存 Sigma3 对象。这目前相当标准,但返回类型更有趣。在典型编程语言中,你会期望成员测试返回一个布尔值。但这里的返回类型是 Prop,它是 Lean 中所有命题的类型(命题是可能有证明也可能没有证明的东西)。

那么,成员测试是如何工作的呢?表达式 wBE ∈ B 将函数应用于 wBE,返回一个命题。在 Lean 中,命题本身就是一种类型,其值是命题的证明。因此,我们不是将 wBE ∈ B 计算为布尔值,而是证明它:为了显示一个单词属于某个语言,我们构造该命题类型的一个值。为了显示一个单词不属于该语言,我们构造一个被否定命题的值。通过这种方式,集合成员测试最终变成了类型检验,而不是在运行时进行计算。

我们首先将 DFA 的状态(进位 0、进位 1、死状态)定义为一个求和类型:

我们可以在 Rust 中使用枚举,或者在 TypeScript 中使用判别联合来定义相同的类型。然而,Lean 的归纳类型比这些语言中的递归求和类型多做了一些:它还生成一些脚手架,使得在证明中使用该类型变得容易。我们稍后会看到更多。

现在讨论 derives。DecidableEq 只是表示该类型支持完全的相等性检查(与 Rust 中的 Eq 相同),而 Fintype 是只有在证明助手中才有的东西。它表示该类型具有有限多个值,并创建一个包含这些值的列表以及该列表完整性的证明。派生 Fintype 让我们能够稍后断言该语言可以使用常量内存识别,因此它是正则语言。

接下来,我们定义 DFA 的转换函数:

该函数有两个参数:当前状态和下一个符号,并返回下一个状态。

正如我们之前看到的,死状态是一个汇点,因此它始终映射到自身。如果我们处于进位状态,并且加法器方程验证通过,那么下一个状态是持有进位值的进位状态。否则,我们进入死状态。

dfaStep 只是一个普通函数,我们可以执行,因此让我们做一个快速的正确性检查。

如果我们想确保这成立,我们可以将其转换为一个例子:

情报判断

Aioga 编辑摘要

Aioga 编辑摘要:一位软件工程师用 Lean 及其 Mathlib 形式化验证了 Sipser《计算理论导引》习题 1.32:证明由三比特列组成的语言 B(底行等于上两行之和)是正则语言。 Aioga 将其归入「行业动态」方向,重点关注它对真实使用和行业竞争的影响。

背景分析

背景分析:公司与行业类动态需要放在竞争格局、商业化路径、资本信号和监管环境中观察,单条公告不能代表最终结果。

Aioga 观点

Aioga 判断:这条动态更适合作为行业观察信号,当前信息足以建立线索,但不足以推导长期结论。

影响与后续

影响分析:对相关团队而言,短期应先核对来源、可用范围和实际成本,再判断是否值得接入或跟进。 后续观察:继续观察官方文件、合作落地、收入或用户信号、竞品动作和监管后续。

来源与版权说明

本页正文由公开来源页面提取并按原有信息整理,同时保留来源、发布时间和原文入口。版权归原作者及来源网站所有,请通过原文链接核验和阅读来源版本。

抓取通道: 摘要聚合 · 原始域名: agostbiro.net

来源: Hacker News 热门

原文链接: 打开原始来源

Aioga 归档: 查看情报页

Content record: source-page · Updated: 2026-10-03T05:52:49.000Z

API 中转站
API RELAY · DEVELOPER INFRASTRUCTURE

API 中转站

统一接入主流 AI 模型 API,为开发、测试与生产环境提供稳定调用入口。

立即访问 api.w173.com
AIOGA SHARE POSTER

分享这篇 AI 情报

一位软件工程师用 Lean 及其 Mathlib 形式化验证了 Sipser《计算理论导引》习题 1.32:证明由三比特列组成的语言 B(底行等于上两行之和)是正则语言。作者通...

Hacker News 热门2026-10-03T05:52:49.000Z
扫码打开文章详情扫码直达文章详情

Aioga 自动聚合全球 AI 动态,并保留来源信息用于核验与引用。