图宾根理论计算机科学笔记-全-

图宾根理论计算机科学笔记(全)

001:形式语言入门 🧠

在本节课中,我们将要学习理论计算机科学的基础——形式语言。我们将从计算机科学的哲学与数学根源讲起,逐步引入形式语言的核心概念,包括字母表、字符串和语言的定义。内容将尽可能简单直白,让初学者能够看懂。

课程背景与哲学根源

理论计算机科学并非凭空产生,它深深植根于数学与哲学之中。上一节我们介绍了课程的总体框架,本节中我们来看看其思想渊源。

它始于对“计算”本质的哲学思考。数学家与哲学家如希尔伯特哥德尔图灵,为理解计算的界限奠定了基础。特别是库尔特·哥德尔的不完备性定理,表明了任何足够强大的形式系统都存在既不能被证明也不能被证伪的命题。

这直接引出了一个问题:什么是“可计算”的?阿兰·图灵通过定义图灵机(Turing Machine)为此提供了答案。图灵机是一个抽象的数学模型,它定义了什么是“算法”或“有效计算”。

图灵机可以用以下方式简单理解:

一个无限长的纸带 + 一个读写头 + 一套状态转移规则 = 图灵机

它虽然简单,但被证明可以模拟任何现代计算机的算法过程。

因此,理论计算机科学位于数学、哲学和工程学的交叉点。它使用数学的精确工具(如集合、函数、逻辑)来形式化地研究计算过程、算法以及它们的能力与极限。

形式语言:沟通的数学基础

理解了计算模型的由来后,我们自然要问:计算机处理的是什么?答案是:符号由符号组成的结构。这就是形式语言的研究范畴。

形式语言是符号串的集合,它为编程语言、编译器设计、协议规范等提供了严格的数学基础。它剥离了自然语言的歧义性,专注于语法结构。

核心概念定义

以下是形式语言中最基础的几个概念,我们将逐一进行定义。

1. 字母表

