【问题标题】:Sorting algorithm times using sorting methods使用排序方法对算法时间进行排序
【发布时间】:2015-02-15 05:19:03
【问题描述】:

所以我刚刚了解了排序算法的冒泡、合并、插入、排序等。它们的排序方法似乎都非常相似,而在我看来,它们的方法变化很小。那么为什么它们会产生如此不同的排序时间,例如 O(n^2) vs O(nlogn)

【问题讨论】:

  • 那么你的问题是为什么不同算法的复杂度不一样? 因为它们是不同的算法
  • 基本上。我知道通过不同的方法,搜索的复杂程度会因依赖于方法的因素而降低,但我对时间复杂度的工作原理缺乏了解,我不明白为什么在不同的排序时间会产生这种情况
  • 我倾向于不同意你的说法,即“他们的排序方法在我看来他们的方法变化很小”。排序函数的复杂性不同,因为它们并不相似。例如递归和非递归排序函数。插入排序:在排序结果中重复添加新元素。冒泡排序:重复比较邻居对并在必要时交换。我觉得这是一个不好的问题,因为这个问题没有显示出任何研究工作。
  • Nitpick:递归与迭代是关于实现的,它们都可以被诱使看起来像另一个。合并排序不是“递归的”,而是分而治之的。

标签: python algorithm sorting time-complexity computation-theory


【解决方案1】:

你看到的“相似”(?!)完全是虚幻的。

基本的 O(N 平方) 方法会一遍又一遍地重复它们的工作,而不会利用在“上一步”上完成的任何工作的“下一步”。所以第一步花费的时间与 N 成正比,第二步花费的时间与 N-1 成正比,依此类推——从 1 到 N 的整数总和与 N 的平方成正比。

例如,在选择排序中,您每次都在查找 I:N 部分中的最小元素,其中 I 首先是 0,然后是 1,依此类推。这是(并且必须)通过检查所有这些元素来完成的元素,因为以前没有注意通过利用以前的通道来减少后续通道的工作量。找到最小元素后,将其与第 I 个元素交换,增加 I,然后继续。当然是 O(N 平方)。

高级 O(N log N) 方法的结构巧妙,可以利用前面步骤中完成的后续步骤。与基本方法相比,这种差异是如此普遍和深刻,以至于如果一个人无法感知它,那主要是关于一个人的感知敏锐度,而不是方法本身:-)。

例如,在归并排序中,您在逻辑上将数组分成两部分,0 到半长,半长到长度。一旦对每一半进行排序(以相同的方式递归,直到长度足够短),两半就会合并,这本身就是一个线性子步骤。

由于您每次都减半,显然您需要与 log N 成比例的步骤数,并且由于每个步骤都是 O(N),因此显然您会得到非常理想的 O(N log N)。

Python 的“timsort”是一种“自然归并排序”,即归并排序的一种变体,它可以利用数组中已经排序(或反向排序)的部分,它可以快速识别这些部分并避免花费任何进一步的工作.这不会改变 big-O,因为这大约是 最坏-case 时间 - 但 expected 时间崩溃得更远,因为在许多现实生活中,一些部分排序 strong>存在

(请注意,按照 big-O 的严格定义,快速排序一点也不快——最坏的情况是与 N 的平方成正比,当您每次都碰巧选择了一个糟糕的枢轴时.. . expected-time 明智,虽然没有 timsort 好,因为在现实生活中,您反复选择灾难支点的情况非常罕见......但是,最糟糕-case,他们可能发生!-)。

timsort如此,即使是非常有经验的程序员也会被吓到。我不算数,因为我是发明者 Tim Peters 的朋友,也是 Python 狂热者,所以我的偏见很明显。但是,考虑...

...我记得在 Google 的一次“技术演讲”中介绍了 timsort。坐在我旁边的前排是乔什·布洛赫,当时他也是一名 Google 员工,并且是杰出的 Java 专家。演讲进行到一半时,他再也无法抗拒——他打开笔记本电脑,开始进行黑客攻击,看看它是否可能像出色、敏锐的技术演示所显示的那样好。

因此,timsort 现在也是 Java 虚拟机 (JVM) 最新版本中的排序算法,尽管仅适用于用户定义的对象(基元数组仍以旧方式排序,quickersort [*]我相信——我不知道哪些 Java 特性决定了这种“拆分”设计选择,我的 Java-fu 相当弱:-)。

[*] 这本质上是快速排序,加上一些用于枢轴选择的技巧,以尝试避免中毒案例——这也是 Python 在蒂姆·彼得斯(Tim Peters)在他做出的许多重要贡献中做出这一不朽贡献之前使用的工具几十年。

结果有时会让具有 CS 背景的人感到惊讶(比如 Tim,我有幸拥有很久以前的学术背景,不是 CS,而是 EE,这很有帮助:-)。例如,您必须维护一个不断增长的数组,该数组始终在任何时间点进行排序,因为必须将新的传入数据点添加到数组中。

经典的方法是使用二等分 O(log N) 来为每个新的传入数据点找到合适的插入点——但是,为了将新数据放在正确的位置,您需要移动后面的内容一个槽,即 O(N)。

使用 timsort,您只需将新数据点附加到数组,然后对数组进行排序 - 在这种情况下,对于 timsort 来说是 O(N)(因为它在利用第一个 N- 1 件!-)。

您可以将 timsort 视为将“利用以前完成的工作”推向一个新的极端——其中不仅以前由算法本身完成的工作,而且还受到现实生活数据处理其他方面的其他影响(导致段被提前排序),都被利用到了极点。

然后我们可以进入桶排序和基数排序,它们通过利用项目的内部结构改变了话语平面——在传统排序中,这限制了一个人能够比较两个项目。

或者一个类似的例子——Bentley 在他的不朽著作“Programming Pearls”中提出——需要对一个包含数百万个唯一正整数的数组进行排序,每个正整数都被限制为 24 位长。

他用一个 16M 位的辅助数组解决了这个问题——毕竟只有 2M 字节——最初都是零:一个通过输入数组设置辅助数组中的相应位,然后一个通过辅助数组在找到1s 的地方再次形成所需的整数——然后砰,O(N) [并且非常快速:-)] 对这种特殊但重要的情况进行排序!-)

【讨论】:

  • 如果你无法感知它,那就说明你的感知敏锐度 - 很深刻......
  • 很好的答案排序概念解释得很好,因此可以接受,但感觉好像消极是没有根据的。刚开始学习计算机科学专业的学生还没有太多关于时间复杂度如何工作的经验。
  • @Overflow2341313,从来没有上过一门 CS 课(除非你算上一门“Fortran 编程”课程,它主要是对参考手册的一部分进行重新散列:-) 我很难与 CS 联系起来人——我在这个领域完全是自学成才,“站在巨人的肩膀上”,写了非常好的书,开源的软件包,一个人可以按照自己的节奏学习。在上一堂 CS 课的时间里,我可以吸收 5-10 种这样的书籍和软件包,并且学到更多东西。 (管理和创业也是如此,顺便说一句,还有我自学的领域:-)。
猜你喜欢
  • 1970-01-01
  • 2019-12-10
  • 2017-07-16
  • 2022-01-12
  • 2016-10-28
  • 1970-01-01
  • 1970-01-01
  • 2012-01-03
  • 1970-01-01
相关资源
最近更新 更多