You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

递归子程序printStuff的asymptotic running time疑问:常规计算与Master Theorem推导结果矛盾

递归子程序printStuff的asymptotic running time疑问:常规计算与Master Theorem推导结果矛盾

嘿,我来帮你拆解这个矛盾点,你遇到的问题大概率是对递推式的子数组长度判断有误,或是误用了主定理的适用场景,咱们一步步理清楚:

首先得先把核心的递推关系搞准确,这是解决矛盾的关键:

先澄清两种可能的递归场景

你描述里的数组切片表述有点模糊,这直接影响了递推式的正确性,我们分两种情况分析:

场景1:递归处理两个长度为n/4的子数组(真正的“前四分之一”和“最后四分之一”)

如果递归调用的是A[:n/4](前四分之一,长度n/4)和A[3n/4:](最后四分之一,长度n/4),那递推式是:
T(n) = 2*T(n/4) + O(n)(n>4时,O(n)是遍历整个数组的耗时)

用主定理推导

主定理的标准形式是T(n) = a*T(n/b) + f(n),这里a=2(两个递归子问题),b=4(子问题是原问题的1/4大小),所以n^(log_b a) = n^(log₄2) = n^(1/2)
而f(n)=O(n),显然n的增长速度远快于n^(1/2),满足主定理第三类情况的条件:

  • f(n) = Ω(n^(log_b a + ε)),这里取ε=0.5即可满足
  • 正则条件:2f(n/4) = 2(n/4) = n/2 ≤ c*f(n),取c=1/2<1,完全符合
    所以根据主定理,T(n)=O(f(n))=O(n)

用递归树常规展开验证

咱们把每一层的耗时加起来:

  • 第1层(根节点):耗时n
  • 第2层:2个节点,每个耗时n/4,总耗时2*(n/4)=n/2
  • 第3层:4个节点,每个耗时n/16,总耗时4*(n/16)=n/4
  • ...
  • 第k层:2(k-1)个节点,每个耗时n/(4(k-1)),总耗时2(k-1)*(n/4(k-1))=n/(2^(k-1))

递归终止条件是子数组长度≤4,所以递归深度k≈log₄n = (log₂n)/2,是O(logn)级别。
把所有层的耗时求和:这是首项为n、公比为1/2的等比数列,求和结果是n*(1 - (1/2)^k)/(1-1/2) ≈ 2n,也就是O(n),和主定理结果完全一致。

场景2:递归处理一个长度3n/4和一个长度n/4的子数组(你的切片描述可能有误)

如果你实际的递归调用是A[n/4:](从n/4到末尾,长度3n/4)和A[3n/4:](从3n/4到末尾,长度n/4),那递推式是:
T(n) = T(3n/4) + T(n/4) + O(n)(n>4时)

用递归树常规展开

这时候每一层的总耗时都是n:

  • 第1层:耗时n
  • 第2层:T(3n/4)耗时3n/4,T(n/4)耗时n/4,总耗时3n/4 + n/4 =n
  • 第3层:T(3n/4)的两个子问题耗时9n/16 +3n/16=3n/4,T(n/4)的两个子问题耗时3n/16 +n/16=n/4,总耗时还是n
  • ...

递归深度由最长的那条链决定:每次取3n/4的子数组,直到长度≤4,深度k≈log_{4/3}n,是O(logn)级别。
总耗时就是n * log_{4/3}n = O(n logn),这就是你之前常规计算得到的结果。
注意:这种情况主定理的标准形式不适用,因为主定理要求所有递归子问题的大小相同,而这里两个子问题大小不同,所以不能硬套主定理,这就是你之前矛盾的根源!

总结矛盾的原因

你之前的矛盾是因为把两种不同场景的计算方法混在了一起:误将场景2的递推式套用到了主定理(主定理只适用于场景1的等大小子问题),同时用场景2的递归树展开得到了O(n logn),才出现了结果不一致的情况。

现在你只需要对照实际代码里的递归调用,就能确定正确的时间复杂度:

  • 如果是处理两个等长的1/4子数组:结果是O(n)
  • 如果是处理一个3/4长度和一个1/4长度的子数组:结果是O(n logn)

备注:内容来源于stack exchange,提问作者elecTron

火山引擎 最新活动