一切始于最基础的符号集合。

  • 定义:字母表是一个有限的、非空的符号集合。通常用大写希腊字母 Σ(Sigma)表示。
  • 示例
    • 二进制字母表:Σ = {0, 1}
    • ASCII字符集:Σ = {a, b, c, ..., A, B, C, ..., !, @, #, ...}
    • 英文字母表:Σ = {a, b, c, ..., z}

2. 字符串

字母表中的符号可以组合起来。

  • 定义:字符串(或字)是字母表 Σ 中符号的一个有限序列
  • 长度:字符串中符号的个数,记为 |w|。例如,对于字符串 w = “abc”,其长度 |w| = 3。
  • 空串:长度为0的特殊字符串,记为 ε(Epsilon)。它是任何字母表上的字符串。
  • 操作
    • 连接:将两个字符串首尾相接。若 x = “ab”, y = “cd”,则它们的连接 xy = “abcd”。
    • 幂运算:字符串 w 的 k 次幂 (w^k) 表示将 w 连接 k 次。例如,(“ab”)^2 = “abab”。特别地,w^0 = ε。

3. 语言

字符串的集合构成了语言。

  • 定义:给定字母表 ΣΣ 上的一个语言 LΣ* 的一个子集。
    • 这里 Σ* 表示由 Σ 中所有符号生成的所有有限字符串(包括空串 ε)的集合,也称为 Σ 的克林闭包
    • Σ+ 表示 Σ* 中去掉空串 ε 的集合,即所有非空字符串的集合。
  • 示例
    • 所有由偶数个0组成的字符串:L = {ε, 00, 0000, 000000, ...}
    • 所有合法的C语言程序。
    • 所有回文串(正读反读都一样的串)的集合。

4. 语言的运算

既然语言是集合,我们可以对它们进行标准的集合运算。

以下是常见的语言运算:

  • 并集:L₁ ∪ L₂ =
  • 交集:L₁ ∩ L₂ =
  • 补集:对于语言 L ⊆ Σ,其补集 ¬L = Σ \ L
  • 连接:两个语言的连接 L₁L₂ =
  • 克林星号:语言 L 的克林星号 L* 表示由 L 中字符串通过有限次连接所能构成的所有字符串的集合,包括空串 ε。

总结与展望

本节课中我们一起学习了理论计算机科学的起源和形式语言的基础。我们了解到,这门学科源于对数学基础与计算本质的深刻哲学思考,并通过图灵机等模型将计算概念形式化。在形式语言部分,我们定义了最核心的三个概念:字母表(Σ)字符串(如 w)语言(L ⊆ Σ*),并介绍了语言的基本运算。

这些抽象定义是后续学习的基础。在下节课中,我们将利用这些概念,探讨如何形式化地描述(即“生成”)一种语言,从而引入文法的概念,特别是著名的乔姆斯基谱系。这将是我们理解编程语言语法和编译器工作的第一步。

002:确定性有限自动机

在本节课中,我们将学习计算理论中的一个核心概念:确定性有限自动机。这是一种用于描述简单计算过程的数学模型,是理解形式语言和更复杂计算模型的基础。

概述

确定性有限自动机是一种抽象的、状态有限的机器。它读取一个由符号组成的输入字符串,并根据预定义的规则在状态之间转移。我们将学习它的形式化定义、工作原理以及如何用它来定义一种被称为“正则语言”的语言类别。


形式化定义 🧩

上一节我们介绍了DFA的基本概念,本节中我们来看看如何用数学语言精确地描述一个确定性有限自动机。

一个确定性有限自动机 是一个五元组:
M = (Q, Σ, δ, q0, F)

以下是每个组成部分的解释:

  • Q:一个有限的状态集合。
  • Σ:一个有限的输入字母表。
  • δ:状态转移函数,其形式为 δ: Q × Σ → Q。它定义了在当前状态和输入符号下,机器应转移到哪个唯一的下一个状态。
  • q0:初始状态,属于Q,即 q0 ∈ Q
  • F:接受状态(或终结状态)的集合,是Q的子集,即 F ⊆ Q

工作原理与图示 🔄

理解了形式定义后,我们来看看DFA是如何实际运行的。通常,我们可以用两种方式来描述它。

状态转移图

这是一种直观的图形表示法。

  • 每个状态用一个圆圈表示。
  • 接受状态用双圈表示。
  • 初始状态用一个指向它的箭头标明(不来自任何其他状态)。
  • 状态转移用带标记的有向边表示。例如,从状态 q 到状态 p 且标记为 a 的边表示 δ(q, a) = p

状态转移表

这是一种表格表示法,清晰地列出了转移函数。

  • 行代表当前状态。
  • 列代表输入符号。
  • 表格单元格中的内容表示下一个状态。

扩展转移函数与语言接受 📜

机器如何一步步处理整个字符串呢?这需要扩展转移函数的概念。

扩展转移函数 δ^ 描述了机器从某个状态开始,处理完整个输入字符串后所到达的状态。其递归定义如下:

  1. δ^(q, ε) = q (处理空字符串 ε 后,状态不变)
  2. δ^(q, wa) = δ( δ^(q, w), a) (先处理字符串 w,再根据最后一个符号 a 转移)

基于此,我们可以定义DFA 接受 的语言:
L(M) = { w ∈ Σ* | δ^(q0, w) ∈ F }
即,所有能使机器从初始状态 q0 开始,运行完整串 w 后,停止在某个接受状态(属于F)的字符串 w 的集合。


封闭性操作 🔒

一个重要的性质是,正则语言类在多种操作下是封闭的。这意味着,如果你对正则语言进行这些操作,得到的新语言仍然是正则的。本节我们探讨其中三种。

补运算

如果 L 是一个正则语言(即能被某个DFA M 接受),那么它的补集 L^c = Σ* \ L 也是正则的。
构造方法: 给定接受 L 的DFA M = (Q, Σ, δ, q0, F),构造一个新的DFA M' 来接受 L^c。只需将 M 中的接受状态集取补即可:
M' = (Q, Σ, δ, q0, Q \ F)

交运算

如果 L1L2 都是正则语言,那么它们的交集 L1 ∩ L2 也是正则的。
构造方法: 使用乘积自动机 的思想。给定接受 L1L2 的DFA M1M2,构造一个新的DFA M,其状态是 M1M2 状态的笛卡尔积。M 模拟两台机器同时运行,仅当 M1 M2 都进入接受状态时,M 才进入接受状态。

并运算

如果 L1L2 都是正则语言,那么它们的并集 L1 ∪ L2 也是正则的。
构造方法: 同样使用乘积自动机。构造方式与交运算类似,但 M 的接受状态集定义为:M1 M2 进入接受状态的状态对集合。


总结

本节课中,我们一起学习了确定性有限自动机的核心内容。

  1. 我们首先给出了DFA的形式化五元组定义 (Q, Σ, δ, q0, F)
  2. 接着,我们探讨了它的两种表示方法:状态转移图和状态转移表。
  3. 然后,我们通过扩展转移函数 δ^ 定义了DFA所接受的语言 L(M)
  4. 最后,我们证明了正则语言在补、交、并运算下是封闭的,并了解了通过修改接受状态集或构造乘积自动机来实现这些运算的方法。

DFA是计算理论中一个简洁而强大的模型,它为理解计算的可能性与局限性奠定了第一块基石。

003:非确定性有限自动机

概述

在本节课中,我们将要学习非确定性有限自动机。这是一种与确定性有限自动机不同的计算模型,它允许机器在特定状态下,对于同一个输入符号,有多个可能的下一个状态。我们将探讨其定义、工作原理、与确定性自动机的关系,以及它在理论计算机科学中的重要性。

非确定性有限自动机的定义

上一节我们介绍了确定性有限自动机,本节中我们来看看它的一个变体:非确定性有限自动机。

一个非确定性有限自动机是一个五元组 (Q, Σ, δ, q0, F),其中:

  • Q 是一个有限的状态集合。
  • Σ 是一个有限的输入字母表。
  • δ转移函数,其形式为 δ: Q × (Σ ∪ {ε}) → P(Q)。这里 P(Q) 表示 Q 的幂集(即所有子集的集合)。这意味着对于给定的状态和输入符号(或空串 ε),机器可以转移到一组可能的状态,而不仅仅是一个。
  • q0 ∈ Q 是初始状态。
  • F ⊆ Q 是接受状态(或终结状态)的集合。

与确定性有限自动机的核心区别在于转移函数 δ。NFA 允许不确定性:在某个状态下读取一个符号后,机器可以“选择”进入多个可能的后继状态之一。此外,NFA 还可以进行 ε-转移,即不消耗任何输入符号就从一个状态转移到另一个状态。

NFA 如何工作?

为了理解 NFA 如何判断一个字符串是否被接受,我们需要引入计算路径的概念。

当 NFA 处理一个输入字符串时,它并非只有一条确定的路可走。相反,由于非确定性,它可能同时探索多条路径。我们可以将 NFA 想象成一棵树,根节点是初始状态,每个分支代表一种可能的选择。

NFA 接受一个输入字符串,当且仅当至少存在一条从初始状态 q0 开始,到某个接受状态 f ∈ F 结束的计算路径,并且该路径上转移的符号(忽略 ε)连接起来正好等于输入字符串。

如果所有可能的路径都无法在消耗完输入字符串后到达接受状态,或者路径在半途“卡住”(没有定义相应的转移),则该字符串被拒绝

NFA 与 DFA 的等价性

一个关键的理论结果是:非确定性有限自动机和确定性有限自动机在识别语言的能力上是等价的。也就是说,任何能被一个 NFA 识别的语言,也一定能被某个 DFA 识别,反之亦然。

以下是证明思路:

  1. 从 NFA 构造等价的 DFA:这个构造称为子集构造法。其核心思想是,让 DFA 的每个状态对应原 NFA 的一个状态集合。这个 DFA 状态代表了 NFA 在读取了部分输入后,所有可能处于的状态的集合。
    • DFA 的初始状态是 NFA 初始状态的 ε-闭包(即从初始状态出发,仅通过 ε-转移所能到达的所有状态的集合)。
    • DFA 的转移函数定义为:对于当前状态集合 S 和输入符号 a,下一个状态集合是 S 中每个状态通过 a 转移所能到达的所有状态的 ε-闭包的并集。
    • DFA 的接受状态是那些包含了至少一个 NFA 接受状态的集合。
  2. 复杂度分析:这种构造法可能导致状态数指数级增长。如果原 NFA 有 n 个状态,那么构造出的 DFA 最多可能有 2^n 个状态。这表明,虽然 NFA 和 DFA 能力等价,但 NFA 在描述某些语言时可能比 DFA 简洁得多

NFA 的优势与应用

尽管 NFA 和 DFA 能力等价,但 NFA 在理论和实践中都有其独特优势:

  • 设计更简洁直观:对于某些模式,用 NFA 描述比用 DFA 更简单、更自然。例如,包含“或”关系的模式。
  • 作为理论工具:NFA 是研究正则表达式、正则语言性质的重要桥梁。正则表达式可以很容易地转换为等价的 NFA。
  • 模型某些计算过程:非确定性可以抽象地模拟并行计算或“猜测”过程。

总结

本节课中我们一起学习了非确定性有限自动机。我们首先定义了 NFA,理解了其非确定性和 ε-转移的核心特性。接着,我们探讨了 NFA 通过计算路径接受字符串的方式。然后,我们学习了 NFA 与 DFA 等价的关键定理,以及通过子集构造法将 NFA 转换为 DFA 的方法,并认识到这种转换可能导致状态数指数增长。最后,我们了解了 NFA 在简洁性和作为理论工具方面的优势。NFA 是理解计算模型多样性和正则语言理论的重要一步。

004:正则表达式

概述

在本节课中,我们将要学习正则表达式。正则表达式是一种强大的工具,用于描述字符串的模式。我们将从基本概念开始,逐步理解其形式化定义、操作以及如何与有限自动机相关联。

正则表达式的基本概念

上一节我们介绍了课程概述,本节中我们来看看正则表达式的核心概念。正则表达式是一种用于描述字符串集合(称为语言)的紧凑方式。它们基于一个给定的字母表。

公式:设 Σ 是一个字母表。正则表达式 R 递归地定义如下:

  1. ε(空串)是一个正则表达式。
  2. (空集)是一个正则表达式。
  3. 对于任意 a ∈ Σa 是一个正则表达式。
  4. 如果 RS 是正则表达式,那么 (R ∪ S)(R ∘ S)(R*) 也是正则表达式。

这些操作分别代表并集连接克林星号(零次或多次重复)。

正则表达式的操作与示例

理解了定义后,本节我们来看看这些操作的具体含义以及一些简单示例。

以下是每个操作所描述的语言:

  • 并集 (R ∪ S): 匹配属于 R 的语言 属于 S 的语言的字符串。
  • 连接 (R ∘ S): 匹配一个属于 R 的语言的字符串后跟一个属于 S 的语言的字符串。
  • 克林星号 (R*): 匹配由零个或多个属于 R 的语言的字符串连接而成的任何字符串。

例如,设字母表 Σ = {0, 1}:

  • 表达式 0 描述语言 {0}。
  • 表达式 0 ∪ 1 描述语言 {0, 1}。
  • 表达式 (0 ∘ 1) 描述语言 {01}。
  • 表达式 (0 ∪ 1)* 描述所有由0和1构成的字符串的集合,包括空串 ε。

正则表达式的语法糖

在实际应用中,正则表达式通常包含一些简写形式,使表达式更简洁易读。这些可以看作是核心操作的“语法糖”。

以下是一些常见的语法糖:

  • R+: 表示一次或多次重复,等价于 (R ∘ R*)
  • R?: 表示零次或一次出现,等价于 (R ∪ ε)
  • 字符类 [abc]: 表示匹配 abc 中的任意一个字符,等价于 (a ∪ b ∪ c)
  • 范围 [a-z]: 匹配从 az 的任何小写字母。
  • 补集 [^abc]: 匹配除 abc 外的任何字符。

例如,[a-zA-Z]+ 描述所有由一个或多个字母(不区分大小写)组成的非空字符串。

正则表达式与有限自动机的等价性

一个关键的理论结果是:正则表达式描述的语言类别与有限自动机(FA)接受的语言类别是完全相同的。这意味着任何正则表达式都可以转换为一个等价的有限自动机,反之亦然。

转换思路

  1. 从正则表达式到有限自动机: 基于正则表达式的递归结构进行构造。基础情况(ε, ∅, 单个字符)对应简单的自动机。并集、连接和克林星号操作则对应自动机的组合操作(如并行运行、顺序连接和添加循环)。
  2. 从有限自动机到正则表达式: 可以使用诸如状态消去法等技术。通过系统地消除自动机中的状态,并在转移边上用正则表达式标记,最终得到一个连接起始状态和接受状态的正则表达式。

这个等价性是形式语言理论中的一个基石,它揭示了用代数(正则表达式)和机器(有限自动机)两种不同方式描述模式的能力是相通的。

实践中的正则表达式

在编程和文本处理中,正则表达式被广泛使用。大多数编程语言(如 Python, Java, JavaScript)和命令行工具(如 grepsedawk)都内置了正则表达式引擎。

代码示例(Python)

import re

# 定义一个正则表达式模式:匹配一个或多个数字
pattern = r‘\d+‘

# 要搜索的文本
text = “There are 123 apples and 456 oranges.“

# 查找所有匹配项
matches = re.findall(pattern, text)
print(matches)  # 输出: [‘123‘, ‘456‘]

在这个例子中,\d 是一个语法糖,等价于字符类 [0-9]+ 表示一次或多次重复。因此,\d+ 匹配任何连续的数字序列。

总结

本节课中我们一起学习了正则表达式。我们从其形式化定义开始,了解了三种基本操作:并集、连接和克林星号。接着,我们探讨了实践中常用的语法糖,使表达式更简洁。最重要的是,我们理解了正则表达式与有限自动机在描述语言能力上是等价的,这是理论计算机科学中的一个核心结论。最后,我们看到了正则表达式在实践中的强大应用,它是在文本中搜索、匹配和替换模式的不可或缺的工具。

005:泵引理

在本节课中,我们将学习一个用于判断语言是否为正则语言的重要工具——泵引理。泵引理为正则语言提供了一个必要条件,我们可以用它来证明某些语言不是正则的。

泵引理概述

泵引理是一个关于正则语言性质的定理。它指出,如果一个语言是正则的,那么该语言中所有足够长的字符串都可以被“泵送”。这意味着字符串可以被划分为三个部分,其中中间部分可以重复任意多次(包括零次),而生成的新字符串仍然属于原语言。

泵引理的形式化描述

L 是一个正则语言。那么存在一个常数 n(称为泵长度),使得对于 L 中任何长度不小于 n 的字符串 w,都可以被划分为三个子串 w = xyz,满足以下条件:

  1. |y| > 0 (中间部分非空)
  2. |xy| ≤ n (前两部分的总长度不超过泵长度 n)
  3. 对于所有整数 i ≥ 0,字符串 xyⁱz 仍然属于语言 L

用公式表示:

∃ n ∈ ℕ. ∀ w ∈ L, |w| ≥ n ⇒ ∃ x, y, z. w = xyz ∧ |y| > 0 ∧ |xy| ≤ n ∧ ∀ i ≥ 0. xyⁱz ∈ L

如何使用泵引理证明语言非正则

上一节我们介绍了泵引理的内容,本节我们来看看如何利用它来证明一个语言不是正则的。其核心思想是反证法。

以下是使用泵引理证明语言 L 非正则的标准步骤:

  1. 假设:首先,假设语言 L 是正则的。那么根据泵引理,存在一个泵长度 n
  2. 选择字符串:精心选择一个属于 L 且长度不小于 n 的字符串 w。这个选择通常依赖于 n
  3. 考虑所有划分:根据泵引理,w 可以被划分为 w = xyz,且满足 |y| > 0|xy| ≤ n
  4. 导出矛盾:对于 w 所有可能的划分(满足上述条件),找到一个整数 i(通常取 i=0i=2),使得泵送后的字符串 xyⁱz 不再属于语言 L。这与泵引理的结论矛盾。
  5. 得出结论:因此,最初的假设(L是正则的)是错误的,所以 L 不是正则语言。

一个经典示例

让我们通过一个具体的例子来应用上述步骤。考虑语言 L = { aᵏ bᵏ | k ≥ 0 },即由数量相等的 ab 组成的字符串。

  1. 假设:假设 L 是正则的,设泵长度为 n
  2. 选择字符串:选择字符串 w = aⁿ bⁿ。显然,w ∈ L|w| = 2n ≥ n
  3. 考虑划分:根据泵引理,w 可以写为 xyz,其中 |y| > 0|xy| ≤ n。由于 |xy| ≤ n,所以 y 完全由 a 组成,即 y = aᵖ,其中 p > 0
  4. 导出矛盾:现在考虑 i = 2。泵送后的字符串为 xy²z = aⁿ⁺ᵖ bⁿ。因为 p > 0,所以 a 的数量 n+p 大于 b 的数量 n,因此 xy²z ∉ L。这与泵引理矛盾。
  5. 得出结论:因此,语言 L 不是正则的。

泵引理的局限性

需要注意的是,泵引理是一个必要条件,而非充分条件。这意味着:

  • 如果一个语言是正则的,那么它一定满足泵引理。
  • 但是,如果一个语言满足泵引理,我们不能断定它就是正则的。存在一些非正则语言也满足泵引理的条件。

因此,泵引理主要用于证明一个语言不是正则的。要证明一个语言是正则的,我们通常需要构造一个有限自动机或正则表达式。

总结

本节课中,我们一起学习了泵引理。我们首先了解了它的核心思想:正则语言中足够长的字符串必然包含一个可以重复的“循环”部分。然后,我们详细学习了如何利用泵引理,通过反证法来证明一个语言不是正则的,并通过 { aⁿ bⁿ } 这个经典例子进行了实践。最后,我们指出了泵引理的局限性,它只是一个用于证伪的工具。掌握泵引理是理解正则语言性质和应用的关键一步。

006:上下文无关文法

在本节课中,我们将学习形式语言理论中的一个核心概念:上下文无关文法。我们将了解它的定义、形式化表示、与自动机的关系,以及它在计算机科学,特别是编程语言和编译器设计中的重要性。


概述

上下文无关文法是描述编程语言语法结构的有力工具。它比正则表达式更强大,能够描述嵌套结构(如括号匹配)。本节我们将从直观的语言学例子入手,逐步形式化其定义,并探讨其与下推自动机的等价性。


从正则文法到上下文无关文法

上一节我们介绍了正则文法和有限自动机,它们擅长描述线性模式。然而,许多语言结构(如算术表达式或程序代码中的嵌套块)需要更强的描述能力。

本节中我们来看看上下文无关文法。它通过引入“变量”或“非终结符”来生成语言,其核心规则允许一个变量被替换为一个符号串。

一个上下文无关文法 G 由四个部分组成:

  • 变量(非终结符)集合 V
  • 终结符集合 Σ (与 V 不相交)
  • 产生式规则集合 P, 每条规则形式为 A -> w,其中 A ∈ Vw ∈ (V ∪ Σ)*
  • 起始变量 S ∈ V

上下文无关文法的形式化定义

根据乔姆斯基的划分,上下文无关文法是2型文法。其形式化定义如下:

一个上下文无关文法是一个四元组 G = (V, Σ, P, S),其中:

  • V 是一个有限集合,称为变量非终结符
  • Σ 是一个有限集合,称为终结符,且 V ∩ Σ = ∅
  • P 是一个有限产生式集合,每个产生式形如 A -> α,其中 A ∈ Vα ∈ (V ∪ Σ)*
  • S ∈ V起始变量

推导与语言

从起始符号开始,反复应用产生式规则将变量替换为字符串的过程称为推导。文法 G 生成的语言 L(G) 是所有能由起始符号 S 推导出的、仅由终结符组成的字符串的集合。

以下是推导过程的要点:

  • 推导从起始变量 S 开始。
  • 每一步,我们选择一个变量,并用其某个产生式规则的右侧替换它。
  • 当字符串中不再包含任何变量时,推导结束,得到的字符串属于该文法生成的语言。

上下文无关文法示例

让我们看一个描述简单算术表达式的文法。

G = ({E}, {+, *, (, ), id}, P, E),其中产生式规则 P 为:

E -> E + E
E -> E * E
E -> ( E )
E -> id

这里,E 是变量(表达式),id 代表标识符(如变量名或数字)。这个文法可以生成诸如 id, id + id, id * (id + id) 等字符串。


上下文无关文法与下推自动机

一个重要结论是:上下文无关文法生成的语言类,恰好是下推自动机能够识别的语言类。

这意味着:

  • 对于任何上下文无关文法,都可以构造一个等价的下推自动机来识别其语言。
  • 对于任何下推自动机,都可以构造一个等价的上下文无关文法来生成其识别的语言。

下推自动机可以看作是一个配备了栈的有限自动机,这个栈提供了必要的内存来处理嵌套结构。


乔姆斯基范式

任何上下文无关文法(除了生成空串的规则)都可以转化为一种标准形式——乔姆斯基范式

在乔姆斯基范式中,所有产生式规则都只能是以下两种形式之一:

  1. A -> BC (其中 A, B, C 都是变量)
  2. A -> a (其中 A 是变量,a 是终结符)

这种范式简化了语法分析算法(如CYK算法)和理论证明。


泵引理与上下文无关语言的局限性

与正则语言类似,也存在一个泵引理用于证明某个语言不是上下文无关的。

上下文无关语言的泵引理:如果一个语言 L 是上下文无关的,则存在一个泵长度 p,使得 L 中任何长度不小于 p 的字符串 s 都可以写成 s = uvwxy 的形式,满足:

  1. |vwx| ≤ p
  2. |vx| ≥ 1
  3. 对于所有 i ≥ 0,字符串 u v^i w x^i y 也属于 L

利用泵引理可以证明像 {a^n b^n c^n | n ≥ 0} 这样的语言不是上下文无关的。


上下文无关文法的封闭性与决策问题

上下文无关语言在某些运算下是封闭的,但在另一些下则不封闭。

以下是封闭的运算:

  • 并集
  • 连接
  • 克莱尼星号
  • 与正则语言的交集
  • 同态映射

以下是不封闭的运算:

  • 交集(两个上下文无关语言的交集不一定是上下文无关的)
  • 补集(上下文无关语言的补集不一定是上下文无关的)

关于上下文无关文法的一些基本决策问题是可判定的,例如:

  • 成员性问题:给定字符串 w 和文法 Gw 是否属于 L(G)?(是,例如使用CYK算法)
  • 空性问题:L(G) 是否为空?(是)
  • 无穷性问题:L(G) 是否无穷?(是)


总结

本节课中我们一起学习了上下文无关文法。我们从其动机和形式化定义出发,理解了它如何通过变量和产生式规则来描述嵌套结构语言。我们探讨了它与下推自动机的等价关系,这是计算理论中的一个核心结论。我们还介绍了标准形式(乔姆斯基范式)、用于证明语言非上下文无关性的泵引理,以及上下文无关语言的封闭性质。上下文无关文法是编译器前端(语法分析器)的理论基础,对于理解和设计编程语言至关重要。

007:凯勒自动机

在本节课中,我们将学习凯勒自动机。这是一种特殊的自动机模型,它使用一个栈作为辅助存储。我们将了解其基本定义、工作原理,以及它与上下文无关文法之间的重要联系。


凯勒自动机的定义

上一节我们介绍了自动机的基本概念,本节中我们来看看凯勒自动机的形式化定义。

一个凯勒自动机是一个六元组:

M = (Q, Σ, Γ, δ, q0, Z0, F)

其中:

  • Q 是有限状态集。
  • Σ 是有限的输入字母表。
  • Γ 是有限的栈字母表。
  • δ 是转移函数:δ: Q × (Σ ∪ {ε}) × Γ → P_finite(Q × Γ*)
  • q0 ∈ Q 是初始状态。
  • Z0 ∈ Γ 是初始栈符号。
  • F ⊆ Q 是接受状态集。

这个定义的核心在于转移函数 δ。它根据当前状态、读入的输入符号(或空串 ε)以及栈顶符号,决定下一个状态以及要压入栈顶的符号串(用以替换栈顶符号)。


凯勒自动机如何工作?

理解了定义后,我们来看看凯勒自动机是如何运行的。其运行依赖于一个称为“格局”的概念。

一个格局 是一个三元组 (q, w, γ),其中:

  • q ∈ Q 是当前状态。
  • w ∈ Σ* 是待处理的剩余输入串。
  • γ ∈ Γ* 是当前栈的内容(栈顶在左侧)。

以下是格局之间转移的规则:
如果存在转移 δ(q, a, Z) 包含 (p, β),那么可以从格局 (q, aw, Zα) 转移到格局 (p, w, βα)。这里 a 可以是输入符号或 ε

自动机从初始格局 (q0, w, Z0) 开始运行。如果经过一系列转移后,到达一个格局 (q_f, ε, γ),其中 q_f ∈ F(即接受状态),并且输入串已消耗完,那么自动机就接受 输入串 w。这种接受方式称为终态接受

另一种等价的接受标准是空栈接受:如果自动机运行后到达格局 (q, ε, ε),即栈变为空,则接受输入串。可以证明,这两种接受方式在能力上是等价的。


凯勒自动机与上下文无关文法

我们已经了解了凯勒自动机如何运行,现在探讨它与其他计算模型的关系。最重要的关系是:凯勒自动机识别的语言类恰好是上下文无关语言

这意味着:

  1. 对于任何一个上下文无关文法 G,都可以构造出一个凯勒自动机 M,使得 L(M) = L(G)
  2. 对于任何一个凯勒自动机 M,都可以构造出一个上下文无关文法 G,使得 L(G) = L(M)

这个等价性是理论计算机科学的一个核心结论。它表明,尽管凯勒自动机(使用栈)和上下文无关文法(使用重写规则)在形式上完全不同,但它们描述语言的能力是完全相同的。


凯勒自动机的例子

为了加深理解,我们来看一个具体的例子。考虑语言 L = {a^n b^n | n ≥ 0},这是一个经典的上下文无关语言。

我们可以设计一个凯勒自动机来识别它:

  • 思路:每读入一个 a,就向栈中压入一个特定符号(例如 A)。然后,每读入一个 b,就从栈中弹出一个 A。最后检查栈是否为空。
  • 操作
    1. 初始状态,栈底为 Z0
    2. a:压入 A
    3. b:弹出 A
    4. 输入结束时,如果栈中只剩下 Z0,则接受。

这个简单的自动机清晰地展示了如何使用栈来“计数”和匹配符号。


凯勒自动机的变体与性质

凯勒自动机有一些重要的变体和性质。

确定性凯勒自动机
我们之前定义的是非确定性的凯勒自动机。如果对转移函数 δ 加以限制,使得在任何格局下至多只有一种可能的转移,就得到了确定性凯勒自动机。确定性凯勒自动机识别的语言类(确定性上下文无关语言)是真包含于非确定性凯勒自动机所识别的语言类(所有上下文无关语言)的。这意味着非确定性对于凯勒自动机的能力有实质性的增强。

封闭性
上下文无关语言(即凯勒自动机识别的语言)在某些运算下是封闭的,在另一些下则不封闭:

  • 封闭于:并、连接、闭包、与正则语言的交、同态。
  • 不封闭于:交、补、差。

例如,两个上下文无关语言的交不一定还是上下文无关语言。


总结

本节课中我们一起学习了凯勒自动机。我们从其形式化定义出发,了解了它如何利用栈进行工作,并通过格局转移来描述其计算过程。我们掌握了凯勒自动机有两种等价的接受方式:终态接受和空栈接受。最重要的是,我们认识到凯勒自动机与上下文无关文法在表达能力上是完全等价的,它们共同定义了上下文无关语言这一重要的语言类。最后,我们简要探讨了其确定性变体以及上下文无关语言的封闭性质。凯勒自动机是连接自动机理论和形式文法理论的关键桥梁,也是理解编译器解析等实际应用的基础。

008:确定性上下文无关文法

概述

在本节课中,我们将学习确定性上下文无关文法。我们将探讨其核心概念、形式化定义,以及它在实际计算中的应用,例如语法高亮和代码解析。


确定性下推自动机

上一节我们介绍了上下文无关文法,本节中我们来看看其对应的自动机模型——下推自动机。

确定性下推自动机是一种特殊的自动机,它包含一个栈用于存储信息。其形式化定义如下:

一个确定性下推自动机是一个七元组:
M = (Q, Σ, Γ, δ, q0, Z0, F)
其中:

  • Q 是有限状态集。
  • Σ 是有限的输入字母表。
  • Γ 是有限的栈字母表。
  • δ 是转移函数:Q × (Σ ∪ {ε}) × Γ → Q × Γ*
  • q0 ∈ Q 是初始状态。
  • Z0 ∈ Γ 是初始栈符号。
  • F ⊆ Q 是接受状态集。

确定性下推自动机与普通下推自动机的关键区别在于,对于任何给定的当前状态、输入符号和栈顶符号,其转移函数 δ 至多只有一个可能的动作。这意味着其计算路径是唯一确定的。


确定性上下文无关语言

理解了确定性下推自动机后,我们现在可以定义它所能识别的语言类别。

由确定性下推自动机接受的语言,称为确定性上下文无关语言。并非所有上下文无关语言都是确定性的。确定性上下文无关语言类是真包含于上下文无关语言类的一个子集。

确定性上下文无关语言在实践中有重要价值,因为其对应的解析器可以高效地(在线性时间内)运行。许多编程语言的语法都设计成是确定性上下文无关的,以便于编译和解析。


句柄与归约

为了理解解析器如何工作,我们需要引入“句柄”这个概念。

在上下文无关文法的推导过程中,句柄是一个产生式的右部,它代表了当前可以进行归约(即用产生式左部替换其右部)的位置。识别句柄是自底向上解析算法的核心。

以下是识别句柄的关键步骤:

  1. 从输入字符串开始。
  2. 使用文法规则,自底向上地构造推导树。
  3. 当某个子串匹配某个产生式 A → β 的右部 β,并且该归约能最终导向起始符号时,β 就是一个句柄。
  4. 将句柄 β 归约为其左部 A

LL(k) 与 LR(k) 解析器

基于对句柄的不同寻找策略,发展出了两大类主要的确定性解析器。

以下是两种主要的确定性解析算法:

  • LL(k) 解析器:这是一种自顶向下的解析方法。LL(k) 中的第一个 L 表示从左向右扫描输入,第二个 L 表示产生最左推导,k 表示向前查看 k 个符号来决定使用哪个产生式。
  • LR(k) 解析器:这是一种自底向上的解析方法。LR(k) 中的 L 表示从左向右扫描输入,R 表示产生最右推导的逆,k 同样是向前查看的符号数。LR 解析器能处理比 LL 文法更广泛的文法类,是实践中编译器构造的基石。

LR 解析器家族包括 SLR、LALR 和规范 LR 等,它们在处理能力和状态表大小之间有不同的权衡。


实际应用:语法高亮与解析

这些理论概念直接应用于我们日常使用的开发工具中。

现代代码编辑器和集成开发环境中的语法高亮、代码自动完成和错误检查功能,其核心就是一个针对该编程语言文法的确定性解析器(通常是 LR 或 LL 变种)。这个解析器在后台运行,分析你的代码文本,识别出关键字、变量、字符串等不同语法成分,并据此进行高亮或提示。

例如,Markdown 语言的解析器也是一个确定性解析器,它识别 # 表示标题,` 表示代码块等语法结构。


总结

本节课中我们一起学习了确定性上下文无关文法。我们从其自动机模型——确定性下推自动机开始,定义了它所能识别的确定性上下文无关语言。然后,我们探讨了自底向上解析的核心概念“句柄”,并介绍了基于此的 LL 和 LR 两类主要确定性解析算法。最后,我们看到了这些理论如何直接应用于语法高亮、代码解析等实际场景中,构成了现代编程工具的基础。

009:图灵机 🧠

在本节课中,我们将学习计算机科学的核心模型——图灵机。图灵机是一个抽象的计算模型,它帮助我们理解计算的本质和极限。我们将从图灵机的历史背景开始,逐步介绍其基本构成、工作原理、形式化定义以及其强大的计算能力。

历史背景与核心思想 📜

上一节我们介绍了自动机等计算模型,本节中我们来看看一个更强大、更通用的模型。

图灵机由艾伦·图灵于1936年提出。这篇开创性的论文旨在形式化地定义“可计算性”这个概念。图灵设想了一种机器,它能够模拟任何人类“计算机”(指执行计算的人)的思维过程。这个思想实验将机械过程与计算联系起来,为理论计算机科学奠定了基础。

图灵机的核心思想是:任何可以由算法描述的过程,都可以由图灵机来执行。这意味着,如果一个问题可以被清晰地计算,那么就存在一台解决这个问题的图灵机。

图灵机的基本构成 ⚙️

图灵机是一个物理和概念的混合体。我们可以将其想象为一个带有读写头的机器,在一个无限长的纸带上进行操作。

以下是图灵机的主要组成部分:

  1. 无限长的纸带:纸带被划分为连续的格子,每个格子可以包含一个符号。纸带是双向无限的。
  2. 读写头:读写头每次对准纸带上的一个格子。它可以读取当前格子的符号,也可以写入一个新符号。
  3. 状态寄存器:存储机器当前所处的状态。状态是有限的。
  4. 控制规则表(转移函数):这是一个指令集,根据机器当前的状态和读写头读到的符号,决定机器下一步该做什么。下一步动作包括:
    • 写入一个新符号到当前格子。
    • 将读写头向左或向右移动一格。
    • 改变到下一个状态。

图灵机从初始状态和输入字符串(写在纸带上,其余格子为空白)开始,一步步执行转移函数。它最终会进入接受状态拒绝状态并停机,或者永远循环下去。

形式化定义 📐

为了使讨论更精确,我们需要对图灵机进行形式化定义。

一台确定性图灵机 M 可以定义为一个七元组:
M = (Q, Σ, Γ, δ, q0, q_accept, q_reject)
其中:

  • Q 是有限的状态集合。
  • Σ 是有限的输入字母表(不包括空白符号)。
  • Γ 是有限的纸带字母表,满足 Σ ⊆ Γ,且包含一个特殊的空白符号(通常记为 B)。
  • δ 是转移函数:δ: Q × Γ → Q × Γ × {L, R}。它表示在状态 q 读到符号 a 时,机器将写入符号 b,移动到方向 D(左L或右R),并进入新状态 q‘
  • q0 ∈ Q 是初始状态。
  • q_accept ∈ Q 是接受状态。
  • q_reject ∈ Q 是拒绝状态,且 q_accept ≠ q_reject

格局与计算

机器的格局描述了其在某个时刻的完整快照,包括:

  • 当前状态 q ∈ Q
  • 纸带内容。
  • 读写头的位置。

计算始于初始格局(状态为 q0,输入字符串写在纸带上,读写头指向输入的第一个符号)。然后,机器根据转移函数 δ 从一个格局推导到下一个格局。

如果机器最终进入状态 q_accept,则称它接受输入字符串。如果进入 q_reject,则称它拒绝输入字符串。如果永远无法进入这两个状态,则机器在输入上不停机(循环)。

图灵机的计算能力 💪

图灵机是极其强大的计算模型。其核心论点是:

图灵机可以计算任何“可计算”的函数。

这被称为丘奇-图灵论题。它不是一个可以被证明的定理,而是一个被广泛接受的基本原理。所有合理的计算模型(如λ演算、递归函数、现代编程语言)都被证明与图灵机在计算能力上是等价的。

这意味着,如果一个问题无法用图灵机解决,那么在任何现实的计算机上也无法用算法解决。

作为语言识别器

图灵机可以识别形式语言。一台图灵机 M 所识别的语言 L(M) 是所有能被 M 接受的字符串的集合。
L(M) = { w ∈ Σ* | M 接受 w }

能够被某个图灵机识别的语言称为递归可枚举语言。如果对于所有输入,图灵机都保证会停机(即总是进入 q_acceptq_reject),那么它识别的语言称为递归语言可判定语言

图灵机的变体 🔄

有许多图灵机的变体,例如多带图灵机、非确定性图灵机、随机存取图灵机等。

一个关键结论是:许多图灵机的变体在计算能力上与基本的标准单带确定性图灵机是等价的。它们可能在某些方面(如时间效率)上有所不同,但不能解决更多的问题。

例如:

  • 多带图灵机:有多个纸带和读写头。这可以简化某些问题的编程,并可能提高效率,但其识别的语言类与单带图灵机相同。
  • 非确定性图灵机:在每个步骤可以有多个可能的动作(转移)。它通过“猜测”和并行计算来运作。尽管这看起来更强大,但任何非确定性图灵机都可以被一台确定性图灵机模拟,尽管模拟可能需要指数级更多的时间。

模拟与等价性证明示例 🧪

为了理解不同变体之间的等价性,我们可以看一个简单的模拟思想:用单带图灵机模拟多带图灵机

基本思路是:

  1. 单带图灵机的纸带被划分为多个轨道(通过使用一个扩展的字母表,其符号是原字母表符号的元组)。每个轨道模拟多带机的一条纸带。
  2. 增加一个特殊的轨道来标记各条模拟纸带上读写头的位置。
  3. 单带机的读写头在一次扫描中,可以读取所有轨道上的符号(即一个元组),并根据多带机的转移函数更新这些轨道的内容和头位置标记。

通过这种方式,单带机可以一步步地模拟多带机的计算。这证明了多带图灵机并没有增加根本的计算能力。

总结 🎯

本节课中我们一起学习了图灵机,这是理论计算机科学的基石。

  • 我们了解了图灵机的历史背景和核心思想:为“可计算性”建立形式模型。
  • 我们详细拆解了图灵机的物理构成和形式化数学定义 (Q, Σ, Γ, δ, q0, q_accept, q_reject)
  • 我们理解了图灵机如何通过格局转移进行计算,并定义其接受、拒绝和不停机的行为。
  • 我们掌握了丘奇-图灵论题的核心地位,它断言图灵机抓住了“有效计算”的本质。
  • 我们探讨了图灵机的多种变体(如多带机、非确定性机),并认识到它们在计算能力上与基本模型等价,这通过模拟的思想得以证明。

图灵机不仅是一个历史概念,它持续影响着我们对计算复杂性、可判定性以及计算机极限的思考。

010:可数性

在本节课中,我们将学习可数性的核心概念,包括集合的基数、可数集与不可数集的定义,以及康托尔对角线论证法。我们将通过图灵机、编码和函数映射等工具来理解这些概念。


可数集与不可数集

上一节我们介绍了集合的基本概念,本节中我们来看看如何比较不同集合的“大小”。

一个集合的基数,指的是其元素的数量。如果两个集合之间存在一个双射函数,即一个一一对应且映满的函数,那么这两个集合具有相同的基数。

定义: 如果存在一个从自然数集 N 到集合 A 的双射函数 f: N → A,则称集合 A 是可数的。

这意味着,可数集的元素可以像自然数一样被“数”出来,即使它是无限的(例如自然数集本身、整数集、有理数集)。


图灵机与编码

为了形式化地讨论可计算性问题,我们需要一个精确的模型。图灵机就是这样一个模型。

一台图灵机由以下几个部分组成:

  • 一个无限长的纸带,被划分为格子。
  • 一个读写头,可以读取、写入纸带上的符号,并左右移动。
  • 一套控制规则(状态转移函数),根据当前状态和读到的符号,决定写入什么符号、如何移动以及转移到什么新状态。
  • 一个状态寄存器,记录当前状态。

我们可以用有限的符号(字母表)来编码图灵机的描述。例如,我们可以将图灵机的状态、规则等编码成一个字符串。

核心概念: 存在一个通用图灵机 U。它接受两个输入:一个是对另一台图灵机 M 的描述编码 ,另一个是输入字符串 w。通用图灵机 U 能够模拟 M 在输入 w 上的运行:U(, w) = M(w)

这意味着,所有图灵机的集合是可数的,因为每台图灵机都可以被编码成一个有限的字符串,而所有有限字符串的集合是可数的。


康托尔对角线论证

现在,我们来看一个著名的论证,它证明了并非所有无限集都是可数的。

考虑所有自然数子集的集合,记作 P(N)。我们断言 P(N) 是不可数的。

证明(康托尔对角线法):

  1. 假设 P(N) 是可数的。那么我们可以将其所有元素(即自然数的子集)列成一个无穷序列:S₁, S₂, S₃, ...
  2. 我们构造一个新的自然数子集 D。构造规则如下:对于每个自然数 i,检查 i 是否属于 Sᵢ
    • 如果 i ∈ Sᵢ,则令 i ∉ D
    • 如果 i ∉ Sᵢ,则令 i ∈ D
  3. 这样构造出的 D 是一个自然数的子集。根据假设,它应该出现在我们的列表中,即存在某个 k,使得 D = Sₖ
  4. 现在问:k 是否属于 D
    • 如果 k ∈ D,根据 D 的定义(k ∈ D 当且仅当 k ∉ Sₖ),意味着 k ∉ Sₖ。但 D = Sₖ,所以 k ∉ D,矛盾。
    • 如果 k ∉ D,根据 D 的定义(k ∉ D 当且仅当 k ∈ Sₖ),意味着 k ∈ Sₖ。但 D = Sₖ,所以 k ∈ D,矛盾。
  5. 因此,假设错误。P(N) 是不可数的。

这个论证的精妙之处在于,通过“对角线”上的元素(即判断 i 是否属于 Sᵢ)来构造一个不可能在列表中出现的集合 D


实数集是不可数的

康托尔对角线论证的一个直接应用是证明实数集 R 是不可数的。我们可以将实数(例如0到1之间的实数)用二进制或十进制无限小数表示,然后使用类似的对角线方法构造一个不在列表中的新实数。

结论: 实数集 R 的基数严格大于自然数集 N 的基数。R 的基数被称为“连续统的势”。


可计算函数与不可计算函数

将图灵机编码的思想与康托尔对角线法结合,可以得出深刻的结论。

所有图灵机的集合是可数的。每台图灵机计算一个函数(可能是部分函数)。因此,所有可计算函数的集合也是可数的。

然而,所有从自然数到自然数的函数的集合 F = {f | f: N → N} 是不可数的。这可以通过类似于 P(N) 的论证来证明。

核心结论: 存在不可计算的函数。因为可计算函数是可数的,而所有函数的集合是不可数的,所以绝大多数函数都是不可计算的。


停机问题

一个具体的不可判定问题的例子是停机问题:给定一台图灵机 M 的描述和输入 w,判断 M 在输入 w 上是否会最终停机(而不是无限循环)。

证明(草图,利用对角线法):

  1. 假设存在一台图灵机 H 可以判定停机问题。即 H(, w) 输出“是”如果 M(w) 停机,输出“否”如果 M(w) 不停机。
  2. 我们构造一台新的图灵机 D,它以自己的描述编码 作为输入。D 的运行如下:
    • D 调用 H 来判断 D() 是否会停机。
    • 如果 H 判断 D() 会停机,那么 D 就进入无限循环。
    • 如果 H 判断 D() 不会停机,那么 D 就立即停机。
  3. 现在分析 D() 的行为:
    • 如果 D() 停机,那么根据 D 的构造,H 应该判断它不会停机,这意味着 D() 不会停机,矛盾。
    • 如果 D() 不停机,那么根据 D 的构造,H 应该判断它会停机,这意味着 D() 会停机,矛盾。
  4. 因此,这样的判定机 H 不可能存在。停机问题是不可判定的。

这个证明是康托尔对角线思想在可计算性理论中的经典应用。


乔姆斯基层级与可计算性

语言(字符串的集合)可以根据识别它们的计算模型的复杂度进行分类,这就是乔姆斯基层级

以下是主要的层级:

  • 正则语言: 可以由有限自动机识别。表达能力有限。
  • 上下文无关语言: 可以由下推自动机识别。常用于描述编程语言的语法。
  • 上下文相关语言: 可以由线性有界自动机识别。
  • 递归可枚举语言: 可以由图灵机识别。这是图灵机能够接受的所有语言的集合。
    • 其子集是递归语言(或可判定语言),即存在一台图灵机总能判定一个字符串是否属于该语言(总是停机并给出是/否答案)。
    • 停机问题对应的语言是递归可枚举的,但不是递归的。

核心关系: 图灵机是这些自动机模型中能力最强的。递归可枚举语言包含了所有可由算法(图灵机)半判定的语言,而递归语言则对应那些可由算法完全判定的语言。


总结

本节课中我们一起学习了:

  1. 可数性: 通过建立与自然数的双射来定义可数集。自然数集、整数集、有理数集都是可数的。
  2. 康托尔对角线论证: 一个强大的证明工具,用于证明某些集合(如自然数幂集 P(N)、实数集 R)是不可数的,其基数大于自然数集。
  3. 图灵机与编码: 图灵机是计算的形式模型,可以被有限编码。所有图灵机的集合是可数的。
  4. 可计算性界限: 由于图灵机可数而所有函数不可数,因此存在(事实上是绝大多数)不可计算的函数。
  5. 停机问题: 一个具体的、利用对角线法证明的不可判定问题,是计算理论的核心结果之一。
  6. 乔姆斯基层级: 描述了不同形式语言类与对应计算模型(自动机)之间的关系,图灵机位于这个层级的顶端。

这些概念奠定了理论计算机科学的基础,揭示了计算的本质、能力与极限。

011:不可识别与不可判定 🧠

📌 概述

在本节课中,我们将学习理论计算机科学中的两个核心概念:不可识别(Unrecognizable)不可判定(Undecidable)问题。通过引入图灵机模型与对角线方法,我们将理解为什么有些问题在计算上是无法解决的,并进一步掌握这些结论的形式化表达。


🧩 一、计算模型与问题形式化

上一节我们介绍了计算的基本思想,本节首先回顾计算模型的形式化定义。

在理论计算机科学中,我们使用图灵机(Turing Machine)作为标准计算模型,用于刻画“可计算性”。

核心定义如下:

图灵机 M = (Q, Σ, Γ, δ, q0, q_accept, q_reject)

其中:

  • Q:状态集合
  • Σ:输入字母表
  • Γ:带字母表
  • δ:状态转移函数
  • q0:初始状态
  • q_accept / q_reject:接受/拒绝状态

在这个模型下,我们可以形式化“问题”为语言(Language)

L ⊆ Σ*

即一个字符串集合。


⚙️ 二、可判定性与可识别性

在建立了计算模型之后,我们来区分两种重要的问题类型。

以下是两种基本概念:

  • 可判定(Decidable)问题
  • 可识别(Recognizable)问题

其形式化定义如下:

1️⃣ 可判定问题

如果存在一个图灵机 M,对任意输入 w

w ∈ L  → M 接受  
w ∉ L  → M 拒绝  

并且总是停机,则称语言 L 是可判定的。


2️⃣ 可识别问题

如果存在图灵机 M

w ∈ L  → M 接受  
w ∉ L  → M 可能拒绝或无限循环  

则称 L 是可识别的。


在这一基础上,我们可以得到关系:

可判定 ⊆ 可识别

🚫 三、不可判定问题

在理解了可判定问题之后,本节重点进入不可判定性。

以下是关键结论:

存在某些问题,不存在任何图灵机可以对所有输入给出正确且终止的判断。

经典例子是停机问题(Halting Problem)

HALT = { ⟨M, w⟩ | M 在输入 w 上会停机 }

结论:

HALT 是不可判定的

🧮 四、康托尔对角线方法

上一节介绍了计算模型,本节进一步使用数学方法证明不可判定性。

核心思想来自对角线法(Diagonalization)

假设所有程序可以列举为:

M1, M2, M3, ...

构造一个新函数 D

D(i) = 1  如果 Mi(i) = 0  
D(i) = 0  如果 Mi(i) = 1

该函数在第 i 位与 Mi 不同,因此:

D ≠ Mi  对所有 i

这说明:

存在函数不在任何图灵机列表中 → 不可计算


🔍 五、不可识别问题

在不可判定之外,还有更强的结论:不可识别。

定义如下:

如果语言 L

不存在图灵机 M,使得 w ∈ L 时 M 接受

则称 L不可识别


进一步可以得到:

不可识别 ⊂ 不可判定

并且:

若 L 不可识别,则 L 和其补集至少一个不可识别

🔁 六、补集与对称性

上一节我们介绍了不可识别语言,本节进一步分析其补集性质。

关键结论如下:

  • L 可判定,则:
L̄ 也可判定
  • L 可识别,则:
L̄ 不一定可识别

一个重要结论:

L 可判定 ⇔ L 和 L̄ 都可识别

🧠 七、与逻辑和数学基础的联系

在理解计算不可解问题后,我们可以将其与数学基础联系起来。

相关思想包括:

  • 形式系统(Formal Systems)
  • 公理化方法(Axiomatic Systems)
  • 哥德尔不完备性

这些理论共同说明:

某些真命题无法通过有限规则证明
某些问题无法通过算法解决


🏁 总结

本节课中,我们一起学习了:

  • 图灵机作为计算模型的定义
  • 可判定与可识别问题的区别
  • 停机问题的不可判定性
  • 康托尔对角线方法
  • 不可识别语言的概念
  • 补集与可识别性的关系
  • 计算理论与数学逻辑的联系

这些内容构成了理论计算机科学中关于“计算极限”的核心基础,为后续学习复杂性理论与形式语言打下了基础。

012:停机问题与归约

在本节课中,我们将学习计算理论中的一个核心概念:停机问题。我们将探讨其定义、不可判定性证明,以及如何通过“归约”方法,将停机问题与其他计算问题联系起来。


概述:什么是停机问题?🤔

停机问题探讨的是:是否存在一个通用算法,能够判断任意一个程序在给定输入下是否会最终停止运行,还是会无限循环下去。

这是一个不可判定问题,意味着不存在一个图灵机(或算法)能够为所有可能的程序-输入对给出正确的“是/否”答案。


对角化论证与自指矛盾 🔄

上一节我们介绍了图灵机作为计算模型。本节中我们来看看如何证明停机问题的不可判定性,其核心思想是对角化论证和自指。

我们假设存在一个能解决停机问题的图灵机 H。它的描述如下:

H(M, w) = 如果图灵机 M 在输入 w 上停机,则接受;否则(即无限循环),则拒绝。

现在,我们利用 H 来构造另一个图灵机 D,它的行为基于 H 的判断结果:

D(M) = 如果 H(M, M) 接受(即 M 在自身编码上停机),则 D 进入无限循环;
        如果 H(M, M) 拒绝(即 M 在自身编码上不停机),则 D 立即停机。

这里出现了矛盾:如果我们问 D 在它自身的编码 上是否会停机呢?

  • 如果 D() 停机,根据 D 的定义,这意味着 H(D, D) 必须拒绝,而 H 拒绝表示 D() 不停机。
  • 如果 D() 不停机,根据 D 的定义,这意味着 H(D, D) 必须接受,而 H 接受表示 D() 停机。

无论哪种情况,我们都得出了矛盾的结论。因此,我们最初的假设——存在能判定停机问题的图灵机 H——是错误的。


补集运算与复杂性类 🔀

我们刚刚证明了停机问题是不可判定的。一个自然的问题是:这个问题的“补集”是否可判定?一个语言的补集包含所有不在原语言中的字符串。

以下是关于补集运算的重要观察:

  • 如果一个问题 A 是可判定的,那么它的补集 ¬A 也是可判定的。你只需要运行判定 A 的算法,然后反转输出结果即可。
  • 然而,对于可识别(递归可枚举)语言,情况则不同。一个语言 A 及其补集 ¬A 如果都是递归可枚举的,那么 A 实际上是可判定的。

停机问题的补集(即判断一个程序是否不停机)同样也是不可判定的。因为如果它的补集可判定,利用上述第一条性质,我们可以间接判定停机问题本身,这与我们已经证明的结论矛盾。


归约:比较问题的难度 🧩

要证明一个新问题 B 也是不可判定的,我们不需要每次都从头进行复杂的对角化论证。我们可以使用一种称为“归约”的技术。

归约的核心思想是:如果我们已经知道问题 A(例如停机问题)是不可判定的,并且我们能将 A 的任意实例“转化”为问题 B 的一个实例,使得 A 的答案等价于 B 的答案,那么问题 B 也一定是不可判定的。

形式化地说,如果存在一个可计算函数 f,使得对于所有输入 x

x ∈ A 当且仅当 f(x) ∈ B

那么我们就说 A 可归约到 B(记作 A ≤ B)。如果 A 不可判定,那么 B 也不可判定。


应用:证明其他问题的不可判定性 🛠️

利用归约技术,我们可以从停机问题出发,证明一大批问题的不可判定性。以下是几个经典例子:

1. 空白带停机问题

  • 问题:给定一个图灵机 M,判断 M 在空输入(空白带)上是否停机。
  • 归约:对于任意一个“普通”停机问题实例 (M, w),我们可以构造一个新的图灵机 M_wM_w 的行为是:首先在带上写下 w,然后将读写头移到起始位置,最后模拟 Mw 上的运行。这样,Mw 上停机 当且仅当 M_w 在空白带上停机。由于停机问题不可判定,故空白带停机问题也不可判定。

2. 语言等价性问题

  • 问题:给定两个图灵机 M1M2,判断它们是否识别相同的语言(即对于所有输入,输出相同)。
  • 归约:我们可以将空白带停机问题归约到此。构造一个图灵机 M_∅,它拒绝所有输入(即识别空集)。对于任意一个图灵机 M,判断 M 是否与 M_∅ 等价,实际上就是在判断 M 是否拒绝所有输入,这比判断它在空白带上是否停机更难。因此,语言等价性问题不可判定。

3. 希尔伯特第十问题(丢番图方程整数解)

  • 问题:给定一个整系数多项式方程,判断是否存在整数解。
  • 结论:马蒂亚谢维奇证明了该问题不可判定。这展示了数论中一个看似纯粹的问题,其难度与停机问题相当。

总结与展望 🎯

本节课中我们一起学习了:

  1. 停机问题的定义及其通过对角化论证证明的不可判定性。
  2. 补集运算对可判定性可识别性类的影响。
  3. 归约作为证明问题不可判定性的核心工具:如果问题 A 可归约到问题 B,且 A 不可判定,则 B 也不可判定。
  4. 利用归约,我们证明了如空白带停机语言等价性等多个自然问题是不可判定的。

停机问题是计算理论中许多不可判定性结果的源头。理解它和归约方法,为我们分析算法的极限、以及理解哪些问题在原则上无法通过计算解决,奠定了坚实的基础。在后续课程中,我们将探讨计算复杂性理论,研究那些虽然可判定但需要大量计算资源(如时间、空间)的问题。


013:赖斯定理

概述

在本节课中,我们将学习理论计算机科学中的一个核心定理——赖斯定理。该定理揭示了关于图灵机可计算函数非平凡性质的深刻限制。我们将从图灵机的基本概念出发,逐步理解赖斯定理的表述、证明思路及其重要含义。


图灵机与可计算函数

上一节我们介绍了计算模型的基础。本节中我们来看看图灵机与它定义的可计算函数。

图灵机是一种抽象的计算模型。一台图灵机 M 定义了一个从输入字符串到输出字符串的(偏)函数。我们记图灵机 M 计算的函数为 φ_M

如果存在一台图灵机 M 使得对于所有输入 xM(x) 停机并输出 f(x),则称函数 f图灵可计算的。


问题的归约与不可判定性

在深入赖斯定理之前,我们需要理解“归约”的概念。这是比较问题计算难度的核心工具。

如果我们可以利用解决问题 A 的方法来解决问题 B,则称问题 B 可以归约到问题 A。形式化地说,如果存在一个可计算函数 f,使得对于任意输入 xx ∈ B 当且仅当 f(x) ∈ A,则 B ≤_T A

一个关键结论是:如果 A 是不可判定的(即不存在总是停机的图灵机来判定 A),并且 B ≤_T A,那么 B 也是不可判定的。


非平凡性质

现在,我们关注图灵机所计算的函数集合的“性质”。

P 是图灵可计算函数集合的一个性质。我们可以将 P 看作一个函数集合:P ⊆ { f | f 是图灵可计算的 }

我们称性质 P 是:

  • 平凡的:如果 P 是空集,或者 P 包含所有图灵可计算函数。
  • 非平凡的:如果 P 不是平凡的,即至少有一个可计算函数具有性质 P,也至少有一个可计算函数不具有性质 P

以下是关于图灵机函数的一些性质示例:

  • 函数 f 对输入 “0” 的输出是 “1”。
  • 函数 f 的定义域(即使其停机的输入集合)是空集。
  • 函数 f 是全函数(对所有输入都停机)。

赖斯定理的陈述

基于以上概念,我们现在可以陈述赖斯定理。

赖斯定理:任何关于图灵可计算函数的非平凡性质都是不可判定的。

更精确地说:对于任何非平凡的性质 P,集合 { | φ_M 具有性质 P } 是不可判定的。其中 是图灵机 M 的编码。

这意味着,不存在一个通用的算法(即总是停机的图灵机),能够通过检查任意一台图灵机的描述 ,来判定这台图灵机所计算的函数 φ_M 是否具有某个特定的非平凡性质 P


证明思路(以“函数是否全函数”为例)

赖斯定理的证明通常使用归约法。我们通过将一个已知的不可判定问题(例如停机问题)归约到判定性质 P 的问题,来证明后者也是不可判定的。

我们以判定“图灵机 M 计算的函数 φ_M 是否全函数”为例。这是一个非平凡性质(存在全函数,也存在非全函数)。

  1. 假设:假设存在一个判定器 H,对于输入 ,它能判定 φ_M 是否为全函数。
  2. 构造归约:我们将停机问题归约到这个问题。对于任意一个图灵机 M 和输入 w,我们构造一台新的图灵机 M’M’ 的行为如下:
    • 对于所有输入 xM’ 首先模拟 M 在输入 w 上的运行。
    • 如果 Mw 上停机,那么 M’ 将忽略 x,并输出某个固定值(例如 0)。
    • 如果 Mw 上永不停机,那么 M’ 也永不停机。
  3. 分析
    • 如果 Mw 上停机,那么 M’ 对所有输入 x 都停机(并输出0),即 φ_{M’} 是全函数。
    • 如果 Mw 上永不停机,那么 M’ 对所有输入 x 都永不停机,即 φ_{M’} 不是全函数。
  4. 得出结论:如果我们能用 H 判定 φ_{M’} 是否为全函数,我们就能判定 Mw 上是否停机。因为停机问题是不可判定的,所以这样的判定器 H 不可能存在。

这个构造方法可以推广到任何非平凡性质 P 上,从而证明赖斯定理。


赖斯定理的含义与影响

赖斯定理是理论计算机科学中一个非常强大且令人惊讶的结论。它意味着:

  • 对程序分析的深刻限制:我们无法编写一个万能的程序分析器,来判定任意程序是否具有诸如“是否会终止(停机)”、“是否输出某个特定值”、“功能是否等价于另一个程序”等非平凡行为特性。
  • 区分语法与语义:我们可以轻易地分析程序的语法(例如代码格式、变量名),但自动分析程序的语义(即程序实际要做什么)在根本上受到限制。
  • 与软件工程的联系:这解释了为什么自动化软件验证、测试用例生成、程序等价性证明等任务是极其困难的,并且在一般情况下是不可判定的。

总结

本节课中我们一起学习了赖斯定理。我们从图灵机和可计算函数出发,定义了问题的归约和非平凡性质。赖斯定理指出,任何关于图灵可计算函数的非平凡性质都是不可判定的。我们通过将停机问题归约到“判定函数是否为全函数”的例子,理解了该定理的证明思路。最后,我们探讨了这一定理对程序分析和软件工程带来的根本性限制。赖斯定理清晰地划定了计算机在自动推理关于程序行为问题方面的能力边界。

014:波斯特对应问题 (Post's Correspondence Problem) 🧩

在本节课中,我们将学习一个名为波斯特对应问题的计算理论核心问题。这个问题是理解计算可判定性边界的关键,它以一种看似简单的形式,揭示了某些计算问题的不可判定性。我们将从直观概念入手,逐步构建其形式化定义,并探讨其与图灵机停机问题的深刻联系。

概述与直观理解

上一节我们介绍了图灵机作为计算模型的基础。本节中,我们来看看一个由埃米尔·波斯特提出的经典问题。

波斯特对应问题是一个关于字符串匹配的决策问题。其核心思想可以借助“多米诺骨牌”来直观理解。

想象我们有一组多米诺骨牌,每张骨牌被一条线分成上下两部分,每一部分都写着一个由特定字母组成的字符串(单词)。例如,一张骨牌可能上面是“a”,下面是“aba”。

PCP实例 就是给定这样一个有限的多米诺骨牌集合。

问题 在于:能否从这组骨牌中(允许重复使用同一张骨牌)按某种顺序选取一个有限序列,使得当我们将这些骨牌按顺序排列时,所有骨牌上方的字符串连接起来形成的长字符串,与所有骨牌下方的字符串连接起来形成的长字符串完全相同

如果能找到这样的序列,我们称该PCP实例有解;否则无解。

形式化定义

以下是波斯特对应问题的精确定义。

一个PCP实例 P 是一个定义在字母表 Σ 上的有限“骨牌”对列表:
P = [(α₁, β₁), (α₂, β₂), ..., (αₖ, βₖ)]
其中,每个 αᵢβᵢ 都是 Σ 上的非空字符串。

一个 是一个非空的索引序列 i₁, i₂, ..., iₘm ≥ 1),满足:
αᵢ₁ αᵢ₂ ... αᵢₘ = βᵢ₁ βᵢ₂ ... βᵢₘ

波斯特对应问题 就是:给定一个PCP实例 P,判断它是否存在解。

示例说明

让我们通过一个具体例子来理解。

假设我们有一个字母表 Σ = {0, 1},并给出以下PCP实例:
P = [ (1, 101), (10, 00), (011, 11) ]

我们需要寻找一个索引序列,使得上下字符串相等。

  • 尝试序列 [1]:上方=1,下方=101,不相等。
  • 尝试序列 [1, 2]:上方=1 + 10 = 110,下方=101 + 00 = 10100,不相等。
  • 尝试序列 [2, 1, 3]:上方=10 + 1 + 011 = 101011,下方=00 + 101 + 11 = 0010111,不相等。

实际上,对于这个特定实例,我们可以找到解 [3, 2, 3, 1]

  • 上方:011 + 10 + 011 + 1 = 011100111
  • 下方:11 + 00 + 11 + 101 = 110011101

检查发现 011100111 不等于 110011101,所以这也不是解。这个例子可能无解,但说明了我们需要系统地寻找匹配。

一个更简单的有解例子是:P = [ (a, aaa), (ab, b), (bba, bb) ]。解可以是 [2, 1, 3]

  • 上方:ab + a + bba = ababba
  • 下方:b + aaa + bb = baaabb
    这并不相等。让我们构造一个有解的例子:P = [ (b, bab), (ab, b), (b, a) ]。解是 [1, 2]
  • 上方:b + ab = bab
  • 下方:bab + b = babb
    仍然不相等。关键在于找到精确匹配。经典的有解例子是:P = [ (1, 111), (10111, 10), (10, 0) ]。解是 [2, 1, 3]
  • 上方:10111 + 1 + 10 = 10111110
  • 下方:10 + 111 + 0 = 101110
    不匹配。可见,即使问题描述简单,寻找解也非易事。

与图灵机停机问题的联系

波斯特对应问题的核心重要性在于它的不可判定性。艾伦·图灵和埃米尔·波斯特等人的工作表明,不存在一个通用的算法,能够正确判定任意给定的PCP实例是否有解。

这个结论是通过归约证明的。证明思路是将另一个已知的不可判定问题——图灵机停机问题——归约到PCP上。

  1. 停机问题:给定一个图灵机 M 和它的输入字符串 w,判断 Mw 上是否会停机。这是一个不可判定的问题。
  2. 归约构造:对于任意一个图灵机 M 和输入 w,我们可以构造一个特定的PCP实例 P_{M,w}。这个构造过程是机械的、算法化的。
  3. 等价关系:可以证明,P_{M,w} 有解 当且仅当 图灵机 M 在输入 w 上会不停机(即进入无限循环)。
  4. 推出矛盾:如果存在一个算法 A 能判定任意PCP实例,那么我们可以用这个算法来判定停机问题(只需构造 P_{M,w} 并用 A 判断,然后取反结论即可)。因为停机问题不可判定,所以这样的算法 A 不可能存在。

因此,PCP本身也是不可判定的。

变体与相关概念

PCP有一些重要的变体和推广:

  • 有界PCP:限制解序列的长度 m 不超过某个给定的数 k。这个问题是可判定的,因为我们可以枚举所有长度不超过 k 的索引序列。
  • 修改的波斯特对应问题:要求解序列必须以第一张骨牌开始。即索引序列必须满足 i₁ = 1。这个变体同样是不可判定的。
  • 上下文无关文法的歧义性:PCP的不可判定性可以用来证明“判断一个给定的上下文无关文法是否歧义”也是一个不可判定的问题。这是通过将PCP归约到文法歧义性问题来实现的。

总结与应用

本节课中我们一起学习了波斯特对应问题。

我们从多米诺骨牌的直观类比开始,给出了PCP的严格形式化定义。通过示例,我们看到了问题本身的简洁性与寻找解的困难性。最重要的是,我们理解了PCP的不可判定性,这是通过将其与图灵机停机问题联系起来证明的。最后,我们简要介绍了PCP的一些变体及其在证明其他问题(如上下文无关文法的歧义性)不可判定性中的应用。

波斯特对应问题如同一面镜子,映照出可计算性理论的深度:一个表述极其初等的问题,其解答的界限却触及了计算的本质极限。它不仅是理论计算机科学中的一个里程碑,也是连接抽象计算理论与形式语言问题的重要桥梁。

015:图灵完备性与等价性

概述

在本节课中,我们将学习图灵完备性等价性的核心概念。我们将探讨不同的计算模型(如图灵机、编程语言)如何描述相同的计算能力,并理解它们之间相互模拟的原理。课程将使用简单的语言和示例,帮助你掌握这些抽象但基础的理论。


图灵完备性的核心概念 🧠

上一节我们概述了课程目标,本节中我们来看看什么是图灵完备性。

一个计算模型(例如一种编程语言或一种抽象机器)被称为是图灵完备的,如果它能模拟一台通用图灵机的计算能力。这意味着,该模型可以计算任何图灵机可计算的函数。

用公式描述,对于一个模型 M,如果存在一个编译/解释函数 C,使得对于任何图灵机程序 P_TM 和输入 x,都有:
M(C(P_TM), x) = P_TM(x)
那么模型 M 就是图灵完备的。其中,M(program, input) 表示在模型 M 下执行程序 program 并输入 input 的结果。


计算模型的等价性 🔄

理解了图灵完备性后,我们自然会问:不同的模型之间如何比较?这就是等价性要解决的问题。

两个计算模型 AB计算等价的,如果满足以下两个条件:

  1. 模型 A 可以模拟模型 B 的任何计算。
  2. 模型 B 也可以模拟模型 A 的任何计算。

这意味着,在模型 A 中编写的任何程序,都可以在模型 B 中找到功能完全相同的对应程序,反之亦然。图灵机、带有 while 循环的简单语言、带有 goto 语句的语言,都被证明是彼此计算等价的。


一个简单的编程模型:WHILE 语言 💻

为了具体说明,我们引入一个极简的编程模型,称为 WHILE 语言。我们将看到,即使是这样一个简单的语言,也是图灵完备的。

WHILE 语言的语法非常简单,只包含以下几种元素:

  • 变量:如 x, y, z,用于存储整数值。
  • 常量:如 0, 1, 2 等整数。
  • 算术表达式:使用 +(加)和 -(减)运算符。
  • 赋值语句变量 := 表达式
  • 循环语句while 变量 != 0 do 程序序列 end
  • 语句序列:多个语句按顺序排列。

以下是一个 WHILE 语言程序的例子,它将变量 y 的值加到变量 x 上:

while y != 0 do
    x := x + 1;
    y := y - 1
end


WHILE 语言的语义

定义了语法(程序长什么样)之后,我们需要定义语义(程序如何运行)。

程序的状态由所有变量的当前值定义。执行过程就是状态根据语句规则逐步改变的过程:

  • 赋值 x := E:计算表达式 E 的值,然后将结果存入变量 x
  • 循环 while X != 0 do P end:检查变量 X 的值。
    • 如果 X ≠ 0,则执行循环体 P,然后再次检查 X
    • 如果 X = 0,则跳过整个循环,执行后面的语句。

这个模型足够简单,我们可以精确推理其行为。同时,它也足够强大,可以表达复杂的计算。


语法糖:让语言更易用 🍬

WHILE 语言虽然完备,但写起来很繁琐。在实际编程中,我们会添加“语法糖”——它们不增加语言的计算能力,只是让编程更方便。

例如,if-then-else 条件语句并不是 WHILE 语言的原生结构,但我们可以用现有的 while 循环来模拟它。

以下是使用 WHILE 语言模拟 if X != 0 then P else Q end 的一种方法:

// 假设我们有一个临时变量 ‘flag’,初始值为1
flag := 1;
while X != 0 do
    // 执行 then 部分 P
    P;
    // 将 X 和 flag 都设为 0,以退出循环并跳过 else 部分
    X := 0;
    flag := 0
end;
while flag != 0 do
    // 执行 else 部分 Q
    Q;
    flag := 0
end

通过类似的方式,我们可以定义出 for 循环、switch-case 语句等。关键在于,这些“糖”都可以被“脱糖”为只包含基本语句(赋值和 while 循环)的程序。


GOTO 语言:另一个等价模型 ⚙️

现在,让我们看看另一个经典模型:GOTO 语言。它使用行号和 goto 语句来控制流程,在计算能力上与 WHILE 语言等价。

一个 GOTO 语言程序由一系列带行号的语句组成:

  • 赋值L: X := E
  • 条件跳转L: if X != 0 then goto L1 else goto L2
  • 停机L: halt

以下是一个用 GOTO 语言实现循环的例子,它将 y 加到 x

1: if y == 0 then goto 4 else goto 2
2: x := x + 1
3: y := y - 1
4: goto 1
5: halt


WHILE 与 GOTO 的相互模拟

WHILE 语言和 GOTO 语言可以相互模拟,这证明了它们的等价性。

  • 用 GOTO 模拟 WHILE:一个 while X != 0 do P end 循环可以很容易地用 ifgoto 语句实现,如上例所示。
  • 用 WHILE 模拟 GOTO:这稍微复杂一些。我们需要用一个变量(称为“程序计数器” PC)来存储当前的行号。程序主体是一个大的 while 循环,内部用一个 if-elseif 链(可以用多个 while 模拟)来检查 PC 的值,并执行对应行的语句,然后更新 PC 的值(模拟 goto)。

这种相互模拟的能力,正是“计算等价性”的核心。


连接到图灵机 🤖

我们讨论了 WHILE 语言和 GOTO 语言的等价性。那么,它们和图灵机有什么关系呢?

关键结论是:WHILE 语言(以及等价的 GOTO 语言)是图灵完备的。这意味着:

  1. 任何图灵机程序都可以用 WHILE 语言编写。我们可以用 WHILE 语言的变量来模拟图灵机的无限长纸带、读写头和状态转移规则。
  2. 任何 WHILE 语言程序都可以由图灵机执行。我们可以设计一个图灵机,它的纸带上编码着 WHILE 程序的源代码和当前变量状态,然后通过一系列状态转移来模拟每一步赋值和循环检查。

因此,WHILE 语言、GOTO 语言和图灵机构成了一个等价模型族。它们在计算能力上没有区别,区别只在于描述的方便程度和底层机制。


现实世界的编程语言 🌐

最后,让我们把理论联系实际。现代编程语言如 Python、Java、C++ 显然比 WHILE 语言复杂得多。它们有丰富的数据结构、库函数和编程范式。

然而,在计算能力上,它们都与 WHILE 语言等价,因此也是图灵完备的。那些额外的特性(如对象、高阶函数、列表推导式)都可以被看作是“语法糖”或对基本模型的“高效实现”。一个 Python 解释器或 Java 虚拟机,在理论上都可以被一个(非常复杂的)WHILE 程序或一台(非常庞大的)图灵机所模拟。

理解这一点,有助于我们穿透纷繁复杂的语法,看到计算本质的统一性。


总结

本节课中我们一起学习了:

  1. 图灵完备性:一个模型能模拟通用图灵机,即具有最强的计算能力。
  2. 计算等价性:不同模型(如图灵机、WHILE语言、GOTO语言)可以相互模拟,计算能力相同。
  3. 简单模型的力量:即使像 WHILE 语言这样只有变量、赋值和循环的极小语言,也是图灵完备的。
  4. 语法糖的作用if-then-elsefor 循环等结构是方便编程的“糖”,不改变本质计算能力。
  5. 理论与实际的联系:所有现代通用编程语言都是图灵完备的,它们在计算本质上与这些抽象模型相通。

掌握这些概念,是理解“计算”本身界限和可能性的第一步。

016:递归函数

在本节课中,我们将学习递归函数的核心概念,特别是原始递归函数和μ递归函数。我们将看到这些函数如何与图灵机等价,并构成可计算性理论的基础。课程还将简要介绍λ演算,作为另一种计算模型。

从图灵机到递归函数

上一节我们介绍了图灵机作为计算模型。本节中我们来看看另一种等价的描述方式:递归函数。

图灵机通过操作纸带上的符号进行计算。然而,存在一种完全不同的方式来形式化“可计算”的概念:通过定义一系列从自然数到自然数的函数。这些函数可以通过一些基本规则来构造,其中最核心的就是递归

原始递归函数

原始递归函数是一类可以通过特定规则从简单基础函数构造出来的函数。以下是其定义的核心组成部分:

1. 基本函数
首先,我们从三个极其简单的函数开始:

  • 零函数Z(n) = 0。对于任何输入,输出恒为0。
  • 后继函数S(n) = n + 1。将输入加1。
  • 投影函数P_i^k(x1, x2, ..., xk) = xi。从k个参数中选出第i个。

2. 复合
如果已有函数 gh1, h2, ..., hm,可以通过复合得到新函数 f
f(x1, ..., xn) = g(h1(x1, ..., xn), ..., hm(x1, ..., xn))
这相当于将多个函数的输出作为另一个函数的输入。

3. 原始递归
这是构造新函数的核心机制。给定函数 gh,可以定义一个新函数 f

  • f(0, x1, ..., xk) = g(x1, ..., xk)
  • f(S(n), x1, ..., xk) = h(n, f(n, x1, ..., xk), x1, ..., xk)
    这个定义是递归的:要计算 f(n+1, ...),你需要知道 f(n, ...) 的值。这类似于编程中的循环。

所有能从基本函数出发,通过有限次应用复合与原始递归规则得到的函数,称为原始递归函数

原始递归函数的例子
许多常见的算术函数都是原始递归的:

  • 加法add(0, y) = yadd(S(x), y) = S(add(x, y))
  • 乘法mult(0, y) = 0mult(S(x), y) = add(mult(x, y), y)
  • 阶乘fact(0) = 1fact(S(n)) = mult(S(n), fact(n))
  • 指数运算前驱函数等也都是原始递归的。

然而,原始递归函数的集合虽然庞大,但并非包含所有“直观上可计算”的函数。它的计算能力存在限制。

μ递归函数与可计算性

为了捕捉更广泛的“可计算”概念,我们需要引入一个新的算子:μ算子(极小化算子)。

μ算子定义
给定一个函数 g(y, x1, ..., xn),μ算子定义一个新函数 f
f(x1, ..., xn) = μy[g(y, x1, ..., xn) = 0]
其含义是:寻找最小的自然数 y,使得 g(y, x1, ..., xn) = 0。如果这样的 y 不存在,则该函数在该输入处无定义。

μ递归函数
如果一个函数可以从基本函数出发,通过有限次应用复合原始递归μ算子得到,则它被称为μ递归函数(或部分递归函数)。

关键定理:图灵可计算 ⇔ μ递归
这是可计算性理论的一个核心结论:

  • 每个图灵可计算的函数都是μ递归的。
  • 每个μ递归函数都是图灵可计算的。
    这意味着,图灵机的计算能力与μ递归函数的定义能力是完全等价的。这强有力地支持了“丘奇-图灵论题”:任何“算法可计算”的函数,都可以用图灵机计算,也都可以用μ递归函数来定义。

阿克曼函数
阿克曼函数是一个经典的例子,它不是原始递归的,但它是μ递归的(因此是图灵可计算的)。它的定义涉及双重递归,增长极其迅速,超越了任何原始递归函数的增长速率。其定义如下:

  • A(0, n) = n + 1
  • A(m+1, 0) = A(m, 1)
  • A(m+1, n+1) = A(m, A(m+1, n))
    阿克曼函数的存在表明,μ递归函数类严格大于原始递归函数类。

λ演算:另一种计算模型

除了递归函数,还有另一种重要的计算模型:λ演算。它由阿隆佐·邱奇发明,是函数式编程语言的理论基础。

λ演算的核心概念
λ演算只关心三件事:

  1. 变量:如 x, y
  2. 抽象λx.M。这表示一个函数,参数是 x,函数体是 M。相当于 def f(x): return M
  3. 应用(M N)。表示将函数 M 应用于参数 N

β归约
λ演算的核心计算规则是 β归约
(λx.M) N → M[x := N]
意思是,将函数 (λx.M) 应用到参数 N 上,结果是将函数体 M 中所有自由出现的 x 替换为 N。这就像函数调用。

邱奇-图灵论题与λ演算
邱奇同样提出了他的论题:任何“有效可计算”的函数都可以用λ演算定义。随后,图灵证明了λ可定义函数 ⇔ 图灵可计算函数。这共同奠定了“丘奇-图灵论题”的坚实基础。

在λ演算中编码
有趣的是,我们可以在λ演算中编码自然数、布尔值甚至递归:

  • 邱奇数:数字 n 被编码为高阶函数 λf.λx. (f (f ... (f x)...))f 应用 n 次)。
  • 递归:通过 Y组合子 Y = λf.(λx.f (x x)) (λx.f (x x)) 可以实现匿名函数的递归调用。

