【问题标题】:Recursive call complexity递归调用复杂度
【发布时间】:2013-12-07 09:48:05
【问题描述】:

我很难证明这两个递归代码的复杂性。我对他们有一些直觉。我认为第一个是 O(n),第二个是 O(nlogn)。这些是代码:

def max_v1(lst):
    if len(lst)== 1:
        return lst[0]
    return max(lst[0], max_v1(lst[1:]))


def max_v2(lst):
    if len(lst)== 1:
        return lst[0]
    l = max_v2(lst[:len(lst)//2])
    r = max_v2(lst[len(lst)//2:])
    return max(l, r)

【问题讨论】:

  • 这真的取决于你是在谈论算法,还是实现。第一个的实现实际上是 O(N*N)!在实现中,但该算法可以递归编码,几乎与 O(N) 中的相同。原因是因为在python中,一个列表切片(lst[1:])其实本身就是一个O(N-1)操作。
  • @mgilson:小心那个感叹号。尽管它在括号之外,但它看起来仍然很像阶乘。
  • @user2357112 -- 很公平。我把它放在那里是为了强调。不幸的是,我的 5 分钟编辑窗口现在消失了……:-/
  • 我在谈论一个长度为 N 的列表的时间复杂度。我使用了一个递归树,发现深度是 O(n),现在由于切片它对每个级别进行 O(n- 1) 还是 O(1)?
  • @user2751595 切片是线性时间操作,而不是恒定时间。您正在为每个O(n) 级别做O(n) 工作。特别是第一个的复杂性大约是O(1 + 2 + 3 + 4 + ...+ n-1) = O(n(n-1)/2) = O(n^2)。第二个是O(nlog(n)),因为您正在为每个O(log(n)) 级别进行O(n) 工作。如果您使用startend 指针,则两个解决方案都将是O(n),但第二个解决方案将使用更少的堆栈深度,而第一个解决方案对于长于1000 的列表将失败(由于默认递归限制) .

标签: python recursion complexity-theory


【解决方案1】:
def max_v1(lst):
    if len(lst)== 1:
        return lst[0]
    return max(lst[0], max_v1(lst[1:]))

max(...) 是 O(2),每个 max_v1 函数调用调用一次,但是每个函数调用的切片是 O(N)。因此,我们得到 O(2*N*N) = O(N2)。

def max_v2(lst):
    if len(lst)== 1:
        return lst[0]
    l = max_v2(lst[:len(lst)//2])
    r = max_v2(lst[len(lst)//2:])
    return max(l, r)

max_v2 每次调用max_v2 函数调用两次,每次传递一半的list,由于切片和函数调用,导致 O(2*N*log(N))。 max() 每次函数调用都会调用一次,因此总共会给出 N*O(2) = O(N)。因此,我们得到 O(N+2*N*log(N)) = O(N*log(N))。

【讨论】:

  • 由于只有两个元素被传递给 max(...),我认为在第一个示例中,'max' 应该花费 O(2),即恒定时间。如果我错了,请纠正我。
  • @sk4x0r 谢谢,你是对的。我进行了编辑并修复了它。不过,第一个仍然是 O(N^2)。
猜你喜欢
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-19
  • 2014-03-14
  • 2017-11-20
  • 1970-01-01
相关资源
最近更新 更多