【问题标题】:Python heapq vs. sorted complexity and performancePython heapq vs. sorted 复杂度和性能
【发布时间】:2014-08-31 05:47:58
【问题描述】:

我对 python 比较陌生(使用 v3.x 语法),希望了解有关 heapq 与 sorted 的复杂性和性能的说明。

我已经为贪婪的“找到最佳工作计划”算法实现了一个基于 heapq 的解决方案。但后来我了解了将“排序”与 operator.itemgetter() 和 reverse=True 一起使用的可能性。

遗憾的是,我找不到任何关于“排序”与 heapq 的预期复杂性和/或性能的解释。

【问题讨论】:

  • 尚不清楚您到底想做什么。如果您正在寻找列表中最大的东西,max(L) 将比创建堆更快,并且比调用 sorted 快得多。
  • @DanRoche,如果这是一个愚蠢的问题,我们深表歉意,但是有没有办法从具有 max(L) 的集合中删除/弹出?
  • 如果L 是一个列表,您可以通过L.remove(max(L)) 删除最大的元素。这有点浪费,因为它只需要一次就进行两次,但它仍然应该比构建堆更快。

标签: python performance sorting heap complexity-theory


【解决方案1】:

如果你使用二进制堆按顺序弹出所有元素,你所做的事情基本上是heapsort。它比sorted function中的排序算法慢,除了它的实现是纯python。

heapqsorted 更快,以防您需要即时添加元素,即添加和插入可能以未指定的顺序出现。在任何堆中添加保持内部顺序的新元素比在每次插入后重新排列数组更快。

如果您以后需要按顺序检索所有元素,sorted 会更快。

他们可以竞争的唯一问题 - 如果您需要集合中最小(或最大)元素的一部分。尽管there are special algorigthms for that caseheapqsorted 是否会更快取决于初始数组的大小和您需要提取的部分。

【讨论】:

  • 在这种情况下,两者都经过完美优化(我假设)并且您所说的一切都是正确的。但是,如果我想测试复杂性和性能以确保哪个比另一个更快,我该怎么做?
  • Rules of Optimization 1. 不要 2. 不要……暂时。 3. 简介先。我对 a) 创建一个包含 10,000 个随机数的列表并在其上调用 sorted 和 b) 创建 10,000 个更多数字并使用 heapq.heappush 构建列表的快速而肮脏的分析产生了 28% 的时间差异。这听起来令人印象深刻,直到您看到数量级:每个元素大约 230 纳秒(哪种算法?我发现很难找到该选择占主导地位的情况。)。
【解决方案2】:

heapqnlargest()nsmallest() 函数在您尝试查找相对较少的项目时最合适。如果您只想找到单个最小或最大数字, min() 和 max() 最合适,因为它更快并且使用 sorted 然后切片。如果您正在寻找 N 个最小或最大的项目,并且 N 与集合的整体大小相比较小,则这些函数可提供卓越的性能。虽然没有必要在代码中使用 heapq,但这只是一个有趣的话题,值得研究。

【讨论】:

    【解决方案3】:

    heapq 实现为二进制堆, 关于二进制堆以及扩展heapq的关键事项:

    1. 不支持搜索
    2. 平均而言,插入是恒定时间
    3. 平均删除时间 O(log n) 时间

    此处描述的其他二进制堆信息:http://en.wikipedia.org/wiki/Binary_heap

    虽然heapq 是一个具有二进制堆 属性的数据结构,但使用sorted 是一个不同的概念。 sorted 返回一个排序列表,所以这本质上是一个结果,而heapq 是一个您不断使用的数据结构,可以选择对它进行排序通过sorted

    额外的sorted 信息在这里:https://docs.python.org/3.4/library/functions.html#sorted

    你具体想完成什么?

    回应 OP 的评论:

    为什么你认为你特别需要heapq二进制堆是一种特殊的数据结构,根据您的要求,它很可能没有必要。

    您似乎非常关心性能,但不清楚原因。如果某件事是“表现不佳”,但它的总时间并不重要,那么从更大的角度来看它真的无关紧要。在总体情况下,dictlist 通常会执行得很好。为什么你特别认为需要heapq

    我想知道这是否是一种不让完美的敌人类型的情况。

    使用C 扩展 编写Python 是一个利基 用例,用于性能确实是一个重要问题的情况。 (也就是说,如果你是处理大文件以及性能是否是您主要关心的问题)。

    关于在复杂的情况下继续玩结构:通过.append()进行排序和添加元素是否会更快

    我仍然不清楚这里的用例是什么。正如我上面提到的,sortedheapq 确实是两个不同的概念。

    您如此关注性能的用例是什么? (如果没有其他尚未指定的因素,我认为您可能在这里过分强调了代码中最佳情况性能的重要性。)

    【讨论】:

    • 平均插入次数是恒定的;一般来说,它们是 O(log n)。 (使用摊销分析,它们也是常数,因为 n 次插入总共需要 O(n) 时间。)
    • 没错,我编辑了我的回复以反映平均值;我无意中省略了。
    • @ken-hampson,我的班级有很多不同的作业。如果是超级基本情况:按顺序排列一次以弹出最小的顺序。我可以假设“排序”是最好的选择吗?执行情况是什么?此外,还有“用 C 语言编写”/“纯 python”与性能相关的概念,这对我来说是全新的。在复杂的情况下继续玩结构案例:使用 sorted 排序并通过 .append() 添加元素会更快吗? -- 希望这能消除我的疑虑。
    • 添加了附加信息以回答问题。
    • @chepner,您关于 n 插入的摊销复杂性的陈述是不正确的,至少如果您使用通常意义上的“最坏情况时间平均超过所有操作”。特别是,如果元素以反向排序顺序插入,则每次插入的摊销成本实际上是 Ω(log n)。您可能对一次构建整个堆的 O(n) 成本感到困惑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    • 2018-07-22
    • 2012-10-12
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    相关资源
    最近更新 更多