λ演算表明,计算可以纯粹通过函数的抽象和应用来完成,无需任何状态或循环的概念。

总结

本节课中我们一起学习了:

  1. 原始递归函数:通过零函数、后继函数、投影函数,经复合和原始递归规则构造的函数类。它能定义许多常见算术函数。
  2. μ递归函数:在原始递归基础上加入μ算子(极小化)得到的函数类。它与图灵可计算函数完全等价,这为丘奇-图灵论题提供了关键证据。
  3. 阿克曼函数作为非原始递归但μ递归(即可计算)的典型例子。
  4. λ演算作为另一种等价的计算模型,它纯粹基于函数抽象和应用,并通过β归约进行计算,是函数式编程的基石。

这些模型共同描绘了“可计算性”的数学边界,告诉我们什么是算法本质上可以解决的,什么是不可以的。从图灵机到递归函数,再到λ演算,它们从不同角度揭示了计算的本质。

017:计算复杂度

在本节课中,我们将学习计算复杂度的核心概念,特别是如何用大O符号(Big O notation)来描述算法的运行时间和空间消耗。我们将从图灵机模型出发,理解复杂度分析的基本思想,并介绍复杂度类P和PSPACE。

从图灵机到复杂度

上一节我们介绍了图灵机作为计算模型的基础。本节中,我们来看看如何基于这个模型来定义和衡量计算的“复杂度”。

