运行时间建模

学习目标

  • 形式化的建模程序的运行时间, 特别是诸如 时间算法的概念
  • 分别对多项式时间和指数时间进行建模的复杂性类
  • 了解时间层级定理, 即对于任意 都存在我们可以在 时间计算但不能在 时间计算的函数.
  • 代表非一致性计算 的复杂性类, 及 这一结论.

Quote

当问题规模的度量标准合理, 且当规模取值任意大时, 对算法难度的阶进行渐近估计在理论上具有重要意义. 这种估计无法被操纵——即无法通过人为地使算法在较小规模下变得困难而扭曲结果.

-杰克·埃德蒙兹, “Paths, Trees, and Flowers”, 1963.

Quote

马克斯·纽曼: “声称机器‘能够’做这做那固然很好, 但是……它做这些事情究竟需要花费多少时间呢?”

艾伦·图灵: “在我看来, 这个时间因素正是所有真正的技术难点所在. ”

-BBC 广播座谈节目 “我们可以说计算机会思考吗?”, 1952.

第12章 中, 我们介绍了一些高效的算法, 并且对他们的运行时间做了一些假设, 不过并未对这些算法的运行时间进行精确数学定义. 我们将在本章节中借助我们之前已经介绍过的图灵机和 RAM(或等价的 NAND-TM 和 NAND-RAM)机完成这一工作. 任何非平凡的算法都会在更大规模的输入上运行更长的时间, 因此算法的运行时间并不能用一个确定的数字来表示. 因此, 我们想要确定的是算法需要运行的步数和输入长度的关系. 我们特别关注以下两者之间的区别, 那些最多只需多项式时间(即对于某个常数 时间为 )的算法, 与那些任何算法都至少需要指数时间(即对于某个 时间为 )的问题. 正如 第12章 中 Edmonds 的引言所提到的, 这两者之间的差异, 有时与可计算和不可计算之间的差异一样重要.

overview

图 13.1. 本章得到结果的概览

本章:一个直观的概述

在这一章中我们形式化的定义一个函数可以被在确定的步数下计算意味着什么. 正如在 第12章 中所说的那样, 运行时间并不是一个数字, 我们关心的是随着输入规模增大, 算法运行步数会以怎样的规模增长. 我们可以用图灵机或 RAM 机来给出一个形式化定义 - 事实上模型的选择并不影响这个问题的核心解决方案 本章我们将给出几个重要定义并证明一些重要的定理. 我们将定义本书中使用的主要时间复杂性类, 并展示时间层级定理, 该定理表明:如果给予更多的资源(即针对每个输入规模允许更多的执行步数), 我们就能够计算更多的函数

