03. 算法复杂度分析与计算复杂性理论
1. 函数的增长与渐进分析 (Growth of Functions and Asymptotic Analysis)
在算法分析中,我们关心的是当输入规模
1.1 核心渐进符号: , ,
这三个符号是评估算法复杂度的基石,是考试的绝对重点。它们的定义和区别必须清晰掌握。
| 符号 (Notation) | 名称 (Name) | 含义 (Meaning) | 数学定义 (Definition) | 通俗理解 |
|---|---|---|---|---|
| 大 O (Big-O) | 增长上界 (Upper Bound) | 存在正常数 | ||
| 大 Omega (Big-Omega) | 增长下界 (Lower Bound) | 存在正常数 | ||
| 大 Theta (Big-Theta) | 紧确界 (Tight Bound) |
难点详述:如何理解和使用大 O 符号?
大 O 符号是三者中最常用的,因为它描述了算法性能的“最坏情况”保证。
-
核心思想:我们想给函数
的增长率找一个“天花板” 。只要 足够大( ), 就永远不会超过 的某个常数倍( )。 -
清晰用例:证明
是 。 - 目标:我们需要找到正常数
和 ,使得当 时,不等式 恒成立。 - 推导:
为了让不等式成立,我们可以对左侧进行放缩。假设
: 因此,
- 结论:我们找到了
和 。当 时, 成立。因此,根据定义, 。
- 目标:我们需要找到正常数
-
重要结论:对于一个多项式
,其增长率由最高次项决定。
1.2 常见函数增长阶
**必须熟记**以下函数的增长速度排序,这对于快速比较算法优劣至关重要。
慢
: 常数时间 (Constant) : 对数时间 (Logarithmic) : 线性时间 (Linear) : 多项式时间 (Polynomial) : 指数时间 (Exponential) : 阶乘时间 (Factorial)
2. 算法的复杂度 (Complexity of Algorithms)
算法的复杂度是衡量其效率的度量,主要分为时间复杂度和空间复杂度。
- 时间复杂度 (Time Complexity): 算法执行所需的基本操作次数。
- 空间复杂度 (Space Complexity): 算法执行所需的内存空间量。
2.1 复杂度分析类型
对于同一个算法,输入数据的不同特性可能导致其运行时间产生巨大差异。因此,我们从不同角度进行分析。
| 分析类型 | 定义 | 关注点 | 示例:插入排序 (Insertion Sort) |
|---|---|---|---|
| 最佳情况 (Best-case) | 算法运行时间最短的输入情况。 | 理想条件下的性能极限。 | 输入数组已排序。只需 |
| 最坏情况 (Worst-case) | 算法运行时间最长的输入情况。 | 提供性能下限保证,是实际应用中最受关注的指标。 | 输入数组逆序。每次插入都需要与所有已排序元素比较和交换。复杂度为 |
| 平均情况 (Average-case) | 在所有可能的输入上,算法的期望运行时间。 | 算法在“典型”情况下的表现。 | 输入数组是随机排列的。期望复杂度为 |
重点:在没有特别说明的情况下,我们讨论的“复杂度”通常指最坏情况时间复杂度。
3. 问题的计算复杂性 (Computational Complexity of Problems)
计算复杂性理论研究的是问题本身的难易程度,而非解决某个问题的特定算法。
3.1 核心概念:输入规模 (Input Size)
这是一个非常关键且容易混淆的概念。
-
定义:问题的输入规模 (Input Size) 是指用于编码该问题实例所需的比特数。
-
陷阱与辨析:算法的复杂度是关于输入规模的函数,而不是输入值的大小。
-
循循善诱的例子:判断合数问题 (COMPOSITE)
- 问题:给定一个正整数
,判断它是否为合数(即能否被大于 1 且小于自身的整数整除)。 - 朴素算法:从
到 逐个尝试,看 是否能整除 。这个算法的计算步骤大约是 次除法。 - 初看之下:这个算法是
,似乎是多项式时间,效率很高。 - 深入分析:这是错误的看法!
- 计算机接收的输入
是以二进制形式表示的。表示一个整数 需要的比特数,即输入规模,是 。 - 因此,算法的运行时间
如果用输入规模 来表示,就是 。 - 结论:这个算法的时间复杂度是输入规模
的指数函数,而不是多项式函数。因此,它是一个低效的算法。
- 计算机接收的输入
- 问题:给定一个正整数
3.2 复杂性类 (Complexity Classes)
复杂性理论将判定问题(回答是/否的问题)划分为不同的类别。
-
P 类 (Class P - Polynomial time)
- 定义:所有可以由一个确定性算法在多项式时间内解决的判定问题集合。
- 通俗理解:这类问题被认为是**“易解的” (Tractable)**。
- 示例:排序问题、查找最大值、判断一个数是否为素数(已于 2002 年被证明)。
-
NP 类 (Class NP - Nondeterministic Polynomial time)
- 定义:如果一个问题的“是”解存在一个**“证明” (Certificate/Witness),并且这个证明可以在多项式时间内被验证 (Verify)**,那么该问题就属于 NP 类。
- P 与 NP 的核心区别:
- P 类:找到解很快。
- NP 类:验证解很快。(但找到解可能非常慢)
- 用例:旅行商问题 (TSP) 的判定版本
- 问题:给定
个城市和它们之间的距离,是否存在一条总长度小于 的回路,恰好访问每个城市一次? - 证明 (Certificate):一条具体的路径(如 A -> C -> B -> D -> A)。
- 验证 (Verification):给定这条路径,计算其总长度并与
比较。这个验证过程是多项式时间的(线性时间)。 - 结论:TSP 问题属于 NP 类。但是,目前没有人知道如何在多项式时间内找到这样一条路径。
- 问题:给定
-
P 与 NP 问题 (P vs. NP Problem)
- 已知:
。因为如果一个问题能在多项式时间内解决,那么它的解自然也能在多项式时间内被验证。 - 核心悬念:
是否成立? 这是计算机科学领域最重要、最著名的未解难题。大多数科学家相信 。
- 已知:
-
NP-完全 (NP-Complete, NPC) 和 NP-难 (NP-Hard)
- NP-完全 (NPC):
- 它是一个 NP 问题。
- 它是 NP 中最难的问题。任何其他 NP 问题都可以在多项式时间内“归约”到它。
- 意义:如果任何一个 NPC 问题被发现在多项式时间内可解,那么所有 NP 问题都可以在多项式时间内解决,即
。
- NP-难 (NP-Hard):
- 定义:一个问题至少和任何 NPC 问题一样难。它不一定是 NP 问题。
- 实践指导:当你在工作中遇到的问题被证明是 NPC 或 NP-Hard 时,通常意味着你应该放弃寻找一个完美的、高效的通用解法,转而寻求近似解、启发式算法或针对特定情况的特解。
- NP-完全 (NPC):