计算复杂度理论研究算法解决问题所需的资源,主要是时间空间。我们使用图灵机作为衡量这些资源的抽象模型。一台图灵机解决某个问题所花费的时间(步数)或占用的空间(带子格子数),就定义了该问题的复杂度。

大O符号:描述函数增长

为了描述复杂度随输入规模增长的趋势,我们使用大O符号。它刻画了函数在渐进意义上的上界。

以下是其核心定义:

  • 大O (O): 我们说一个函数 f(n)O(g(n)),如果存在正常数 cn0,使得对于所有 n > n0,都有 f(n) ≤ c * g(n)。这表示 f(n) 的增长速度不快于 g(n)

    • 公式:f(n) = O(g(n)) ⇔ ∃ c>0, n0>0, ∀ n>n0: f(n) ≤ c·g(n)
  • 大Ω (Ω): 我们说 f(n)Ω(g(n)),如果存在正常数 cn0,使得对于所有 n > n0,都有 f(n) ≥ c * g(n)。这表示 f(n) 的增长速度不慢于 g(n)

    • 公式:f(n) = Ω(g(n)) ⇔ ∃ c>0, n0>0, ∀ n>n0: f(n) ≥ c·g(n)
  • 大Θ (Θ): 如果 f(n) 同时是 O(g(n))Ω(g(n)),那么我们说 f(n)Θ(g(n))。这表示 f(n)g(n) 的增长速度同阶

    • 公式:f(n) = Θ(g(n)) ⇔ f(n) = O(g(n))f(n) = Ω(g(n))