要将这一切用不那么数学化的语言表述出来, 我们将定义能在 步内将函数 计算出来的含义, 其中 是一个将输入长度 映射到计算所需的步数的函数. 使用这些定义, 我们将做以下事情(可参考 图 13.1

  • 我们定义复杂性类 为可以在多项式时间内计算的布尔函数的集合, 复杂性类 为可以在指数时间内计算的函数的集合. 注意 即如果我们能在多项式时间内计算一个函数, 那么当然也能在指数时间内计算他.

  • 我们证明, 用图灵机和RAM机计算一个函数所需的时间是多项式相关的. 这意味着, 无论使用图灵机还是 RAM 机(或 NAND-RAM 机)来定义, 总是相同.

  • 我们给出一个高效且通用的 NAND-RAM 程序, 并使用它建立时间层级定理, 该定理意味着 的真子集.

  • 我们将此处定义的概念与 第3章 中定义的布尔电路和 NAND-CIRC 程序等非一致性模型联系起来. 我们将 定义为可以由一系列多项式大小的电路所计算的函数类. 我们证明了 包含不可计算函数.

13.1 形式化的定义运行时间

我们的计算模型(图灵机, NAND-TM 和 NAND-RAM 程序等)都是通过其运作方式都是对输入逐步执行一系列指令. 我们可以通过测量算法 在输入 上执行的步数, 并将其表示为输入长度 的函数, 从而定义算法 在这些模型下的运行时间. 我们首先定义基于图灵机的运行时间:

定义 13.1 (运行时间(图灵机)).

为某个实数到实数的映射. 如果存在一台图灵机 使得对于每一个充分大的 和每一个 当给定输入 时, 机器 在执行最多 步后停机并输出 那么我们称函数 是在 图灵机时间(Turing Machine Time, 简称 TM 时间)内可计算的. 我们定义 为所有在 图灵机时间内可计算的布尔函数(即映射 的函数)的集合.

重要启示

重要提示 13.1. 对于函数 我们可以形式化的定义 能在至多 的时间内计算意味着什么, 其中 为输入规模.

暂停一下

定义 13.1 并不复杂, 但这是本书中最为重要的定义之一. 照例, 代表一类函数, 而不是机器类. 若 是图灵机, 则像 “ 属于 ” 这样的表述并不正确. 此处定义的 TM 时间(图灵机时间)概念在文献中有时被称为“单带图灵时间”(single-tape Turing machine time), 这是因为有些文献会考虑拥有多条工作带的图灵机.

放宽条件只考虑充分大的 虽然本质上并不是很重要, 但却非常便利, 因为这使我们能够避免讨论一些无趣的边界情况. 尽管“函数的运行时间”这一概念可以在任意函数上定义, 但在定义 类时, 我们只考虑布尔函数, 即那些只有一个 bit 输出的函数. 这一选择并不重要, 是为了后续讨论的简洁与便利而做出的. 事实上, 任何一个非布尔函数都有一个与之计算等价的布尔变体, 参见 习题 13.3

练习 13.1 (时间界限的示例). 证明

exampletimebounds

图 13.2. 比较(右图的 Y 轴采用对数标度). 因为对于足够大的

练习 13.1 的解答

证明其实已经在 图 13.2 中展示了. 假设 则存在数 和计算模型 满足对于任意 都有 会在最多 步内输出 的结果. 因为 一定存在数 满足对于任意 都有 则对于任意 会在至多 步内输出 的结果, 即证明了

13.1.1 多项式时间和指数时间

与可计算性的概念不同, 精确的运行时间可能会取决于我们所使用的计算模型. 然而, 事实上, 如果我们只关心“足够粗糙”的尺度(大部分情况下都是如此), 那么模型的选择——无论是图灵机、RAM 机、NAND-TM/NAND-RAM 程序, 还是 C/Python 程序——都无关紧要了. 这就是所谓的扩展Church-Turing论题 (extended Church-Turing Thesis). 具体来说, 我们主要关心的是多项式时间与指数时间之前的区别.

我们将关注以下两个主要的时间复杂性类:

  • 多项式时间: 如果一个函数 属于类 则称其是 多项式时间可计算 的. 也就是说, 若 则存在一个计算 的算法, 其运行时间关于输入长度至多是多项式的(换言之, 对于某个常数 至多 ).

  • 指数时间: 如果一个函数 属于类 则称其是 指数时间可计算 的. 也就是说, 若 则存在一个计算 的算法, 其运行时间关于输入长度至多是指数的(换言之, 对于某个常数 至多 ).

形式化的说, 他们是如下定义的.

定义 13.2 ().

设函数

若存在一个多项式 和一个图灵机 满足对于任意 当给出输入 时, 图灵机将在至多 步内停机并输出 则我们称

若存在一个多项式 和一个图灵机 满足对于任意 当给出输入 时, 图灵机将在至多 步内停机并输出 则我们称

暂停一下

请务必花点时间, 确保你透彻理解了这些定义. 特别需要注意的是, 学生们有时会误以为 类指的是那些不在 中的函数. 然而, 事实并非如此. 如果 属于 这意味着它能够在指数时间内被计算出来. 这并不意味着它不能同时在多项式时间内被计算.

练习 13.2 ( 的另一定义).

证明 定义 13.2 中定义的 等价.

练习 13.2 的解答

为了证明这两个集合相等, 我们可证明 以及

我们从前一个包含关系开始. 假设 那么存在某个多项式 和一台图灵机 使得 能计算 并且对于每一个输入 都在至多 步内停机. 我们可以将多项式 写成 的形式, 其中 并且我们假设 非零(否则我们就让 对应使得 非零的最大数). 这个 即为 的次数(degree). 由于 无论系数 是多少, 对于足够大的 都有 这意味着图灵机 在处理长度为 的输入时, 会在少于 步内停机, 因此

对于第二个包含关系, 假设 那么存在某个正整数 使得 这意味着存在一台图灵机 和某个数值 使得 能计算 并且对于每一个 在处理长度为 的输入时, 都在至多 步内停机. 设 在处理长度至多为 的输入时所花费的最大步数. 那么, 如果我们定义多项式 我们就会发现 在处理每一个输入 时都在至多 步内停机, 因此 的存在证明了

因为指数时间比多项式时间大得多, 类. 我们在 第12章 中列出的所有问题都属于 不过如我们所见, 对于他们中的一些问题存在更高效的算法,这证明了他们实际上属于更小的 类.

(但目前不知道属于
最短路最长路
最小割最大割
2SAT3SAT
解线性方程组解二次方程组
零和博弈纳什均衡
行列式积和式
素数判定整数分解

这是一个来自 第12章 的表格. 表格中的所有问题都属于 类但只有左列中的问题目前已知属于 类. (换言之, 他们有多项式时间的算法). 参见 图 13.3.

PvsEXPfig

图 13.3. 一些在 类中的问题和一些在 类中但不知道在不在 类中的问题的例子. 因为 都是布尔函数的类, 在此图中,我们始终指的是这些问题的布尔变体 (即只关心是/否).

问题的布尔版本

备注 13.1. 第12章 中定义的许多问题都对应于非布尔函数 (即输出超过一个 bit 的函数), 而 是布尔函数的集合. 然而, 对于每一个非布尔函数 我们总是可以通过定义 的第 个比特, 来定义一个与之等价的布尔函数 (参见 习题 13.3). 因此, 上表以及 图 13.3 中所指的, 都是这些问题的计算等价布尔变体.

13.2 使用 RAM 机 / NAND-RAM 建模运行时间

图灵机虽然是一种简洁的理论计算模型, 但它与现实世界的计算架构并不十分吻合. 当我们考虑哪些函数是“可计算的“这一问题时, 图灵机与实际计算机之间的这种差异关系不大; 但在涉及“效率“的语境下, 这种差异就会产生影响. 甚至是本科算法课程中的基础内容——如“归并排序“, 也无法在图灵机上以 的时间实现 (参见 参考文献). RAM 机 (或等价的 NAND-RAM 程序) 更接近实际的计算架构, 也更符合我们在算法课程或白板编程面试中所说的 算法的含义. 我们可以像定义图灵机那样, 定义针对 NAND-RAM 程序的运行时间.

定义 13.3 (运行时间 (RAM)).

是某个将自然数映射到自然数的函数. 我们称函数 RAM 时间内可计算的 (简称 RAM 时间), 如果存在一个 NAND-RAM 程序 使得对于每一个足够大的 和每一个 当给定输入 时, 程序 在执行至多 行指令后停机, 并输出

我们定义 为在 RAM 时间内可计算的布尔函数 (即映射 的函数) 的集合.

因为 NAND-RAM 程序更加符合我们对运行时间的直观理解, 我们将把 NAND-RAM 作为我们讨论运行时间的默认模型, 并因此使用不带任何下标的 来表示 然而, 事实证明, 只要我们只关心指数时间和多项式时间之间的区别, 模型的选择并没有太大影响. 原因是图灵机可以模拟 NAND-RAM 程序, 且其开销至多是多项式级别的 (参见 图 13.4):

定理 13.1 (图灵机和 RAM 机的联系).

为一个函数, 满足对任意 都有 且映射 可以由一台图灵机在 时间内计算得出. 那么:

暂停一下

定理 13.1 中的一些技术细节并不重要, 如要求 可以在 时间内被计算出来的条件, 或者 (13.1) 中的常数 (这些常数并非紧致的, 是可以被改进的) 特别的, 我们在实践中遇到的所有非病态的时间界限函数, 如 等, 都满足 定理 13.1 的条件 (另见 备注 13.2)

该定理的核心信息是: 图灵机和 RAM 机是“大致等价“的, 在这个意义上, 其中一个可以模拟另一个, 且只产生多项式级别的开销. 同样地, 虽然证明过程涉及一些技术细节, 但它并不深奥也不困难, 仅仅是沿用了我们在 定理8.1 中看到的用图灵机模拟 RAM 机的方法, 只是做了更仔细的“簿记“ (即状态维护) 工作.

RAMTMsimulationfig

图 13.4. 定理 13.1 的证明表明, 我们可以用 步的 NAND-RAM 程序来模拟 步的图灵机, 并且可以用 步的图灵机来模拟 步的 NAND-RAM 程序. 因此,

例如, 通过将 代入 定理 13.1, 并利用 这一事实, 我们看到 这意味着 (根据 练习 13.2): 也就是说, 我们完全可以将 定义为由 NAND-RAM 程序 (而不是图灵机) 在输入长度的多项式时间内计算的函数类. 同样地, 通过将 代入 定理 13.1, 我们看到 类也可以定义为由 NAND-RAM 程序在至多 时间内计算的函数集, 其中 为某个多项式. 对于许多其他模型, 包括元胞自动机, C/Python/Javascript 程序, 并行计算机以及许多其他模型, 已知都存在类似的等价结果. 这证明了选择 作为捕捉独立于技术的“易处理性“概念是合理的 (参见 13.3 节 关于此问题的更多讨论). 图灵机和 NAND-RAM (以及其他模型) 之间的这种等价性, 允许我们根据手头的任务选择我们最喜欢的模型 (即“鱼与熊掌兼得“), 即使在研究效率问题时也是如此—只要我们只关心多项式时间指数时间之间的差距. 当我们想要设计一个算法时, 我们可以利用 NAND-RAM 提供的额外能力和便利. 当我们想要分析一个程序或证明一个否定性结果时, 我们可以将注意力局限于图灵机.

重要启示

重要提示 13.2.

只要我们仅关注多项式时间指数时间之间的区别, 所有 “合理的” 计算模型都是等价的.

上文中的形容词 “合理的” 指的是所有已实现的、可扩展的计算模型, 而 量子计算机 可能是唯一的例外. 参见 13.3 节第23章.

定理 13.1 的证明思路

证明 这一方向并不困难, 因为 NAND-RAM 程序 可以通过在数组中存储图灵机 的状态转移表(如 定理9.1 的证明中所做的那样), 以常数级的开销模拟 模拟图灵机的每一步都可以在常数 步 RAM 操作内完成, 且可以证明这个常数 小于

因此, 该定理的核心在于证明 这一证明紧随 定理8.1 的证明思路, 在那里我们已经证明了任何由 NAND-RAM 程序 可计算的函数 同样可以由图灵机(或等价的 NAND-TM 程序) 来计算. 为了证明 定理 13.1, 我们沿用完全相同的证明过程, 只需核实 模拟 的开销是多项式级别的即可.

该证明包含许多细节, 但并不深奥. 因此, 相比于证明过程, 理解该定理的 陈述 要重要得多.

定理 13.1的证明

我们仅关注非平凡方向的 可由某个 NAND-RAM 程序 的时间内计算, 且我们需要证明它同样可以被一个图灵机 的时间内被计算. 这等价于证明 可以被一个 NAND-TM 程序在 时间内被计算, 因为对于任意 NAND-TM 程序 都存在一台模拟它的图灵机 使得 的每一次迭代都对应 的一个单步操作.

如前文所述, 我们沿用 定理8.1 的证明方法 (使用 NAND-TM 程序模拟 NAND-RAM 程序), 并且使用一样的模拟方法, 但更仔细地核算每步模拟所需要消耗的步数. 回想一下, NAND-RAM 的模拟是通过“剥离“其特性, 直到只剩下 NAND-TM 为止.

我们不会提供所有证明的细节, 但将展示证明 NAND-RAM 的每个特性都能以至多多项式开销被 NAND-TM 模拟的核心思路:

  1. 回想一下, 每个 NAND-RAM 变量或数组元素包含的整数范围在 0 到 T 之间, 其中 T 是目前已经执行的行数. 因此, 如果 P 是一个在 时间内计算 的 NAND-RAM 程序, 那么在长度为 的输入下, P 所使用的所有整数的大小至多为 这意味着索引 i 能到达的最大值至多是 因此 的每个变量都可以看作是一个拥有至多 个索引的数组, 每个索引存放一个大小至多为 的自然数. 令 为编码此类数字所需要的对比特数 (我们可以在模拟开始时先计算出

  2. 我们可以将一个长度 包含范围在 内数字的 NAND-RAM 数组, 编码为一个包含 个比特的布尔 (即 NAND-TM) 数组. 我们也可以像 定理8.1 的证明那样, 将其视为一个二维数组. 一个包含数字的 NAND-RAM 标量则简单地编码为一个长度为 的较短 NAND-TM 数组.

  3. 我们可以使用长度为 的一维数组来模拟二维数组. 所有关于整数的算术运算都是用“小学数学算法“, 其耗时是整数比特数 的多项式级别的, 在本例中即为 因此, 我们可以用一个使用随机访问内存但仅有布尔值的一维数组, 在 步内模拟 步的 NAND-RAM 模型.

  4. 最昂贵的步骤是将随机访问内存转化为 NAND-TM/图灵机 的顺序内存模型. 正如我们在 定理8.1 证明中所做的, 我们可以通过以下步骤模拟访问数组 Foo 中由数组 Bar 编码的某个位置:

    1. Bar 复制到某个临时数组 Temp
    2. 维护一个数组 Index, 其初始除第一位为 外其余位置为
    3. 重复以下步骤直到 Temp 编码了数字 (最多重复 次)
      • Temp 编码的数值减 (消耗步数为 的多项式级)
      • 减小 i 直到其等于 (消耗 步)
      • 扫描 Index 直到直到值为 的位置, 将其改成 向后移动一步并写下 (消耗 步)
    4. 完成后, 如果我们扫描 Index 直到找到 Index[i] 的点, 那么 i 就包含了原先由 Bar 编码的值. (消耗 步)

    每次此类操作的总代价为 步.

综上所述,我们使用 步 NAND-TM 来模拟 NAND-RAM 的单步操作,因此总模拟时间为 。对于足够大的 ,这个值小于

Info

备注 13.2 (好的时间界限).

在讨论一般的时间界限时, 我们需要确保排除掉一些“病态“的情况, 比如函数 没有给算法留足够读取输入的时间, 或者时间界限函数本身就是不可计算的.

我们称函数 是一个 好的时间界限函数 (或简称为好函数), 如果它满足以下条件:

  • 对于任意 都有 (即 预留了足够的读入时间)
  • 对于任意 都有 (即 允许在更长的输入上花费更长的时间)
  • 映射 (即把长度为 的字符串映射为长度为 的全 序列) 可以被一个 NAND-RAM 程序在 时间内计算出来.

我们在应用中遇到所有“正常的“时间复杂度界限, 如 等, 都是好的. 因此, 从现在起, 我们只关心当 是“好函数“时的复杂性类 可计算性的条件一般是很容易被满足的. 比如, 对于像 这样的代数函数, 我们可以在关于 的比特数的多项式时间内 (即关于 的对数多项式级) 计算出 的二进制表示. 因此, 在这种情况下, 写出字符串 的时间将会是

13.3 扩展Church-Turing论题 (讨论)

定理 13.1 表明, 图灵机和 RAM 机/ NAND-RAM 程序这几个计算模型在运行时间上是多项式等价的. 其他多项式等价模型的例子有:

  • 所有标准的编程语言, 包括 C/Python/JavaScript/Lisp/等.
  • 算子 (参见 13.8 节)
  • 元胞自动机
  • 并行计算机
  • 生物计算设备, 如基于 DNA 的计算机.

扩展Church-Turing论题 指出, 这一表述对于所有物理上可实现的计算模型均成立. 换言之, 扩展Church-Turing论题认为, 对于任意一个可以扩展的计算设备 (该设备具有有限的描述, 但原则上可以用于处理任意长度的输入), 都存在某个常数 使得对于 在长度为 的输入上使用 量的物理资源所能计算的每一个函数 都属于 这是对一般的Church-Turing论题 (在 第 8.8 节 中被讨论) 的加强. 普通论题仅指出所有物理上可实现模型的“可计算函数集“是相同的, 但不要求不同模型之间模拟的开销至多为多项式级别.

目前所有关于可扩展计算模型和编程语言的构建都遵循扩展Church-Turing论题, 即它们都可以被图灵机 (以及 NAND-TM 或 NAND-RAM 程序) 以多项式级开销进行模拟. 因此, 类对于模型的选择具有鲁棒性. 我们可以使用任何我们喜欢的编程语言, 或者算法的高层描述, 来确定一个问题是否属于

与Church-Turing论题本身一样, 扩展Church-Turing论题也处于渐近设定之下, 并不直接产生可实验验证的预测. 然而, 它可以用更具体的开销界限来实例化, 从而产生可实验验证的预测, 例如我们在 5.6 节 中提到的物理扩展Church-Turing论题.

在过去一百多年对计算的研究和机械化进程中, 尚未有人制造出能违反扩展Church-Turing论题的可扩展计算设备. 然而, 量子计算 (如果得以实现) 将对扩展丘奇-图灵论题提出严峻挑战 (见 第23章). 但是, 即便量子计算的愿景完全实现, 扩展Church-Turing论题在“精神层面“上依然是正确的: 虽然我们需要修正该论题以纳入量子计算的可能性, 但其宏观框架保持不变. 我们依然能够对计算进行数学建模; 依然可以将程序视为字符串并拥有通用程序; 依然拥有时间层级和不可计算性结果; 并且依然没有理由怀疑 (“普通”) Church-Turing论题. 此外, 量子计算的前景似乎并不会改变我们所关心的许多 (虽非全部!) 具体问题的运行时间复杂度. 特别是, 就我们目前所知, 在 第12章 提到的所有示例问题中, 只有整数分解这一个问题的复杂度, 会因为将模型修改为包含量子计算机而受到影响.

13.4 高效的通用机器: 在 NAND-RAM 中的 NAND-RAM 解释器

我们已经在 定理 9.1 中见过了 “通用图灵机”. 审视其证明, 并结合 定理 13.1 , 我们可以看到程序 具有多项式开销, 即它可以在 步内模拟给定 NAND-TM (或 NAND-RAM) 程序 在输入 上运行 步. 但事实上, 通过直接模拟 NAND-RAM 程序, 我们可以做的更好, 仅需常数倍的乘法开销. 也就是说, 存在一个通用 NAND-RAM 程序 使得对于每一个 NAND-RAM 程序 仅需要 步就能模拟 步. ( 记号中隐含的常数可能取决于程序 但不依赖输入的长度.)

定理 13.2 (NAND-RAM 的高效通用性).

存在一个 NAND-RAM 程序 满足以下性质:

  1. ( 是一个通用的 NAND-RAM 程序) 对于任意 NAND-RAM 程序 和输入 其中 表示 在一个编码 的字符串上的输出.

  2. ( 是高效的) 存在一个常数 使得对于每一个 NAND-RAM 程序 如果 在输入 后运行至多 步后停机, 那么 在运行至多 步后停机, 其中

暂停一下

正如 定理 13.1 的情况一样, 定理 13.2 的证明并不很深奥, 因此理解它的陈述更加重要. 具体来说, 如果你明白如何使用像 Python 这样的现代语言写一个 NAND-RAM 解释器, 那么你就知道了关于该定理证明的一切.

universalrammachinefig

图 13.5. 通用 NAND-RAM 程序 通过将输入程序 的所有变量存储在 的单个数组 Vars 来模拟 如果 个变量, 那么 Vars 被划分为长度为 的块, 其中第 个块的第 个坐标包含 的第 个数组的第 个元素. 如果 的第 个变量是标量, 那么我们只需将其值存储在 Vars 的第 个块中.

定理 13.2 的证明

若要完整展示一个通用 NAND-RAM 程序, 我们需要描述一个精确的表示方案, 以及该程序的完整 NAND-RAM 指令.

虽然这可以被完成, 但关注主要想法更为重要, 因此我们在这里仅概述证明.

NAND-RAM 的规范在 附录 中给出, 出于此模拟的目的, 我们可以简单地将 NAND-RAM 代码表示为 ASCII 字符串.

程序 接收一个 NAND-RAM 程序 和一个输入 作为输入, 并逐步模拟

为此, 执行以下操作:

  1. 维护变量 program_counternumber_steps, 分别用于表示待执行的当前行和迄今为止已执行的步数.

  2. 最初扫描 的代码以找出 使用的变量名的数量 将把每个变量名转换为 之间的一个数字, 并使用一个数组 Program 来存储 的代码, 其中对于每一行 Program[] 将存储 的第 行, 其中的变量名已被转换为数字. (更具体地说, 我们将使用常数数量的数组来分别编码该行中使用的操作, 以及操作数的变量名和索引.)

  3. 维护一个数组 Vars, 其中包含 的变量的所有值. 我们将 Vars 分割为长度为 的块. 如果 是对应于 的数组变量 Foo 的数字, 那么我们将 Foo[0] 存储在 Vars[] 中, 将 Foo[1] 存储在 Vars[] 中, 将 Foo[2] 存储在 Vars[] 中, 依此类推 (参见 图 13.5). 一般的, 如果 的第 个变量是标量变量, 那么它的值将被存储在位置 Vars[] 中. 如果它是一个数组变量, 那么它的第 个元素的值将被存储在位置 Vars[] 中.

  4. 为了模拟 的一步, 程序 Program 中获取对应于 program_counter 的行并执行它. 由于 NAND-RAM 具有常数数量的算术运算, 我们可以使用一连串常数数量的 if-then-else 来实现执行哪种运算的逻辑. 从 Vars 中检索每条指令的操作数的值可以使用常数数量的算术运算来完成.

初始化阶段仅花费常数 (取决于 而非输入 数量的步骤.

一旦我们完成了初始化, 为了模拟 的单一步骤, 我们只需要获取相应的行, 并进行常数数量的 “if else” 和对 Vars 的访问来模拟它.

因此, 当忽略依赖于程序 的常数时, 模拟程序 个步骤的总运行时间至多为

13.4.1 限时通用图灵机

高效通用机的一个推论如下. 给定任意图灵机 输入 以及 “步数预算” 我们可以在关于 的多项式时间内模拟 执行 步. 形式化地, 我们定义一个函数 它接受 和时间预算这三个参数, 如果 在至多 步内停机, 则输出 否则输出 限时通用图灵机在多项式时间内计算 (见 图 13.6). (由于我们将时间作为输入长度的函数来度量, 我们将 定义为接受以 一元 表示的输入 即由 个 1 组成的字符串.)

定理 13.3 (限时通用图灵机).

为如下定义的函数 那么

timeduniversaltmfig

图 13.6. 限时 通用图灵机接受图灵机 输入 和时间界限 作为输入, 并在 于至多 步内停机时输出 定理 13.3 指出存在这样一台机器, 其运行时间是关于 的多项式.

定理 13.3 的证明

我们只给出证明概要, 因为该结果相当直接地由 定理 13.1定理 13.2 推导得出. 根据 定理 13.1, 要证明 只要给出一个计算 的多项式时间 NAND-RAM 程序即可.

这样的程序可以通过如下方式获得. 给定图灵机 根据 定理 13.1, 我们可以在关于其描述长度的多项式时间内, 将其转换为功能等价的 NAND-RAM 程序 使得 执行 步的过程可以由 执行 步来模拟. 然后我们可以运行 定理 13.2 中的通用 NAND-RAM 机器来模拟 执行 步, 耗时 如果执行在该预算内没有停机则输出 这表明 可以由一个 NAND-RAM 程序在关于 的多项式且关于 的线性时间内计算出来, 这意味着

13.5 时间层级定理 (Time Hierarchy Theorem)

一些函数是不可被计算的, 但是否存在可被计算, 但只能以很高的代价计算的函数呢? 具体来说, 是否存在 时间内被计算, 但不能 时间内被计算的函数呢? 事实证明, 这个问题的答案为.

定理 13.4 (时间层级定理).

对于任意一个好函数 (nice function) 一定存在一个函数 属于

这里出现 并没有什么特殊的理由, 我们也可以用其他能被高效计算, 且当 n 趋于无穷时也趋于无穷的函数来替代

重要启示

重要提示 13.3.

如果我们有更多的时间, 我们就能计算更多的函数.

Info

备注 13.3 (时间层级定理的简单推论).

时间层级定理的普适性会让其证明读起来略显晦涩. 如果你先尝试自己证明一个简单的命题 可能会让你更易理解该证明.

你可以通过证明 属于 来做到这一点: 对于任意图灵机 和输入 当且仅当 在输入 上运行最多 步后停机. 通过使用通用图灵机 (或 定理 13.2 中的高效通用 NAND-RAM 程序), 可以证明 另一方面, 我们可以利用与 9.3.2节 中用于证明 不可计算性中类似的思路来证明

timehierarchythmfig

图 13.7. 时间层级定理 (定理 13.4) 说明图中这些复杂性类有本质区别.

定理 13.4 的证明思路

定理 9.3 (停机问题的不可计算性) 的证明中, 我们已经证明函数 无法在任何有限时间内被计算. 仔细审查该证明可以发现, 它实际上给出了更强的结论. 具体来说, 该证明表明, 如果我们将计算预算固定为 步, 那么我们不仅无法区分停机的程序和不停机的程序, 甚至无法区分那些在至多 步停机的程序与那些运行超过 步的程序 (其中 是某个由 决定的数值). 因此 定理 13.4 的证明沿用了停机问题不可计算性证明的思路, 但对运行时间进行了更仔细地分析.

定理 13.4 的证明

我们的证明灵感来源于停机问题不可计算性的证明. 具体的, 对于定理中描述的每个函数 我们定义 有界停机 函数 的输入是二元组 满足 编码着某个 NAND-RAM 程序. 我们定义

(常数 和函数 实际上是为了证明的便捷性任意选择的.)

定理 13.4 是以下两个断言的直接推论:

断言 1:

断言 2:

请确保你明白为什么这两个断言能直接得出 定理 13.4. 接下来我们将转而证明这两个断言.

断言 1 的证明: 我们可以轻松的在线性时间内检查是否输入具有 的形式, 其中 因为 是一个好函数, 我们可以在 内计算它的值. 因此, 我们可以如下计算

  1. 步内计算

  2. 使用 定理 13.2 中的通用 NAND-RAM 程序在至多 步内模拟 在输入 上运行 步. (回想一下, 我们用 表示一个上界为 的量, 其中 为某个常数.)

  3. 如果 步内停机则输出 否则输出

输入的长度为 因为 且对于任意 都有 程序的运行时间将会是 因此上述算法证明了 从而完成了对 断言 1 的证明.

断言 2 的证明: 这个证明是 定理 13.4 的核心, 并且容易让人回想起 不可计算性的证明. 假设 (为了导出矛盾), 存在某个 NAND-RAM 程序 可在 步内计算 我们将通过构造一个程序 来导出矛盾. 我们将证明, 在我们的假设下, 如果 在给定其自身代码 (的填充版本) 作为输入时运行少于 步, 那么它实际上会运行超过 步, 反之亦然. (这句话值得反复阅读二到三次以确保你理解其中的逻辑. 这与停机问题不可计算性的直接证明非常相似, 在那个证明中我们利用假设的 “停机求解器” 构造了一个程序, 那个程序在给定它自身代码作为输入时, 停机当且仅当自身不停机.)

我们定义将程序 为: 当输入字符串 时, 执行以下三个阶段的操作:

  1. 如果 不具备 的形式, 其中 表示一个 NAND-RAM 程序且 则返回 (回想一下, 表示有 的字符串.)

  2. 计算 (在我们的假设下以最多 步的代价).

  3. 如果 进入无限循环, 否则停机.

作为字符串时描述的长度, 并令 我们将通过讨论 等于 还是 来得出矛盾.

一方面, 如果 则在我们 计算 的假设下, 在输入 上将进入无限循环, 因此 在输入为 不会 步内停机. 这与我们的假设 矛盾.

这意味着 必然成立. 但在这个情况下, 由于我们假设了 计算 在其计算的第 阶段不会做任何事情, 因此计算的唯一开销来自第 和第 阶段. 不难验证第 阶段可以在线性时间内完成 (事实上少于 步). 第 阶段包括执行 根据我们的假设, 这需要 步. 我们可以在总计少于 步执行这两个阶段. 根据定义, 这说明 但这显然是一个矛盾, 完成了断言 2 的证明, 从而也完成了 定理 13.4 的证明.

练习 13.3 ( vs ).

证明

练习 13.3 的解答

这一陈述直接由时间层级定理得出, 但直接证明它也是一项有益的练习 (参见 定理 13.4). 我们需要证明存在 两者都是良好的函数. 由于 根据 定理 13.4, 存在某个 属于 由于对于充分大的 另一方面, 实际上, 假设反之, 存在常数 以及一个图灵机, 对于所有充分大的 它在至多 步内对长度为 的输入计算 那么, 由于对于足够大的 这将推出 这与我们对 的选择矛盾.

时间层级定理告诉我们, 存在一些函数我们能在 时间计算但不能在 时间计算, 能在 时间计算但不能在 时间计算, 等等.. 特别的, 肯定存在一些函数我们能在 时间计算但不能在 时间计算. 我们已经见过了太多自然的函数, 其已知的最好算法需要大约 的时间, 且已经有许多人投入了大量的时间与精力来尝试改进这些问题的算法. 然而, 不像有穷对无穷那样, 上述的所有例子, 我们目前仍然不知道如何去排除有 时间的算法存在.
然而我们将看到, 存在一个未被证明的猜想表明大多数这类问题都有这样的结论.

complexityclassinclusionfig

图 13.8. 一些函数已知 (或猜想) 包含在某个复杂性类里面.

时间层级定理的存在依赖于高效通用 NAND-RAM 程序 (已在 定理 13.2 被证明存在). 对于其他计算模型, 如图灵机, 我们有类似的时间层级定理表明存在某个函数能在 时间内被计算但不能在 时间内被计算, 其中 对应于相应通用机器的开销.

13.6 非一致性计算

我们现在已经了解过了两种 “计算代价” 的度量. 在 4.6 节 中, 我们使用电路 / 直线式程序定义了计算有限函数的复杂性. 具体来说, 对于有限函数 和数 如果存在一个至多包含 个与非门的电路 (或一个等价的 行的 NAND-CIRC 程序) 来计算 为了将其与本章定义的类 联系起来, 我们首先需要将类 扩展到具有无界输入长度的函数.

定义 13.4 (非一致性计算).

为一个好的时间界限函数. 对于任意 定义 在大小为 的输入上的 限制. 也就是说, 是将 映射到 的函数, 使得对于任意

如果存在与非门电路序列 满足以下条件, 我们称 在至多 大小内非一致可计算的, 记作

  • 对于任意 计算函数

  • 对于任意足够大的 至多有 个门.

换言之, 当且仅当对于任意 在非一致性中的类似物是 其被定义为

非一致性计算与一致性复杂性类 (如 之间存在巨大差异. 意味着存在一个固定的 (不由输入改变) 图灵机 满足在任意输入上, 都能以多项式时间计算 的结果. 而 仅意味着, 对于每个输入长度 存在一个不同的 (可能由输入大小改变) 的电路, 使用多项式数量的门来计算该长度输入上的 正如我们所见, 并不意味着 然而, 这一陈述的反方向是成立的.

Ppolyfig

图 13.9. 我们可以将无限函数 视为有限函数集合 其中 在长度为 的输入上的限制. 如果对于任意 函数 可由多项式大小的 NAND-CIRC 程序 (或等价地, 多项式大小的布尔电路) 计算, 我们就说 属于

定理 13.5 (非一致性计算包含一致性计算).

存在某个 使得对于每个好函数 都有

特别的, 定理 13.5 表明对于每个 因此

定理 13.5 的证明思路

证明的思路是 “循环展开”. 具体的, 我们将使用一致性计算和非一致性计算的编程语言变体, 即 NAND-CIRC 和 NAND-TM. 两者之间的主要差别在于 NAND-TM 有循环. 然而, 对于每个固定的 如果我们知道一个 NAND-TM 程序最多运行 步, 那么我们就可以将这些循环用简单的“复制粘贴“代码 替代, 类似于在 Python 我们可以将

for i in range(4):
	print(i)

替换成没有循环的代码

print(0)
print(1)
print(2)
print(3)

为了将这一证明思路转化为实际的证明, 我们需要解决一个技术难点, 即确保 NAND-TM 程序是非感知的, 意思是说在循环的第 次迭代中, 索引变量 i 的值仅取决于 j, 而不取决于输入的内容. 我们将在 13.6.1 节 中暂时岔开话题来专门解决这一点, 随后完成 定理 13.5 的证明.

13.6.1 非感知的 NAND-TM 程序

我们证明 定理 13.5 的方法涉及了 “循环展开”. 比如, 考虑下面这个用于计算任意输入长度 函数的 NAND-TM 程序:

temp_0 = NAND(X[0],X[0])
Y_nonblank[0] = NAND(X[0],temp_0)
temp_2 = NAND(X[i],Y[0])
temp_3 = NAND(X[i],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)
MODANDJUMP(X_nonblank[i],X_nonblank[i])

举个例子, 若 我们可以尝试通过简单地把循环复制三遍 (删去 MODANDJMP 这行), 把这个 NAND-TM 程序翻译成用于计算 的 NAND-CIRC 程序

temp_0 = NAND(X[0],X[0])
Y_nonblank[0] = NAND(X[0],temp_0)
temp_2 = NAND(X[i],Y[0])
temp_3 = NAND(X[i],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)
temp_0 = NAND(X[0],X[0])
Y_nonblank[0] = NAND(X[0],temp_0)
temp_2 = NAND(X[i],Y[0])
temp_3 = NAND(X[i],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)
temp_0 = NAND(X[0],X[0])
Y_nonblank[0] = NAND(X[0],temp_0)
temp_2 = NAND(X[i],Y[0])
temp_3 = NAND(X[i],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)

然而, 上面这个仍然不是一个合法的 NAND-CIRC 程序, 因为它包含一个对特殊变量 i 的引用. 我们可以通过将第一个迭代中的 i 替换为 第二个迭代中的替换为 第三个迭代中的替换为 来把上述程序转化为一个合法的 NAND-CIRC 程序. (我们还创建了一个变量 zero, 并在任意变量第一次初始化时使用, 同时移除了那些后续不再使用的非输出变量的赋值) 结果程序是一个标准的计算 的 “无索引无循环” 的 NAND-CIRC 程序. (另见 图 13.10)

temp_0 = NAND(X[0],X[0])
one = NAND(X[0],temp_0)
zero = NAND(one,one)
temp_2 = NAND(X[0],zero)
temp_3 = NAND(X[0],temp_2)
temp_4 = NAND(zero,temp_2)
Y[0] = NAND(temp_3,temp_4)
temp_2 = NAND(X[1],Y[0])
temp_3 = NAND(X[1],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)
temp_2 = NAND(X[2],Y[0])
temp_3 = NAND(X[2],temp_2)
temp_4 = NAND(Y[0],temp_2)
Y[0] = NAND(temp_3,temp_4)

unrolledcircuitfig

图 13.10. 一个通过 “循环展开” 三次计算 的 NAND-TM 程序得到的计算 的 NAND 电路.

这种转换的关键在于, 在我们最初的 NAND-TM 程序中, 无论输入是 还是任何其他字符串, 索引变量 i 都保证在第一次迭代中等于 在第二次迭代中等于 在第三次迭代中等于 依此类推. 特定的序列 并不重要: 关键属性在于 的 NAND-TM 程序是 非感知的, 即在第 次迭代中索引 i 的值仅取决于 而不取决于输入的具体选择. 幸运的是, 我们能够将每个 NAND-TM 程序转换为功能等效的非感知程序, 且其开销至多为二次方. (类似地, 我们可以将任何图灵机转换为功能等效的非感知图灵机, 参见 习题 13.6)

定理 13.6 (使 NAND-TM 非感知).

为一个好函数, 且 则存在一个 NAND-TM 程序 内计算 且满足下述条件: 对于任意 存在一个序列 满足对于任意 在输入 上执行时在第 次迭代时变量 i 等于

换言之, 定理 13.6 意味着如果我们能在 步内计算 那么我们就能用一个程序 步内计算它, 且变量 i 在第 次迭代中的值只取决于 和输入的长度, 不依赖于输入的内容. 这样的程序可以通过 “循环展开” 轻松的被转译成 行的 NAND-CIRC 程序.

定理 13.6 的证明思路

我们可以通过让一个 NAND-TM 程序 扫描它的数组来把任意 翻译成非感知的程序 换言之, 中的索引 i 将始终在 之间反复移动. 于是我们便可以用至多 的开销来模拟程序 如果 想要在一个向右的扫描中向左移动, 则我们可以简单的等待至多 步直到下一次在向左移动的过程中回到原位置.

obliviousnandtmfig

图 13.11. 通过添加两个特殊数组 AtstartAtend 来分别标记 两个位置, 我们得已用一个非感知的 NAND-TM 程序 来模拟一个 时间的 NAND-TM 程序 程序 会简单的从左到右再从右到左反复扫描它的数组. 如果原来的程序 想要向一个相反的方向移动 i, 那么我们会等待 步直到我们到达了相同的位置, 因此 时间运行.

定理 13.6 的证明

为一个在 步内计算 的 NAND-TM 程序. 我们构造一个非感知的 NAND-TM 程序 以如下过程计算 (另见 图 13.11).

  1. 在输入 上, 会计算 并创建数组 AtstartAtend 满足 Atstart[] 且对于 Atstart[]Atend[] 且对于 Atend[i] 因为 是一个好函数, 所以我们可以做到这一点. 注意因为这步计算并不依赖于 而只依赖于其长度, 因此这是非感知的.

  2. 还会有一个初始化为全 的特殊数组 Marker.

  3. Atstart[i] 时, 的索引变量会会将其移动方向改为向右, 当 Atend[i] 时, 会改为向左.

  4. 程序 会模拟程序 的指令执行. 不过当遇到指令 MODANDJMP 时, 且此时 在向左移动时尝试向右移动 (反之亦然), 那么 会将 Marker[i] 设置为 并进入一个特殊的 “等待模式”. 在这个模式下, 将会一直等待直到 Marker[i] 再次成立, 且此时 会将 Marker[i] 设为 并继续模拟的过程. 在最坏的情况下, 这将会消耗 步 (如果 需要从一头移动到另一头并从另一头移动回来.)

  5. 我们同样会在 更早结束的情况下通过添加 “虚拟步” 来保证 在恰好模拟了 步之后结束计算.

我们可以看到 每步的开销模拟 的执行, 因此我们完成了证明.

定理 13.6 能导出 定理 13.5. 事实上, 如果 是一个 行的非感知的在 时间内计算 的 NAND-TM 程序, 那么对于每个 只需要简单的做 次复制黏贴 (删去 MODANDJMP 指令), 我们都可以得到一个 NAND-CIRC 程序. 在第 个副本中, 我们将所有形为 Foo[i] 的引用替换为 foo_ 其中 i 在第 次迭代中的值.

13.6.2 “循环展开”: 从图灵机到电路的转换算法

定理 13.5 的证明是 算法的, 即这个证明给出了一个多项式时间的算法能够在给出一个图灵机 参数 的前提下生成一个有 个门的电路, 且这个电路在任意输入 上运行的结果都与 一致 (只要 在这些输入上的运行步数少于 ) 我们将在下面的定理中记录这一事实, 因为这之后会对我们很有用.

unrollloopfig

图 13.12. 函数 以图灵机 输入长度参数 和时间界限 为输入, 输出一个 大小的 NAND 电路, 该电路在 于至多 步内停机的所有输入 上与 一致.

定理 13.7 (编译图灵机到电路的编译器).

存在一个算法 满足对于任意图灵机 和参数 在至多 步内运行, 且输出一个 NAND 电路 该电路接受长度为 的输入, 有 个门, 只有一个输出, 并且满足:

定理 13.7 的证明

我们将只概述证明, 因为它可以通过直接将 定理 13.5 的证明转化为算法, 并结合 NAND-TM 程序对图灵机的模拟得到 (另见 图 13.13). 具体来说, 将执行以下操作:

  1. 将图灵机 翻译为等价的 NAND-TM 程序

  2. 将 NAND-TM 程序 翻译为等价的非感知的程序 (按照 定理 13.6 的证明). 程序 需要 步来模拟 程序的 步.

  3. 通过获得对应于 次迭代执行的 NAND-CIRC 程序 (或等价的具有 个门的 NAND 电路) 来展开 的循环.

unrolldescriptionfig

图 13.13. 我们可以将图灵机 输入长度参数 和时间界限 转换为一个 大小的 NAND 电路, 该电路在 于至多 步内停机的所有输入 上与 一致. 该转换首先利用图灵机和 NAND-TM 程序 的等价性, 然后通过 定理 13.6 转换为等价的 非感知的 NAND-TM 程序 接着 “展开” 的循环 次迭代以获得一个与 在长度为 的输入上一致的 行 NAND-CIRC 程序, 最后将此程序翻译为等价的电路.

重要启示

重要提示 13.4.

通过 “循环展开”, 我们可以将一个需要 步来计算 的算法转换为一个使用 个门来计算 上的限制的电路.

暂停一下

回顾 图 13.13 中描述的转换, 以及解决以下两个练习, 是更适应非一致性复杂度, 特别是 及其与 关系的绝佳方式.

练习 13.4 ( 的另一刻画).

证明对于任意 当且仅当存在一个多项式时间图灵机 使得对于任意 输出一个 输入电路 的描述, 该电路计算 在输入 上的限制

练习 13.4 的解答

我们从 “当” 的方向开始. 假设存在一个多项式时间图灵机 它在输入 上输出计算 的电路 那么以下是计算 的多项式时间图灵机 对于输入 将:

  1. 并计算

  2. 返回 上的执行结果.

由于我们可以在多项式时间内计算布尔电路在输入上的结果, 因此 在多项式时间内运行并对每个输入 计算

对于 “仅当” 的方向, 如果 是一个在多项式时间内计算 的图灵机, 那么 (应用图灵机和 NAND-TM 的等价性以及 定理 13.6) 同样存在一个非感知的 NAND-TM 程序 它在时间 内计算 其中 为某个多项式. 我们现在可以定义 为这样一个图灵机: 在输入 上, 它输出通过将 的 “循环展开” 次迭代而获得的 NAND 电路. 结果 NAND 电路计算 并且具有 个门. 它也可以被转换为具有 个 AND/OR/NOT 门的布尔电路.

练习 13.5 ( 的刻画).

那么 当且仅当存在一个多项式 一个多项式时间图灵机 和一个字符串序列 满足对于任意

  • 对于任意

练习 13.5 的解答

我们只概述证明. 对于 “仅当” 方向, 如果 那么我们可以简单地使用对应电路 的描述作为 并使用在多项式时间内计算一个电路在其输入上的结果的程序作为

对于 “当” 方向, 我们可以使用与 定理 13.5 相同的 “循环展开” 技术来证明: 如果 是一个多项式时间 NAND-TM 程序, 那么对于任意 映射 可以由多项式大小的 NAND-CIRC 程序 计算.

13.6.3 一致性算法可以模拟非一致性算法吗?

定理 13.5 向我们展示了每个属于 的函数都属于 有人可能会问是否存在一个反向的关系. 假设有一个 满足对于每个 都有一个 “短的” NAND-CIRC 程序. 我们能说对于某些 “小的” 它一定在 中吗? 答案是坚决的 . 不仅 不包含在 中, 事实上, 中存在一些函数 无法计算.

定理 13.8 ( 包含不可计算函数).

存在一个 不可计算 函数 满足

定理 13.8 的证明思路

因为 对应于非一致性计算, 若对于每个 限制 在输入长度 上有一个小的电路/程序, 尽管对于不同的 这个电路/程序可能完全不同, 我们就说函数 属于 特别的, 如果对于所有相同长度的输入 函数 都满足 那么这意味着 要么是常函数 要么是常函数 因为常函数有一个 (非常!) 小的电路, 这样的函数 总是属于 (事实上属于一个更小的类). 然而通过规约停机问题, 我们可以得到一个具有上述性质但不可计算的函数.

定理 13.8 的证明

考虑如下定义的 “一元停机函数” 我们令 为这样一个函数: 接受输入 输出对应于数字 的二进制表示但不包含最高位 1 的字符串. 注意 是一个 满射. 对于所有 我们定义 即, 如果 的长度, 那么 当且仅当字符串 编码了一个在输入 上停机的 NAND-TM 程序.

是不可计算的, 因为如果 可被计算, 那么我们就可以通过将程序 转化为数字 满足 并运行 (换言之, 在长为 的全 串上的结果) 来计算 的结果. 另一方面, 对于所有 对于所有输入 总是为 或总是为 因此这个程序可以被一个常数行的 NAND-CIRC 程序计算.

这里的问题显然是 一致性. 对于一个函数 如果 属于 那么我们有 单一 的算法可以对于每个 计算 另一方面, 对于每个 可能都在 中, 但对每个输入长度使用完全不同的算法. 因此, 我们通常不将 用作 高效 计算的模型, 而是用来建模 低效计算. 例如, 在密码学中, 人们通常将一个加密方案是安全的定义为: 破解长度为 的密钥需要超过多项式数量的 NAND 行. 由于 这特别排除了用于破解的多项式时间算法, 但在密码学中使用非一致性模型还有技术上的原因. 它也允许用非渐进术语来谈论安全性, 例如方案具有 “ 位安全性”.

虽然这有时可能是一个真正的问题, 但在许多自然的背景下, 一致性和非一致性计算之间的差异似乎并不那么重要. 特别的, 在我们之前讨论的所有未知是否在 中的问题示例中: 最长路径, 3SAT, 因数分解等, 这些问题也都不知道是否在 中. 因此, 对于 “自然的” 函数, 如果你假装 大致等同于 你正确的概率通常比错误的要大.

PEXPPpolyrelationsfig

图 13.14. 之间的关系. 已知的是 包含不可计算函数 (特别的, 这些函数不属于 目前仍未知 是否成立, 虽然大部分人相信

13.6.4 一致性与非一致性计算: 总结

总而言之, 我们目前描述的两种计算模型是:

  • 一致性模型 (Uniform models): 图灵机, NAND-TM 程序, RAM 机器, NAND-RAM 程序, C/JavaScript/Python 等. 这些模型包含循环和无界内存, 因此单个程序可以计算具有无界输入长度的函数.

  • 非一致性模型 (Non-uniform models): 布尔电路直线程序 没有循环, 只能计算有限函数. 执行它们的时间恰好是它们包含的行数或门的数量.

对于一个函数 和某个良好的时间界限 我们知道:

  • 如果 在时间 内是一致可计算的, 那么存在一系列电路 其中 具有 个门并且对每个 计算 (即, 的限制).

  • 反之不一定成立 - 存在函数 的例子, 使得 甚至可以由常数大小的电路计算, 但 是不可计算的.

这意味着非一致性复杂度对于建立函数的 困难性 比建立其 容易性 更有用.

本章回顾

  • 我们可以使用 NAND-TM 程序定义函数的时间复杂度, 与可计算性的概念类似, 这似乎捕捉了函数的固有复杂度.

  • 有许多自然问题具有多项式时间算法, 也有其他我们很想解决的自然问题, 但其已知最好的算法是指数级的.

  • 多项式时间的定义, 以及由此产生的类 对模型的选择具有鲁棒性, 无论是图灵机, NAND-TM, NAND-RAM, 现代编程语言, 还是许多其他模型.

  • 时间层级定理表明, 有 一些 问题可以在指数时间内解决, 但不能在多项式时间内解决. 然而, 我们不知道本章节中描述的自然示例是否属于这种情况.

  • 通过 “循环展开”, 我们可以证明每个在时间 内可计算的函数都可以由一系列 NAND-CIRC 程序 (每个输入长度一个) 计算, 每个程序的大小至多为

13.7 习题

习题 13.1 ( 不同定义之间的等价性.).

证明在 定义 13.2 中定义的复杂性类 分别等价于 (如果 是一个集合的集族, 那么集合 是所有满足存在某个 的元素 的集合.)

习题 13.2 (表示的鲁棒性).

定理 13.1 表明类 对于计算模型的选择具有_鲁棒性_. 本练习表明这些类对于我们输入表示的选择也具有鲁棒性.

具体来说, 令 为一个将图映射到 的函数, 并令 为定义如下的函数. 对于每个

  • 当且仅当 通过邻接矩阵表示法表示一个图

  • 当且仅当 通过邻接表表示法表示一个图

证明 当且仅当

更一般地, 对于每个函数 关于 (或 的问题的答案在切换表示后保持不变, 只要一种表示转换为另一种表示可以在多项式时间内完成 (这基本上对所有合理的表示都成立).

习题 13.3 (布尔函数).

对于每个函数 定义 为一个将 映射到 的函数, 使得对于输入三元组 (的字符串表示), 其中

其中 是字符串 的第 位.

证明对于每个 当且仅当存在一个图灵机 和一个多项式 使得对于每个 在输入 上, 步内停机并输出

习题 13.4 (多项式时间的复合).

如果存在一个图灵机 和一个多项式 使得对于每个 在输入 上, 步内停机并输出 则称 (可能非布尔的) 函数 是_多项式时间可计算的_. 证明对于每一对多项式时间可计算的函数 它们的_复合_ (即满足 的函数 也是多项式时间可计算的.

习题 13.5 (指数时间的非复合性).

如果存在一个图灵机 和一个多项式 使得对于每个 在输入 上, 步内停机并输出 则称 (可能非布尔的) 函数 是_指数时间可计算的_. 证明存在某些 使得 都是指数时间可计算的, 但 不是指数时间可计算的.

习题 13.6 (非感知的图灵机).

我们称图灵机 非感知的, 如果存在某个函数 使得对于每个长度为 的输入 满足:

  • 如果 在输入 上停机所需的步数超过 那么在第 的读写头将位于位置 (注意该位置仅取决于 的_长度_而不取决于其内容.)

  • 如果 在第 步之前停机, 则

证明如果 那么存在一个 非感知的 图灵机 在多项式时间内计算 见脚注提示. 1

习题 13.7.

为这样一个函数: 对于表示三元组 的输入字符串, 其中 个顶点图 的邻接表表示, 且 中的数字, 如果图中存在边 在所有其他输入上输出

  1. 证明

  2. 为这样一个函数: 当输入为邻接矩阵 时, 当且仅当 表示的图是_平面图_ (即可以画在平面上且边互不交叉) 时输出 对于这个问题, 你可以直接使用 这一事实而无需证明. 证明 其中 是这样一个函数: 当输入为邻接表 时, 当且仅当 表示一个平面图时输出

习题 13.8 (评估 NAND 电路).

为这样一个函数: 对于每个表示二元组 的字符串, 其中 是一个 输入 输出的 NAND-CIRC (不是 NAND-TM!) 程序, 且 在所有其他输入上 输出

证明

习题 13.9 (寻找困难函数).

为这样一个函数: 对于表示二元组 的输入字符串, 其中

  • 对于某个

如果不存在至多 行的 NAND-CIRC 程序 能计算真值表为字符串 的函数 也就是说, 如果对于每个至多 行的 NAND-CIRC 程序 都存在某个 使得 其中 表示 的第 个坐标, 这里使用二进制表示将 与数字 对应起来.

  1. 证明

  2. (挑战) 证明存在一个算法 使得如果 足够大, 则 在时间 内运行并输出一个字符串 该字符串是一个不包含在 中的函数的真值表. (换句话说, 如果 输出的字符串, 那么如果我们令 为使得 输出 的第 个坐标的函数, 则 2

习题 13.10.

假设你负责 X 大学的计算机科学课程调度. 在 X 大学, 计算机科学系的学生起得很晚, 下午必须去忙他们的创业公司, 并且还要和投资人一起度过长周末. 所以你只有两个可能的时间段: 你可以将课程安排在周一-周三的上午 11 点到下午 1 点, 或者周二-周四的上午 11 点到下午 1 点.

为一个函数, 它接受一个课程列表 和一个_冲突_列表 (即不能共享同一时间段的课程对列表) 作为输入, 当且仅当 中的课程存在一个 “无冲突” 的调度方案 (即 中没有一对课程被安排在同一时间段) 时输出

更准确地说, 列表 是一个字符串列表 列表 是一个形式为 的配对列表. 当且仅当存在 的一个划分为两部分, 使得不存在 满足 都在同一部分中.

证明 像往常一样, 你不需要提供完整的代码来证明这一点, 可以高层次地描述操作, 也可以引用本书或讲座中提到的任何数据结构或其他结果. 注意, 要证明一个函数 中, 你需要同时 (1) 给出一个在多项式时间内计算 的算法 (2) 证明 确实在多项式时间内运行, 并且确实计算出正确的答案.

试着思考你的算法是否可以扩展到有三个可能时间段的情况.

13.8 参考文献

因为我们对给定长度输入的 最大 步数感兴趣, 我们定义的运行时间通常被称为 最坏情况复杂度. 计算长度为 的输入上的函数的 最小 步数 (或 “最好情况” 复杂度) 通常不是一个有意义的量, 因为本质上每个自然问题都会有一些极其简单的实例. 然而, 平均情况复杂度 (即 “典型” 或 “随机” 输入上的复杂度) 是一个有趣的概念, 我们将在讨论 密码学 时回到这个话题. 话虽如此, 最坏情况复杂度是复杂度度量中最标准和最基本的, 并且将是我们本书大部分内容的重点.

单带图灵机的一些下界在 (Maass, 1985) 中给出.

为了定义 演算中的效率, 人们需要对归约步骤的应用顺序保持谨慎, 这对计算效率可能很重要, 例如参见 这篇论文.

符号 的使用是出于历史原因. 它是由 Karp 和 Lipton 引入的, 他们认为这个类对应于可以由多项式时间图灵机计算的函数, 这些图灵机对于任何输入长度 都被赋予一个长度为 的多项式的 建议串.


1: 提示: 这是 定理 13.6 的图灵机类比. 我们将计算 的原始 TM 的一步替换为 非感知 TM 的一次 “扫描”, 在扫描中它向右移动 步, 然后向左移动 步.

2: 提示: 使用第 1 项, 需要指数级困难 NAND 程序的函数的存在性, 以及映射 的函数只有有限多个这一事实.