【问题标题】:Complexity does not match actual growth in running time? (python)复杂度与运行时间的实际增长不匹配? (Python)
【发布时间】:2015-12-15 21:36:26
【问题描述】:

我在 python 中运行了 2 个代码,然后测量了完成所需的时间。代码非常简单,只是递归最大值。这里是: 1.

def max22(L, left, right):
  if(left>=right):
    return L[int(left)]
  k = max22(L,left,(left+right-1)//2)
  p = max22(L, (right+left+1)//2,right)
  return max(k,p)


def max_list22(L):
  return max22(L,0,len(L)-1)

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

第一个应该在 O(logn) 中运行 (imo),第二个应该在 O(n*logn) 中运行。 但是,我测量了 n=1000 、 n=2000 和 n=4000 的运行时间, 不知何故,这两种算法的增长似乎都是线性的!这怎么可能?我弄错了复杂性,还是可以? 谢谢。

【问题讨论】:

  • 第一个算法不可能是 O(log n),因为它检查每个元素的值
  • 为什么你认为第二种算法是O(n logn)?
  • 嘿,至于第二个,我的导师告诉我们它是 O(n*logn)。
  • @ScottHunter 因为列表的每个元素都被复制了 O(log n) 次? (但我不确定python中没有使用引用计数等某种优化,那么它可能会更快(但它仍然是O(n log n)))

标签: python algorithm time-complexity complexity-theory


【解决方案1】:

第一个算法不是O(log n),因为它检查每个元素的值。可能显示是O(n)

至于第二个,可能你只是没有注意到 n 和 nlogn 在这么小的尺度上的区别。

【讨论】:

  • 是的,我也对这个有疑问。但是,计算每个递归调用的复杂度,然后将其乘以树的高度,难道不是正确的方法吗?
  • @Bar 计算这些复杂性的正确方法是表达 T(n) 的递归关系并应用 Master Theorem。您的第一个是T(n) = 2T(n/2) + O(1),它产生O(n) 运行时间(实际上,它是Theta 而不是O)。第二次重复是T(n) = 2T(n/2) + O(n),因为切片需要O(n) 工作,产生O(n log n) 运行时间。
【解决方案2】:

仅仅因为一个函数将搜索空间除以 2,然后递归地查看每一半并不意味着它在复杂性中具有 log(n) 因素。

在您的第一个解决方案中,您将搜索空间除以 2,但最终检查每一半中的 每个 元素。与丢弃一半搜索空间的二进制搜索不同,您正在检查两半。这意味着搜索中不会丢弃任何内容,最终您最终会查看每个元素,从而使您的复杂度为 O(n)。您的第二个实现也是如此。

【讨论】:

    【解决方案3】:

    您的第一个算法在普通机器上是 O(n),因此您的测试表明这一点并不奇怪。您的第二个算法是 O(n*log in),但如果您使用正确的数组而不是列表,它将是 O(n)。由于 Python 内置列表操作非常快,您可能还没有遇到对数减速;尝试使用更像n=4000000 的值,看看你会得到什么。

    请注意,如果您可以并行运行两个递归调用(使用 O(1) 切片),则两种算法都可以在 O(log n) 时间内运行。当然,你需要 O(n) 个处理器来做到这一点,但如果你设计的是芯片,而不是编写程序,那么这种缩放将很简单......

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-13
      • 1970-01-01
      相关资源
      最近更新 更多