这些定义可以用极限的概念来等价描述。例如,f(n) = O(g(n)) 也意味着当 n 趋于无穷时,f(n)/g(n) 的极限上界是一个常数。

复杂度度量与图灵机变体

理解了增长阶的表示方法后,我们具体定义图灵机的复杂度。

对于一台图灵机 M 和输入 x

  • 时间复杂度 Time_M(x)M 在输入 x 上停机前所执行的步数
  • 空间复杂度 Space_M(x)M 在计算过程中使用的带子格子总数

我们通常更关心算法在最坏情况下的表现。因此,对于一个解决语言 L 的图灵机 M,我们定义:

  • 时间复杂度函数 T_M(n) = max{ Time_M(x) : |x| = n },即对所有长度为 n 的输入,M 所需的最大运行时间。
  • 空间复杂度函数 S_M(n) = max{ Space_M(x) : |x| = n },即对所有长度为 n 的输入,M 所需的最大空间。

一个关键问题是,图灵机有不同的变体(如单带、多带、非确定性),这会影响复杂度吗?理论表明,这些变体之间可以在多项式时间内相互模拟。这意味着,当我们用大O符号讨论复杂度类(如多项式时间)时,这些具体的机器模型选择通常不影响问题的归属。

复杂度类:P 与 PSPACE

