1. 猜测上限函数 f(n)。 在这个例子中,f(n) = n log n。 2. 构造一个合适的常数 c。 我们想证明 T(n) ≤ c n log n。 3. 利用数学归纳法证明。 基本情况 (Base Case): 对于某个小的 n (比如 n=1, n=2),直接计算 T(n) 的值,然后检查 T(n) ≤ c f(n) 是否成立。 假设 T(1) = 1。那么我们想要 1 ≤ c 1 log 1。这里 log 1 是 0,所以这不太好处理。我们通常会选择一个可以处理的最小 n,比如 n=2,假设 T(2) = 4。我们想证明 T(2) ≤ c 2 log 2。如果 c=2,22log 2 = 4log 2 ≈ 2.77。那么 4 ≤ 2.77 不成立。我们需要调整 c。 通常处理递推式时,我们会假设 n 是某个数(比如2的幂次),这样 n/2, n/4 等等都还是整数。 让我们换个思路,找到一个更合适的 c 来开始。 我们猜测 T(n) = n log₂ n。带入递推式: n log₂ n = 2 ((n/2) log₂ (n/2)) + n n log₂ n = n (log₂ n log₂ 2) + n n log₂ n = n (log₂ n 1) + n n log₂ n = n log₂ n n + n n log₂ n = n log₂ n 这说明 T(n) = n log₂ n 恰好是这个递推式的解(假设基础情况合适的话)。这暗示了我们的大O估算会非常接近。
归纳假设 (Inductive Hypothesis): 假设对于所有 k < n,都有 T(k) ≤ c k log k。 归纳步骤 (Inductive Step): 我们要证明 T(n) ≤ c n log n。 根据递推关系:T(n) = 2 T(n/2) + n 应用归纳假设于 T(n/2):T(n/2) ≤ c (n/2) log (n/2) 代入:T(n) ≤ 2 [c (n/2) log (n/2)] + n T(n) ≤ c n log (n/2) + n T(n) ≤ c n (log n log 2) + n T(n) ≤ c n log n c n log 2 + n 我们想让这个结果 ≤ c n log n。所以我们需要: c n log n c n log 2 + n ≤ c n log n c n log 2 + n ≤ 0 n ≤ c n log 2 1 ≤ c log 2 c ≥ 1 / log 2 我们还需要处理一个问题: 归纳假设是 T(k) ≤ c k log k。但我们的递推式是 T(n) = 2 T(n/2) + n。当 n 变小时,T(n/2) 的系数是 2,而 T(n) 的“加项”是 n。在处理常数项时,比如 T(n) ≤ c n log n,可能会出现一些“余数”。 更严谨的做法: 为了处理 T(n) = 2 T(n/2) + n,通常会选择一个比 n log n 稍大一点的函数作为上限,或者在 c 的选择上更宽松一些。 让我们回到 T(n) ≤ c n log n。我们需要证明 c n log n c n log 2 + n ≤ c n log n。 调整策略: 我们设 T(n) ≤ c n log n + d n (引入一个线性项来抵消 +n)。 假设 T(n) ≤ c n log n。 T(n) = 2 T(n/2) + n T(n) ≤ 2 [c (n/2) log (n/2)] + n T(n) ≤ c n (log n 1) + n T(n) ≤ c n log n c n + n 我们希望这个 ≤ c n log n。那么我们需要 c n + n ≤ 0,即 n ≤ c n,这要求 c ≥ 1。 但是,我们还剩下 c n log 2 这个项没有处理好。这个项是被减去的,所以我们希望它能被抵消。 更好的方法: 我们想要 T(n) ≤ c n log n。 T(n) = 2 T(n/2) + n 假设 T(k) ≤ c k log k 对于 k < n。 T(n) ≤ 2 (c (n/2) log (n/2)) + n T(n) ≤ c n (log n log 2) + n T(n) ≤ c n log n c n log 2 + n 我们希望这个整体小于等于 c n log n。所以需要: c n log 2 + n ≤ 0 n ≤ c n log 2 1 ≤ c log 2 c ≥ 1 / log 2 另外,我们还要处理基础情况。 如果 T(1)=1,我们希望 T(1) ≤ c 1 log 1 是不行的。我们通常会把基础情况的范围扩大一些,或者处理一个稍微不同的函数形式。 比如,我们尝试证明 T(n) ≤ c n log₂ n for n ≥ 2. 假设 T(2) = C. 我们想要 C ≤ c 2 log₂ 2 = 2c. 所以 c ≥ C/2. 回到归纳步骤:T(n) ≤ c n log n c n log 2 + n. 我们希望这个结果是 ≤ c n log n。 现在我们看到问题了: T(n) = 2 T(n/2) + n。如果你直接用 T(n/2) ≤ c (n/2) log (n/2),会得到 c n log (n/2) = c n (log n 1) = c n log n c n。再加上那个 +n,就变成了 c n log n c n + n。 为了让这个东西小于 c n log n,我们需要 cn + n ≤ 0,也就是 n ≤ cn, c ≥ 1。 关键来了: 我们还有一个 c n log 2。这个是在推导过程中产生的“剩余项”。 让我们换个思路,选择一个更大的函数作为上限。 比如,我们知道 T(n) = O(n²)。所以我们选择 f(n) = n² 作为上限来验证。 T(n) = 2 T(n/2) + n 假设 T(k) ≤ c k² for k < n. T(n) ≤ 2 (c (n/2)²) + n T(n) ≤ 2 (c n²/4) + n T(n) ≤ c n²/2 + n 我们想要证明 T(n) ≤ c n²。所以我们需要 c n²/2 + n ≤ c n²。 n ≤ c n²/2 1 ≤ c n / 2 n ≥ 2/c 只要 c 是一个正数,并且我们选择的 n₀ 大于 2/c,这个不等式就成立。例如,选择 c=1。那么 n ≥ 2。 基本情况: T(1)=1。我们想证明 T(1) ≤ 1 1² = 1。成立。 所以,T(n) = O(n²) 是一个有效的上限。
回到 O(n log n) 的证明: T(n) = 2 T(n/2) + n 我们来证明 T(n) ≤ c n log n 对于某个 c 和 n ≥ n₀。 为了避免 log(n/2) 的问题,我们通常会在归纳假设上做点手脚,或者调整 c。 设我们想证明 T(n) ≤ c n log₂ n。 T(n) = 2 T(n/2) + n. 假设 T(k) ≤ c k log₂ k d k for k < n. (引入一个负的线性项) T(n) ≤ 2 (c (n/2) log₂ (n/2) d (n/2)) + n T(n) ≤ c n (log₂ n 1) d n + n T(n) ≤ c n log₂ n c n d n + n T(n) ≤ c n log₂ n + (1 c d) n 我们希望这个整体小于等于 c n log₂ n。所以需要 (1 c d) n ≤ 0。 这就要求 1 c d ≤ 0,即 c + d ≥ 1。 基础情况: 假设 n 是2的幂次。 T(2) = 2 T(1) + 2. 假设 T(1)=1. T(2)=4. 我们需要 T(2) ≤ c 2 log₂ 2 = 2c. 所以 c ≥ 2. 我们也要满足 T(k) ≤ c k log₂ k d k。 T(1)=1. 我们需要 1 ≤ c 1 log₂ 1 d 1 = d. 所以 d ≤ 1. 现在代入 c + d ≥ 1 的条件。如果 c = 2, d = 1. 那么 c + d = 1. 满足条件。 所以,我们可以选择 c = 2,d = 1。 证明 T(n) ≤ 2n log₂ n n 对于 n ≥ 2。 T(n) ≤ 2 T(n/2) + n T(n) ≤ 2 (2 (n/2) log₂ (n/2) n/2) + n (归纳假设 T(k) ≤ 2k log₂ k k) T(n) ≤ 2 (n log₂ (n/2) n/2) + n T(n) ≤ 2 (n (log₂ n 1) n/2) + n T(n) ≤ 2 (n log₂ n n n/2) + n T(n) ≤ 2 (n log₂ n 3n/2) + n T(n) ≤ 2n log₂ n 3n + n T(n) ≤ 2n log₂ n 2n 这证明了 T(n) ≤ 2n log₂ n 2n. 因为 2n log₂ n 2n ≤ 2n log₂ n (当 n ≥ 1 时,2n ≤ 0),所以 T(n) ≤ 2n log₂ n 成立。 最终结论: T(n) 是 O(n log n)。
方法三:使用主定理(适用于特定形式的递推关系)
如果你的递推关系是 T(n) = a T(n/b) + f(n) 的形式,其中 a ≥ 1, b > 1,并且 f(n) 是一个单调函数,那么可以使用主定理。主定理有三个情况:
情况 1: 如果 f(n) = O(n^(log_b a ε)) 对于某个 ε > 0,那么 T(n) = Θ(n^(log_b a))。 情况 2: 如果 f(n) = Θ(n^(log_b a) log^k n) 对于某个 k ≥ 0,那么 T(n) = Θ(n^(log_b a) log^(k+1) n)。 情况 3: 如果 f(n) = Ω(n^(log_b a + ε)) 对于某个 ε > 0,并且 a f(n/b) ≤ c f(n) 对于某个常数 c < 1 和足够大的 n,那么 T(n) = Θ(f(n))。
示例: T(n) = 2 T(n/2) + n a = 2, b = 2, f(n) = n log_b a = log₂ 2 = 1 我们将 f(n) = n 与 n^(log_b a) = n¹ 比较。 f(n) = Θ(n¹) 并且 log^k n 这里的 k=0。 这符合主定理的情况 2,k=0。 所以,T(n) = Θ(n^(log_b a) log^(k+1) n) = Θ(n¹ log^(0+1) n) = Θ(n log n)。 主定理直接给出了精确估算!
使用主定理的优势是快,但前提是递推关系符合其形式。如果不是,你就得回到数学归纳法或者其他方法。
第三问:证明下限(大Ω符号)
证明一个算法的时间复杂度是 Ω(g(n)),意思就是说,当 n 足够大的时候,算法的实际运行时间 T(n) 不会小于一个常数 c 乘以 g(n)。数学上写出来就是:
T(n) ≥ c g(n),对于所有 n ≥ n₀。
这里的 c 和 n₀ 都是大于零的常数。
怎么证明呢?方法和证明上限有点类似,但目标是找到一个“不低于”某个函数的界。
方法一:直接代入和替换(适用于直接给出演示式的情况)
如果 T(n) = 3n² + 5n + 10,你想证明它是 Ω(n²)。
1. 找出你想要证明的下限函数 g(n)。 在这个例子里,g(n) = n²。 2. 分析 T(n) 的主要增长项。 n² 是主导项。 3. 寻找一个合适的常数 c 和起始点 n₀。 我们想证明 T(n) ≥ c n²。 把表达式代进去:3n² + 5n + 10 ≥ c n² 为了让不等式成立,我们希望主要项(3n²)能够“压住”常数项和低次项,并且确保 c 至少是主要项系数的一部分。 考虑 n ≥ 1。 3n² 这一项就是 n² 的3倍。 5n 这一项,当我们 n 足够大的时候,会小于 3n²。例如,如果 n ≥ 1,那么 5n ≤ 5n²。 常数项 10,当我们 n 足够大的时候,也会小于 3n²。例如,如果 n ≥ 4,10 ≤ 3n² (316=48)。 所以,我们尝试选择 c,让它小于等于主项的系数。比如 c = 1。 我们需要证明 3n² + 5n + 10 ≥ 1 n²。 => 2n² + 5n + 10 ≥ 0. 对于所有 n ≥ 1,这个不等式显然成立。 结论: 选择 c = 1,n₀ = 1。对于所有 n ≥ 1,T(n) = 3n² + 5n + 10 ≥ 1n²。因此,T(n) 是 Ω(n²)。
证明思路总结: 1. 找到你想要证明的下限函数 g(n)。 2. 将 T(n) 的各项与 c g(n) 进行比较。 3. 通过调整常数 c 和确定一个合适的起始点 n₀,使得对于所有 n ≥ n₀,T(n) ≥ c g(n) 成立。通常是保留主导项,而忽略其他项(或将其设为非负)。
方法二:利用递推关系和数学归纳法(适用于递推式)
T(n) = 2 T(n/2) + n,你想证明 T(n) 是 Ω(n log n)。
1. 猜测下限函数 g(n)。 在这个例子中,g(n) = n log n。 2. 构造一个合适的常数 c。 我们想证明 T(n) ≥ c n log n。 3. 利用数学归纳法证明。 基本情况 (Base Case): 比如 T(2) = 4。我们需要 4 ≥ c 2 log₂ 2 = 2c。所以 c ≤ 2。我们选择 c = 1。 归纳假设 (Inductive Hypothesis): 假设对于所有 k < n,都有 T(k) ≥ c k log k。 归纳步骤 (Inductive Step): T(n) = 2 T(n/2) + n 应用归纳假设于 T(n/2):T(n/2) ≥ c (n/2) log (n/2) 代入:T(n) ≥ 2 [c (n/2) log (n/2)] + n T(n) ≥ c n log (n/2) + n T(n) ≥ c n (log n log 2) + n T(n) ≥ c n log n c n log 2 + n 我们想让这个结果 ≥ c n log n。所以我们需要: c n log n c n log 2 + n ≥ c n log n c n log 2 + n ≥ 0 n ≥ c n log 2 1 ≥ c log 2 c ≤ 1 / log 2 这个结果和我们之前猜测 c=1 矛盾了! 这说明直接证明 T(n) ≥ c n log n 时,选择 c=1 可能是不够的。 让我们换个思路,调整我们对递推关系的理解,或者调整 c。 我们知道 T(n) = 2 T(n/2) + n。 如果我们忽略 +n 这个项,那么 T(n) ≈ 2 T(n/2),这会是 n log n 的级别。但是 +n 这个项会增加复杂度。 再次回到主定理: T(n) = 2 T(n/2) + n. log_b a = 1. f(n) = n. 主定理情况 2:f(n) = Θ(n^(log_b a) log^k n) with k=0. T(n) = Θ(n^(log_b a) log^(k+1) n) = Θ(n¹ log¹ n) = Θ(n log n)。 主定理直接给出了精确估算,所以 T(n) = Ω(n log n) 也是自然成立的。
如果不用主定理,手动证明 Ω(n log n): T(n) = 2 T(n/2) + n 我们想证明 T(n) ≥ c n log n. 考虑一个弱化版的归纳假设, 让它更容易满足。比如,我们知道 T(n) 的增长至少和 n 成正比,因为每次递归都至少消耗 O(n) 的时间。 T(n) = 2 T(n/2) + n. 如果我们想证明 T(n) ≥ c n log n. T(n) ≥ 2 T(n/2) + n. 假设 T(n/2) ≥ c (n/2) log (n/2). T(n) ≥ 2 [c (n/2) log (n/2)] + n = c n (log n log 2) + n = c n log n cn log 2 + n. 我们需要 c n log n cn log 2 + n ≥ c n log n. 这要求 n ≥ cn log 2. => 1 ≥ c log 2. => c ≤ 1/log 2. 这个结果表明,我们选择的 c 值不能太大。 让我们尝试一个更简单的下限,比如 Ω(n)。 T(n) = 2 T(n/2) + n. 假设 T(k) ≥ c k for k < n. T(n) ≥ 2 (c (n/2)) + n T(n) ≥ cn + n T(n) ≥ (c+1) n. 只要 c ≥ 0,这个就成立。选择 c=1, n₀=1. T(n) ≥ n. 所以 T(n) 是 Ω(n)。
回到 Ω(n log n) 的证明: 我们看到上面使用 T(n) ≥ c n log n 的归纳法,在 n ≥ cn log 2 这个条件上卡住了。 一个常见的技巧是“反向”或者使用更宽松的松弛条件。 T(n) = 2 T(n/2) + n. 我们知道 T(n/2) ≥ c (n/2) log (n/2). 但是,如果 n/2 本身还不是我们定义 log 的那个范围呢? 核心问题在于,T(n) = 2 T(n/2) + n 这个“+n”项,虽然是线性的,但在对数增长的背景下,它非常关键。 它使得 T(n) 的增长速度比纯粹的 n log n 的“常数”要大一些。 重新思考 T(n) 的展开: T(n) = 2 T(n/2) + n T(n) = 2 (2 T(n/4) + n/2) + n = 4 T(n/4) + n + n T(n) = 4 (2 T(n/8) + n/4) + 2n = 8 T(n/8) + n + n + n T(n) = 2^k T(n/2^k) + k n 当 n/2^k = 1 (即 k = log₂ n) 时: T(n) = 2^(log₂ n) T(1) + n log₂ n = n T(1) + n log₂ n 假设 T(1)=1. T(n) = n + n log₂ n. 这几乎就是 n log n 了! 这说明了,当我们展开递推式时,底层的 +n 项累积起来就是 n log n。 所以,要证明 T(n) ≥ c n log n,我们只需要找到一个合适的 c 和 n₀。 T(n) = n log₂ n + n 我们需要 n log₂ n + n ≥ c n log₂ n. n ≥ (c1) n log₂ n 1 ≥ (c1) log₂ n 如果 c > 1,比如 c=2,那么 1 ≥ log₂ n,这是在 n ≤ 2 时成立的。对于更大的 n,不等式就不成立了。 所以,c 必须小于等于 1。 如果我们选择 c = 1/2,那么 1 ≥ (1/2 1) log₂ n = 1/2 log₂ n。这个对所有 n ≥ 1 都成立。 结论: T(n) = n log₂ n + n。 我们选择 c = 1/2, n₀ = 2 (为了让 log₂ n 有意义且非负)。 T(n) = n log₂ n + n ≥ (1/2) n log₂ n + n. 我们需要 (1/2) n log₂ n + n ≥ (1/2) n log₂ n. 即 n ≥ 0。 所以,T(n) 是 Ω(n log n)。