基于上述度量,我们可以定义重要的复杂度类,它们将问题按照所需资源进行分类。

以下是两个最基础的复杂度类:

  • P (Polynomial Time): 由所有能被确定性图灵机多项式时间内判定的语言组成。

    • 形式化定义:P = ∪_{k≥0} DTIME(n^k)
    • 直观理解:P类包含了那些存在“高效”算法的问题,其运行时间随输入规模的增长是可控的多项式级(如 n^2, n^3)。
  • PSPACE: 由所有能被确定性图灵机多项式空间内判定的语言组成。

    • 形式化定义:PSPACE = ∪_{k≥0} DSPACE(n^k)
    • 直观理解:PSPACE类关注的是内存使用效率。一个问题即使需要很长时间,但只要它使用的内存(空间)不超过输入规模的多项式倍,就属于PSPACE。

显然,任何在多项式时间内完成的计算,其空间消耗也不会超过多项式时间(因为每一步最多访问一个新格子),所以我们有 P ⊆ PSPACE。但是,PSPACE 中的问题是否都能在多项式时间内解决?这是计算复杂性理论中一个著名的未解之谜(P vs PSPACE 问题)。

时间与空间的权衡

时间和空间资源之间常常存在权衡。一个使用较少空间的算法可能需要更多时间,反之亦然。

萨维奇定理描述了这种关系的一个基本界限:任何在空间 S(n) 内运行(且 S(n) ≥ log n)的确定性图灵机,都可以在至多 2^(O(S(n))) 的时间内被模拟。这意味着:

  • PSPACE 包含于 EXPTIME(指数时间)中。
  • 更一般地,DSPACE(S(n)) ⊆ DTIME(2^(O(S(n))))

这个定理表明,多项式空间的计算,其最坏情况下的时间可能是指数级的。这直观地说明了为什么 PSPACE 类可能比 P 类更大。

总结

本节课中我们一起学习了计算复杂度的基础。我们从图灵机模型出发,引入了用于描述算法资源消耗增长趋势的大O、大Ω和大Θ符号。接着,我们定义了图灵机的时间复杂度和空间复杂度,并解释了为什么在讨论多项式级复杂度时,不同的图灵机变体本质上是等价的。最后,我们介绍了两个核心的复杂度类:P(多项式时间可解的问题)和PSPACE(多项式空间可解的问题),并了解了连接时间与空间资源的萨维奇定理。这些概念为我们理解不同计算问题的内在难度和分类奠定了基础。

018:核心概念与复杂性类

在本节课中,我们将学习计算复杂性理论中的核心概念,特别是确定性图灵机与非确定性图灵机,以及由此定义的P类和NP类。我们将探讨这些复杂性类的基本定义、它们之间的关系,并通过具体问题(如上下文无关文法成员资格问题和团问题)来加深理解。

图灵机与复杂性度量

上一节我们介绍了计算模型的基础。本节中我们来看看如何用图灵机来度量计算的复杂性。

我们使用图灵机作为计算模型。对于一台图灵机M,我们关注其时间复杂性空间复杂性

  • 时间复杂性 Time_M(n):定义为图灵机M在所有长度为n的输入上运行所需的最大步数。
  • 空间复杂性 Space_M(n):定义为图灵机M在所有长度为n的输入上运行时所访问的磁带单元的最大数量。

我们通常关注最坏情况下的复杂性。对于一个函数 f(n),我们可以定义复杂性类。

以下是基于时间复杂性的类:

  • DTIME(f(n)):存在一台确定性图灵机D,使得对于所有长度为n的输入,D在 O(f(n)) 时间内停机并给出正确答案的所有语言(问题)的集合。
  • NTIME(f(n)):存在一台非确定性图灵机N,使得对于所有长度为n的输入,N在 O(f(n)) 时间内停机并给出正确答案的所有语言的集合。

类似地,可以定义基于空间复杂性的类 DSPACE(f(n))NSPACE(f(n))

多项式时间与P类

上一节我们定义了基于特定函数的复杂性类。本节中我们来看看一个特别重要的类:多项式时间。

我们特别关心那些运行时间可以被输入规模的多项式函数所限定的问题。这引出了P类的定义。

P类(Polynomial time)是所有可以在确定性图灵机上以多项式时间解决的判定性问题的集合。形式化定义为:
P = ∪_{k∈ℕ} DTIME(n^k)

这意味着,一个问题属于P类,当且仅当存在一个常数k和一台确定性图灵机,使得对于任何长度为n的输入,该图灵机都能在 O(n^k) 步内给出正确答案。

上下文无关文法成员资格问题属于P

为了理解P类,我们来看一个具体例子:上下文无关文法(CFG)的成员资格问题。

问题:给定一个上下文无关文法G和一个字符串w,判断w是否可由G生成(即 w ∈ L(G)?)。
这是一个经典的判定性问题。我们将展示它属于P类。

核心算法是CYK算法(Cocke–Younger–Kasami算法)。该算法使用动态规划。

假设文法G是乔姆斯基范式(CNF)的。设字符串 w = a1 a2 ... an
我们定义一个二维布尔数组 P[i][j],其含义为:从第i个字符开始的长度为j的子串是否可由文法G生成。
以下是算法的高层描述:

  1. 初始化:对于每个终结符(单个字符)ai,设置 P[i][1] = true 当且仅当存在产生式 A -> ai
  2. 动态规划递推:对于长度 len 从2到n,对于起始位置 i 从1到 n-len+1
    • 将子串 w[i:i+len] 在位置 k (1 <= k < len) 处拆分为两部分。
    • 检查是否存在非终结符A,以及产生式 A -> B C,使得 P[i][k] 对应B生成左半部分,且 P[i+k][len-k] 对应C生成右半部分。
    • 如果存在这样的产生式,则设置 P[i][len] = true
  3. 结果w ∈ L(G) 当且仅当 P[1][n] = true

该算法包含三层嵌套循环,时间复杂度为 O(n^3)。由于n是输入规模(字符串长度),这是一个多项式时间算法。因此,上下文无关文法的成员资格问题属于P类。

非确定性多项式时间与NP类

上一节我们看到了一个可在多项式时间内验证解的问题。本节中我们正式定义NP类。

NP类(Nondeterministic Polynomial time)的定义有两种等价方式:

  1. 所有可以在非确定性图灵机上以多项式时间解决的判定性问题的集合。即 NP = ∪_{k∈ℕ} NTIME(n^k)
  2. 所有其解可以在确定性图灵机上以多项式时间验证的判定性问题的集合。

第二种定义更直观。一个问题属于NP,如果对于每个答案为“是”的实例,都存在一个简短的“证书”(或证明),使得我们可以用一台确定性图灵机在多项式时间内验证该证书的有效性。

团问题属于NP

让我们通过一个经典问题——团问题(Clique Problem)来理解NP类。

问题:给定一个无向图G和一个整数k,判断图G中是否存在一个大小至少为k的团(即一个完全子图,其中每对顶点之间都有边相连)。

首先,团问题显然属于NP。原因如下:

  • 证书:如果答案为“是”,一个大小为k的顶点子集S(即团)就是一个证书。
  • 验证:验证算法(确定性图灵机)接收图G、整数k和候选证书S后,需要做两件事:
    1. 检查S是否恰好包含k个顶点。
    2. 检查S中每一对不同的顶点之间在图G中是否都有边相连。
  • 时间分析:第一步是 O(k),第二步需要检查 C(k, 2) = O(k^2) 对顶点。由于k最大为图的顶点数n,验证总时间是 O(n^2),是输入规模的多项式时间。

因此,根据定义2,团问题属于NP。

值得注意的是,我们目前不知道是否存在一个多项式时间的确定性算法来解决团问题(即不知道它是否属于P)。寻找这样的算法或证明其不存在,是P与NP问题的核心。

P与NP的关系及验证的本质

上一节我们通过例子理解了NP的定义。本节中我们来探讨P与NP的关系以及验证过程的本质。

显然,任何属于P类的问题也属于NP类(P ⊆ NP)。因为如果一个确定性图灵机能在多项式时间内直接解决问题,那么它当然也可以忽略证书,直接运行该算法来验证答案——证书甚至可以为空。

NP类的核心在于“验证”比“求解”可能更容易。对于NP问题,找到一个解可能非常困难(需要指数时间尝试所有可能性),但一旦有人给了你一个候选解(证书),你可以在多项式时间内检查它是否正确。

验证过程的关键在于:

  1. 证书的长度必须是输入规模的多项式。
  2. 验证算法本身必须是确定性的,且运行时间是输入规模(包括证书)的多项式。

总结

本节课中我们一起学习了计算复杂性理论的核心基础。

  • 我们首先回顾了用图灵机度量时间与空间复杂性的方法。
  • 然后定义了P类,即所有能被确定性图灵机在多项式时间内解决的判定性问题。我们以上下文无关文法成员资格问题为例,通过 O(n^3) 的CYK算法展示了其属于P类。
  • 接着,我们定义了NP类,即所有解能在多项式时间内被验证的判定性问题。我们以团问题为例,说明了如何通过验证一个大小为k的顶点子集来证明其属于NP。
  • 最后,我们讨论了 P ⊆ NP 的关系,并强调了NP类中“验证”与“求解”的可能差异。P是否等于NP,是理论计算机科学中悬而未决的核心问题。

019:P 不等于 NP?

在本节课中,我们将探讨理论计算机科学中一个核心且悬而未决的问题:P 与 NP 的关系。我们将介绍复杂性类 P 和 NP 的定义,解释“P 是否等于 NP”这个问题的意义,并讨论相关的概念,如时间与空间复杂性、确定性与非确定性图灵机,以及相对化证明。

概述

P 与 NP 问题是克雷数学研究所公布的七个“千禧年大奖难题”之一,解决者将获得一百万美元的奖金。这个问题探讨的是,那些其解可以快速验证的问题(NP),是否也能被快速求解(P)。理解这个问题是理解计算复杂性理论的基础。

P 与 NP 的定义

上一节我们概述了 P 与 NP 问题的背景,本节中我们来看看这两个复杂性类的精确定义。

P 是确定性图灵机在多项式时间内可判定的所有决策问题的集合。用公式表示,即:
P = { L | 存在一个确定性图灵机 M 和一个多项式 p(n),使得 M 在输入长度为 n 时,在 O(p(n)) 步内判定 L }

NP 是非确定性图灵机在多项式时间内可判定的所有决策问题的集合。这意味着,对于 NP 中的问题,如果给定一个“证书”(例如一个潜在的解),我们可以在多项式时间内验证这个证书的正确性。用公式表示,即:
NP = { L | 存在一个非确定性图灵机 M 和一个多项式 p(n),使得 M 在输入长度为 n 时,在 O(p(n)) 步内接受 L }

一个经典的 NP 问题是布尔可满足性问题(SAT):给定一个布尔公式,是否存在一组变量赋值使其为真?虽然寻找解可能很难,但验证一个给定的赋值是否满足公式是简单的。

时间与空间复杂性

理解了 P 和 NP 的核心定义后,我们需要更广泛地了解衡量计算效率的尺度:时间复杂性和空间复杂性。

时间复杂性关注的是图灵机解决问题所需的步骤数。空间复杂性关注的是图灵机解决问题所需的存储单元(磁带格子)数。

以下是主要复杂性类的关系:

  • L:确定性图灵机在对数空间内可判定的问题类。
  • P:确定性图灵机在多项式时间内可判定的问题类。
  • NP:非确定性图灵机在多项式时间内可判定的问题类。
  • PSPACE:确定性图灵机在多项式空间内可判定的问题类。
  • EXPTIME:确定性图灵机在指数时间内可判定的问题类。

已知的包含关系是:L ⊆ P ⊆ NP ⊆ PSPACE ⊆ EXPTIME。并且已知 P ≠ EXPTIME。P 与 NP 是否相等,就位于这个包含链的中间。

确定性、非确定性与指数爆炸

我们介绍了复杂性类,现在来深入看看非确定性计算的一个关键特性,以及它如何与确定性计算相关联。

非确定性图灵机可以同时“猜测”多条计算路径。对于 NP 问题,如果至少存在一条接受路径,机器就接受输入。一个简单的模拟方法是使用确定性图灵机尝试所有可能的猜测(例如,所有可能的证书)。然而,如果分支因子是常数,这种穷举搜索会导致计算时间指数级增长。

例如,对于一个运行时间为 O(n^k) 的非确定性图灵机,用确定性图灵机模拟它可能需要 O(2(nk)) 的时间。这解释了为什么虽然 NP ⊆ EXPTIME,但我们不知道 NP 是否真的等于 EXPTIME,或者它是否更小(比如等于 P)。

对角化技术与相对化障碍

既然我们看到了模拟非确定性计算可能带来指数开销,一个自然的问题是:能否证明 P ≠ NP?历史上,研究者们使用了称为“对角化”的技术来证明某些复杂性类的分离(例如 P ≠ EXPTIME)。

对角化的核心思想是构造一个语言,使得任何运行时间较短的图灵机都无法正确判定它,但一个运行时间较长的图灵机可以。这类似于康托尔证明实数不可数的方法。

然而,Baker、Gill 和 Solovay 在 1975 年证明,仅使用对角化技术(以及“相对化”技术,即允许图灵机访问一个“预言机”)无法解决 P 与 NP 问题。他们构造了预言机 A 和 B,使得:

  • P^A = NP^A (在预言机 A 存在下,P 等于 NP)
  • P^B ≠ NP^B (在预言机 B 存在下,P 不等于 NP)

这意味着任何证明如果只依赖于图灵机模拟和对角化(即能够“相对化”),则既不能证明 P = NP,也不能证明 P ≠ NP。要解决 P 与 NP 问题,需要不能相对化的新思想。

问题的现状与意义

我们讨论了证明 P ≠ NP 的技术障碍,现在来看看这个问题的现状及其实际意义。

P 与 NP 问题仍然是开放的。它不仅仅是理论上的好奇,而且有深刻的实际影响。如果 P = NP,那么许多目前被认为困难的问题(如旅行商问题、集成电路设计、蛋白质折叠等)都将存在高效算法,这将对科学、工程和工业产生革命性影响。

目前,普遍相信 P ≠ NP。这意味着存在本质上就难以计算但易于验证的问题。这种信念基于多年的研究,以及许多 NP 问题尽管经过巨大努力仍未找到多项式时间算法的事实。

研究者们正在从多个方向逼近这个问题,包括电路复杂性、描述复杂性、证明复杂性等。例如,电路复杂性试图证明 NP 问题需要超多项式大小的电路,如果成功,则意味着 P ≠ NP。

总结

本节课中我们一起学习了 P 与 NP 问题的核心内容。我们定义了复杂性类 P 和 NP,理解了时间与空间复杂性的层次结构,探讨了非确定性计算与指数爆炸的关系,并了解了为什么传统的对角化技术受到相对化障碍的限制而无法解决这个问题。最后,我们审视了该问题的现状及其对计算理论和现实世界的深远意义。尽管答案尚未知晓,但对这个问题的探索持续推动着理论计算机科学的发展。

020:NP完全性

在本节课中,我们将学习计算复杂性理论中的一个核心概念:NP完全性。我们将探讨P类与NP类的定义,理解什么是多项式时间归约,并最终学习如何证明一个问题是NP完全的。课程的核心是库克-列文定理,它首次证明了布尔可满足性问题(SAT)是NP完全的。

P类与NP类

上一节我们介绍了计算问题的基本概念。本节中,我们来看看两个最重要的复杂性类:P和NP。

  • P类:指所有能在多项式时间内被确定性图灵机解决的问题的集合。公式化表示为:存在一个确定性图灵机M和一个多项式函数p(n),使得对于任意长度为n的输入,M都能在p(n)步内停机并给出正确答案。
  • NP类:指所有能在多项式时间内被非确定性图灵机解决的问题的集合。等价地,一个问题属于NP,如果对于每个答案为“是”的实例,都存在一个多项式长度的“证书”,使得一个确定性图灵机可以在多项式时间内验证该证书的正确性。

P类问题通常被认为是“容易”或“可高效解决”的问题。NP类则包含了许多我们尚未找到高效算法,但一旦给出一个潜在解(证书),我们却能快速验证其正确性的问题。一个核心的未解问题是:P是否等于NP?

多项式时间归约

为了比较不同问题的难度,我们使用“归约”的概念。如果我们可以将问题A的任何一个实例,转化为问题B的一个实例,并且这个转化过程本身是高效的,那么我们就说问题A可以归约到问题B。

以下是多项式时间归约的精确定义:
存在一个函数f,它可以在多项式时间内被确定性图灵机计算,并且满足:对于任意输入x,x是问题A的“是”实例 当且仅当 f(x) 是问题B的“是”实例。

我们用符号 A ≤ₚ B 表示“问题A可以在多项式时间内归约到问题B”。这意味着,如果B是容易的(即在P中),那么A也是容易的。反之,如果A是困难的,那么B也是困难的。

NP完全问题与库克-列文定理

基于归约的概念,我们可以定义NP中最难的一类问题:NP完全问题。

一个问题是NP完全的,需要满足两个条件:

  1. 它本身属于NP类。
  2. NP中的每一个问题都可以在多项式时间内归约到它。

这意味着,如果你找到了一个NP完全问题的多项式时间算法,那么NP中的所有问题就都找到了多项式时间算法,即P=NP。反之,如果证明了任何一个NP完全问题不存在多项式时间算法,那么P≠NP。

那么,第一个被证明是NP完全的问题是什么呢?这就是库克-列文定理的核心内容。

库克-列文定理:布尔可满足性问题(SAT)是NP完全的。

布尔可满足性问题

在证明库克-列文定理之前,我们需要精确定义什么是布尔可满足性问题。

  • 布尔变量:取值为真或假的变量。
  • 文字:一个布尔变量或其否定。
  • 子句:多个文字的析取。例如:(x₁ ∨ ¬x₂ ∨ x₃)。
  • 合取范式:多个子句的合取。例如:(x₁ ∨ ¬x₂) ∧ (¬x₁ ∨ x₃) ∧ (x₂ ∨ ¬x₃)。
  • SAT问题:给定一个布尔公式(通常特指合取范式CNF),问是否存在一组对变量的真值赋值,使得整个公式的值为真。

SAT问题显然是NP的:给定一个公式和一组赋值,我们可以在多项式时间内验证每个子句是否被满足。

库克-列文定理证明思路

证明SAT是NP完全的关键在于证明第二部分:NP中的任何问题L都可以多项式归约到SAT

证明思路如下:

  1. 因为L在NP中,所以存在一个非确定性图灵机M和一个多项式p(n),使得M能在p(n)步内验证L的证书。
  2. 对于L的任何一个输入x,我们构造一个布尔公式Φ,它编码了图灵机M在输入(x, 证书)上运行p(|x|)步的计算过程。
  3. 这个公式Φ是可满足的,当且仅当存在一个证书使得M接受(x, 证书),即当且仅当x属于L。
  4. 我们需要证明,从x构造Φ的过程可以在关于|x|的多项式时间内完成。

这个构造非常巧妙,它利用布尔变量来表示:

  • 在计算的第i步,读写头的位置。
  • 在计算的第i步,图灵机所处的状态。
  • 在计算的第i步,纸带每个格子上的符号。

然后,我们利用子句来编码图灵机运行的规则(转移函数)、初始配置、接受配置,以及每一步配置到下一步配置的合法转移。最终,所有这些子句合取起来,就构成了公式Φ。

从SAT到3-SAT

库克-列文定理证明了最一般的SAT是NP完全的。但在实践中,我们经常使用一个更受限但同样NP完全的版本:3-SAT。

  • k-SAT:每个子句恰好包含k个文字的CNF公式的可满足性问题。
  • 3-SAT:每个子句恰好包含3个文字的CNF公式的可满足性问题。

可以证明,SAT可以多项式归约到3-SAT。归约方法是将一个超过3个文字的子句,通过引入新的辅助变量,等价地转化为多个长度为3的子句的合取。例如,子句 (a ∨ b ∨ c ∨ d) 可以转化为 (a ∨ b ∨ z) ∧ (¬z ∨ c ∨ d)。因此,3-SAT也是NP完全的。

其他NP完全问题示例

一旦我们有了第一个NP完全问题(SAT),要证明一个新问题B是NP完全的,就只需要做两件事:

  1. 证明B属于NP。
  2. 证明一个已知的NP完全问题A(如SAT或3-SAT)可以多项式归约到B。

通过这种“归约链”,人们已经发现了成千上万个NP完全问题。以下是一些经典示例及其与3-SAT的归约关系:

  • 团问题:给定图G和整数k,问G中是否存在一个大小为k的团(即一个每对顶点都相连的子图)。可以从3-SAT归约。
  • 顶点覆盖问题:给定图G和整数k,问是否存在一个不超过k个顶点的集合,使得图中每条边都至少有一个端点在该集合中。可以从团问题归约。
  • 哈密顿回路问题:给定图G,问是否存在一个经过每个顶点恰好一次并回到起点的回路。可以从顶点覆盖问题归约。
  • 旅行商问题:给定城市列表和城市间的距离,问是否存在一条访问每个城市恰好一次并返回起点,且总距离不超过k的路线。可以从哈密顿回路问题归约。

总结

本节课中我们一起学习了:

  1. P与NP类的定义,理解了“多项式时间可解”与“多项式时间可验证”的区别。
  2. 多项式时间归约的概念,它是比较问题计算难度的关键工具。
  3. NP完全性的定义:属于NP且NP中所有问题都可归约到它。
  4. 库克-列文定理的历史性意义,它通过构造性地证明SAT是NP完全的,为整个NP完全理论奠定了基础。
  5. 如何利用归约,从SAT出发证明像3-SAT团问题等大量问题是NP完全的。

NP完全性理论解释了为什么我们在现实中遇到许多组合优化问题难以找到高效精确算法,并引导我们转向近似算法、启发式算法等研究方向。P与NP的关系仍然是理论计算机科学中最深刻、最著名的开放问题之一。

021:库克-列文定理

在本节课中,我们将学习计算复杂性理论中的一个核心定理——库克-列文定理。该定理证明了布尔可满足性问题(SAT)是NP完全问题,为理解NP问题的本质以及P与NP关系问题奠定了基础。

概述

库克-列文定理是理论计算机科学的基石。它表明,任何可以在多项式时间内被非确定性图灵机验证的问题,都可以在多项式时间内归约到布尔可满足性问题。这意味着SAT是NP完全问题中最“难”的问题之一。如果SAT能在多项式时间内解决,那么所有NP问题都能在多项式时间内解决,即P=NP。


NP类与验证过程

上一节我们介绍了计算复杂性的基本概念。本节中,我们来看看NP类问题的定义。

NP类包含那些其解可以在多项式时间内被验证的问题。更形式化地说,一个问题属于NP,当且仅当存在一个多项式时间的确定性图灵机(称为验证机)和一个多项式p(n),使得对于问题的任意一个实例x(长度为n),x是“是”实例当且仅当存在一个长度不超过p(n)的“证书”c,使得验证机在输入(x, c)后接受。

核心概念

  • NP类定义L ∈ NP 当且仅当存在多项式p和多项式时间确定性图灵机V(验证机),使得对于所有输入x
    • 如果 x ∈ L,则存在证书c|c| ≤ p(|x|)),满足 V(x, c) 接受。
    • 如果 x ∉ L,则对所有证书cV(x, c) 都拒绝。

多项式时间归约与NP完全性

为了比较不同问题的难度,我们使用“归约”的概念。如果问题A可以归约到问题B,意味着解决B就能解决A。

多项式时间多一归约:如果存在一个多项式时间可计算的函数f,使得对于所有输入xx ∈ A 当且仅当 f(x) ∈ B,则称问题A可以在多项式时间内多一归约到问题B,记作 A ≤_p B

NP完全问题:一个问题B是NP完全的,需要满足两个条件:

  1. B ∈ NP
  2. 对于所有问题 A ∈ NP,都有 A ≤_p B

如果一个NP完全问题能在多项式时间内解决(即属于P类),那么所有NP问题都能在多项式时间内解决,即P=NP。


库克-列文定理的证明思路

库克-列文定理的核心是证明SAT是NP完全的。证明分为两步:

  1. 证明SAT属于NP:给定一个布尔公式和一组变量赋值(作为证书),我们可以在多项式时间内验证这组赋值是否使公式为真。因此,SAT ∈ NP。
  2. 证明任何NP问题都可以归约到SAT:这是证明的关键。对于任意一个NP问题L,存在一个非确定性图灵机M和多项式p(n),使得M能在p(n)步内判定L。我们需要构造一个从L的实例x到布尔公式φ_x的归约函数f,使得:
    • f可在多项式时间内计算。
    • x ∈ L 当且仅当 φ_x 是可满足的。

构造方法:公式φ_x将模拟图灵机M在输入x上的计算过程。我们使用布尔变量来描述计算过程中的三个核心方面:

  • 状态:在特定时间步,图灵机处于哪个状态。
  • 纸带内容:在特定时间步,纸带的每个格子包含哪个符号。
  • 读写头位置:在特定时间步,读写头在纸带的哪个位置。

由于M在p(n)步内运行,且使用的纸带格子数也是p(n)的多项式,因此描述整个计算过程所需的变量总数是多项式规模的。然后,我们构造一系列子句来编码:

  • 初始配置:时间步0时,纸带内容是x,机器处于起始状态,读写头在起始位置。
  • 转移函数:每个时间步的配置必须根据图灵机的转移规则,从前一个时间步的配置合法地演变而来。
  • 接受配置:在p(n)步内,机器必须进入接受状态。

如果存在一组变量赋值使φ_x为真,那么就对应了M在输入x上的一个接受计算路径,即x ∈ L。反之亦然。


证明的技术细节:规范化与编码

为了使构造更清晰,通常会对非确定性图灵机进行规范化,例如确保其只有一个接受状态,并且纸带是单向无限的。然后,将计算表(时间 vs. 纸带位置)的每个单元格可能的情况编码为布尔变量。

以下是构造中涉及的主要逻辑约束类型:

  • 唯一性约束:每个单元格在每一时刻只能处于一种状态/包含一个符号。
  • 局部一致性约束:每个单元格在t+1时刻的内容,仅由t时刻自身及其相邻单元格的状态和内容决定,这编码了图灵机的局部转移规则。
  • 边界约束:编码初始输入和最终接受条件。

所有这些约束都可以写成合取范式(CNF)的布尔公式,且公式的整体大小是输入规模的多项式。这就完成了从任意NP问题L到SAT问题的多项式时间归约。


意义与推论

库克-列文定理具有深远的意义:

  • 第一个NP完全问题:它给出了第一个被证明是NP完全的问题(SAT),为后续证明其他问题的NP完全性提供了基础。通常,要证明问题B是NP完全的,只需证明 SAT ≤_p BB ∈ NP
  • NP完全问题的普遍性:大量来自图论、调度、逻辑、组合优化等领域的实际问题(如旅行商问题、背包问题、图着色问题、哈密顿路径问题等)都被证明是NP完全的。这意味着它们极有可能不存在多项式时间精确算法。
  • P vs. NP问题的核心:SAT的NP完全性使得它成为研究P与NP关系的关键。如果找到SAT的多项式时间算法,就证明了P=NP;如果证明SAT不存在多项式时间算法,则证明了P≠NP。这正是“千禧年大奖难题”之一。


总结

本节课中我们一起学习了库克-列文定理。我们首先回顾了NP类问题的定义,即那些解能在多项式时间内验证的问题。接着,我们引入了多项式时间归约和NP完全性的概念,它们是衡量问题计算难度的关键工具。

库克-列文定理的核心证明在于,对于任何一个NP问题,我们都可以构造一个多项式时间的归约,将其转换为一个布尔可满足性问题(SAT)。这个构造通过用布尔变量和子句模拟非确定性图灵机的计算过程来实现。该定理不仅证明了SAT是NP完全的,也揭示了NP完全问题在计算复杂性中的核心地位,将无数实际问题的计算难度与一个根本性的逻辑问题联系起来。理解这一定理是深入探索计算复杂性理论的基础。

022:在NP中,围绕NP,以及NP周围

在本节课中,我们将学习计算复杂性理论中的核心概念,特别是P、NP、co-NP等复杂性类。我们将探讨这些类的定义、它们之间的关系,以及一些典型的NP完全问题。课程还将介绍近似算法和概率图灵机等扩展概念。

P与NP的定义

上一节我们介绍了计算复杂性理论的基本框架,本节中我们来看看P和NP这两个核心复杂性类的正式定义。

  • P类:指所有可以由确定性图灵机多项式时间内解决的判定问题集合。形式化地,一个问题L属于P,如果存在一个确定性图灵机M和一个多项式函数p(n),使得对于任意长度为n的输入x,M在最多p(n)步内停机,并且当且仅当x是L的实例时,M接受x。
  • NP类:指所有可以由非确定性图灵机多项式时间内解决的判定问题集合。等价地,一个问题L属于NP,如果存在一个多项式时间验证算法(验证者)V和一个多项式函数q(n),使得对于任意实例x:
    • 如果x是L的实例(即答案为“是”),则存在一个长度不超过q(|x|)的“证书”c,使得V(x, c)接受。
    • 如果x不是L的实例,则对于所有可能的证书c,V(x, c)都拒绝。

NP完全问题与co-NP

理解了NP的定义后,我们来看看NP中一类特别困难的问题——NP完全问题,以及NP的“补集”类co-NP。

以下是几个经典的NP完全问题示例:

  • 整数规划
  • 背包问题
  • 旅行商问题
  • 子集和问题

co-NP类定义为NP中所有问题的补集所构成的类。形式化地,co-NP = { L | L的补集 ∈ NP }。这意味着,对于co-NP中的一个问题L,其“是”实例的补集(即“否”实例)可以在NP中被高效验证。一个关键问题是:P = NP 是否成立? 如果成立,那么NP = co-NP。但即使NP = co-NP,P也可能不等于NP。

多项式时间归约与复杂性类层级

为了比较不同问题的难度,我们使用多项式时间归约的概念。如果问题A可以多项式时间归约到问题B,那么解决B的算法也能用来解决A。

基于此,我们可以构建更复杂的复杂性类层级,例如多项式层级:

  • Σ₁P = NP
  • Π₁P = co-NP
  • Σ₂P:存在一个NP预言机可以解决的问题。
  • Π₂P:co-NP预言机可以解决的问题的补集。

一个典型的问题是独立集问题:给定一个图G和一个整数k,问G中是否存在一个大小至少为k的独立集(即顶点集合,其中任意两点无边相连)。这个问题是NP完全的。而它的补问题(即判断图G是否不存在大小至少为k的独立集)则属于co-NP。

近似算法

对于NP难问题,我们通常无法在多项式时间内找到精确最优解。因此,我们转向寻找近似解。近似算法的目标是,在多项式时间内,找到一个解,其目标函数值(如成本或收益)与最优值的比值在一个可证明的常数因子之内。

以下是两个经典问题的近似算法示例:

  • 顶点覆盖问题:贪心算法可以给出一个2-近似解(即找到的覆盖大小不超过最优解的两倍)。
  • 最大割问题:随机分配顶点到两个集合,或者使用局部搜索算法,可以获得一个0.5-近似解(即找到的割大小至少是最优解的一半)。

概率图灵机与复杂性类BPP

除了确定性计算,我们还可以考虑概率性计算。概率图灵机在计算过程中可以进行随机选择。

BPP类(有界错误概率多项式时间)是指那些存在概率图灵机在多项式时间内解决的问题,并且对于所有输入:

  • 如果答案为“是”,则接受概率至少为2/3。
  • 如果答案为“否”,则接受概率至多为1/3。
    这里的错误概率1/3可以通过多次运行算法并取多数结果来任意减小。

概率算法为许多问题提供了简洁高效的解决方案,例如素数测试。

总结

本节课中我们一起学习了计算复杂性理论的核心内容。我们从P和NP的定义出发,探讨了NP完全问题的概念以及co-NP类。我们了解了如何使用多项式时间归约来比较问题难度,并简要接触了多项式层级。面对NP难问题,我们介绍了近似算法的思想,并以顶点覆盖和最大割问题为例进行了说明。最后,我们扩展到了概率计算模型,定义了BPP复杂性类。这些概念构成了我们理解计算问题内在难度和设计高效算法的基础。

023:信息与随机性

在本节课中,我们将学习信息论与随机性的核心概念,特别是柯氏复杂度的定义、性质及其与可计算性和概率计算的关系。我们将从图灵机的基本概念出发,逐步构建对算法信息论的理解。


柯氏复杂度:定义与基本性质

上一节我们介绍了计算理论的基础。本节中,我们来看看如何量化一个字符串的信息内容,这引出了柯氏复杂度的概念。

柯氏复杂度 K(x) 定义为:输出字符串 x 的最短程序的长度。更形式化地说,对于一个通用图灵机 U,字符串 x 的柯氏复杂度是满足 U(p) = x 的最短程序 p 的长度。

以下是柯氏复杂度的几个关键性质:

  • 依赖于通用机,但仅差一个常数:选择不同的通用图灵机 UU',对应的复杂度 K_U(x)K_U'(x) 最多相差一个与 x 无关的常数 c。即 |K_U(x) - K_U'(x)| ≤ c
  • 存在不可压缩字符串:对于任意长度 n,都存在长度为 n 的字符串 x,其柯氏复杂度 K(x) ≥ n。这样的字符串被认为是“随机的”或“不可压缩的”。
  • 大多数字符串不可压缩:随着字符串长度 n 的增长,绝大多数字符串的柯氏复杂度都接近 n。只有极少数字符串是高度可压缩的。

柯氏复杂度与随机性

我们已经看到,柯氏复杂度可以定义字符串的随机性。本节中,我们来看看如何利用这个概念区分随机字符串与非随机字符串。

直观上,如果一个字符串的模式可以被一个简短的程序描述,那么它就不是随机的。反之,如果描述它的最短程序几乎和它本身一样长,那么它就是随机的。以下是判断随机性的核心思路:

  • 对于一个字符串 x,如果其柯氏复杂度 K(x) 不小于其长度 |x|,则 x 被认为是随机的。
  • 然而,由于 K(x) 是不可计算的,我们无法精确判断一个给定字符串是否随机。但我们可以证明,随机字符串是存在的,并且在所有字符串中占绝大多数。

柯氏复杂度与概率计算类 BPP

之前我们讨论了确定性计算。本节中,我们来看看柯氏复杂度如何与概率计算联系起来,特别是与有界错误概率多项式时间类 BPP 的关系。

BPP 类包含那些存在多项式时间概率图灵机可以解决的问题,该机器对任何输入的错误概率小于 1/3。以下是 BPP 与柯氏复杂度的联系:

  • 研究显示,如果存在足够多的“柯氏随机”字符串(即复杂度很高的字符串),那么这些字符串可以作为概率算法中理想的随机比特源。
  • 理论上,如果我们可以获取真正的高柯氏复杂度字符串,那么 BPP 类中的许多算法可以变得更高效或更可靠。这连接了算法信息论与计算复杂性理论。

不可压缩性方法及其应用

我们了解了随机字符串的存在性。本节中,我们来看看如何利用字符串的不可压缩性来证明计算问题的下界,这种方法称为“不可压缩性方法”。

其核心思想是:如果一个问题的所有解都很长,那么假设存在一个很短的解,就可以构造出一个描述长随机字符串的短程序,这与该字符串的高柯氏复杂度(即不可压缩性)矛盾。以下是应用此方法的典型步骤:

  1. 假设某个问题存在一个大小(或运行时间)很小的算法或解决方案。
  2. 选择一个高柯氏复杂度的长随机字符串。
  3. 利用假设中的小方案,可以简短地描述这个随机字符串。
  4. 这与该随机字符串的不可压缩性(即高柯氏复杂度)产生矛盾。
  5. 从而反证原假设不成立,即证明了该问题需要较大的解决方案或较长的运行时间。

这种方法常用于证明数据结构的下界、通信复杂度等问题。


总结

本节课中我们一起学习了柯氏复杂度的核心概念。我们首先定义了字符串的柯氏复杂度 K(x),并理解了它作为信息内容度量的基本性质。接着,我们探讨了如何用柯氏复杂度来定义随机性,并认识到大多数字符串实际上是随机的、不可压缩的。然后,我们将这一概念与概率计算类 BPP 联系起来,看到了理想随机源的理论价值。最后,我们介绍了强大的“不可压缩性方法”,展示了如何利用柯氏复杂度来证明计算问题的下界。这些内容构成了算法信息论的基础,连接了信息、随机性与计算复杂性。

024:摘要与总结 📚

在本节课中,我们将回顾理论计算机科学课程的核心概念。我们将从形式语言和自动机的基础知识开始,逐步深入到图灵机、可计算性以及计算复杂性理论。课程旨在构建一个从简单到复杂的知识体系,帮助初学者理解计算理论的基本框架。


形式语言与自动机 🤖

上一节我们介绍了计算理论的基本范畴,本节中我们来看看形式语言与自动机理论。这是理论计算机科学的基石,它研究如何用数学模型描述语言和计算过程。

形式语言是由符号组成的字符串集合。我们使用文法来形式化地定义语言。例如,一个上下文无关文法可以表示为:

G = (V, Σ, R, S)

其中 V 是非终结符集合,Σ 是终结符集合,R 是产生式规则集合,S 是起始符号。

自动机是用于识别语言的抽象机器。最基本的自动机是有限自动机。

以下是几种关键的语言类及其对应的自动机模型:

  • 正则语言: 由正则表达式描述,可由有限自动机识别。
  • 上下文无关语言: 由上下文无关文法描述,可由下推自动机识别。
  • 上下文相关语言: 由上下文相关文法描述,可由线性有界自动机识别。
  • 递归可枚举语言: 由无限制文法描述,可由图灵机识别。

这些语言类之间存在严格的包含关系,构成了著名的乔姆斯基谱系


图灵机与可计算性 💻

在掌握了自动机模型后,我们进入更强大的计算模型——图灵机。图灵机是理论计算机科学中最重要的概念之一,它定义了什么是“可计算”。

一台图灵机可以形式化地定义为一个七元组:

TM = (Q, Σ, Γ, δ, q0, q_accept, q_reject)

其中 Q 是状态集合,Σ 是输入字母表,Γ 是纸带字母表,δ 是转移函数,q0 是初始状态,q_accept 是接受状态,q_reject 是拒绝状态。

图灵机的核心思想是模拟人类使用纸笔进行计算的过程。它具有无限长的纸带和一个读写头,可以根据当前状态和读到的符号进行写入、移动和状态转换。

关于图灵机,有几个根本性的结论:

  • 通用图灵机: 存在一台特殊的图灵机,它可以模拟任何其他图灵机在给定输入上的行为。这是现代存储程序式计算机的理论原型。
  • 停机问题: 不存在一个算法(图灵机),能够判断任意图灵机在任意输入上是否会停机。这表明有些问题是不可判定的。
  • 丘奇-图灵论题: 任何在算法上可计算的功能,都可以被一台图灵机计算。这确立了图灵机作为计算能力极限的地位。

不可判定性通过对角化论证等技术得到证明,揭示了计算的固有局限性。


计算复杂性理论 ⏱️

了解了什么是可计算之后,我们自然要问:哪些可计算问题是“容易”的,哪些是“困难”的?计算复杂性理论试图回答这个问题。

我们主要根据解决问题所需的时间资源来对问题进行分类。时间通常表示为输入规模 n 的函数。

以下是两个最重要的复杂性类:

  • P 类: 包含所有可以由确定性图灵机多项式时间内解决的问题。例如,排序、搜索。
  • NP 类: 包含所有可以由非确定性图灵机在多项式时间内解决的问题,或者等价地说,其解可以在多项式时间内被验证的问题。例如,布尔可满足性问题、旅行商问题。

P 与 NP 的关系是理论计算机科学中悬而未决的核心问题,即 “P 是否等于 NP?”。目前普遍认为 P ≠ NP,但尚未得到证明。

为了比较问题的难度,我们使用多项式时间归约的概念。如果问题 A 可以归约到问题 B,那么 B 至少和 A 一样难。NP 类中最难的问题被称为 NP 完全问题,例如 SAT 问题。如果任何一个 NP 完全问题被证明属于 P,那么 P 就等于 NP。


总结与展望 🎯

本节课中我们一起学习了理论计算机科学的核心脉络。

我们从形式语言与自动机出发,建立了语言和计算的形式化模型。然后,我们引入了强大的图灵机模型,它定义了计算的边界,并揭示了像停机问题这样的固有局限性。最后,我们进入了计算复杂性理论,探讨了在可计算的问题中,如何区分“易”与“难”,并了解了 P、NP 和 NP 完全性等关键概念。

这门课程不仅提供了理解计算机科学本质的理论工具,也为后续学习编译器构造、算法分析、密码学等领域奠定了坚实的基础。尽管有些结论(如不可判定性)看起来是消极的,但它们深刻地刻画了计算的本质,并指导着我们在可行的范围内设计和分析高效的算法。

posted @ 2026-03-29 09:46  布客飞龙III  阅读(14)  评论(0)    收藏  举报