【问题标题】:Pre-sorting analysis algorithm?预排序分析算法?
【发布时间】:2010-12-23 09:17:33
【问题描述】:

快速排序的一个众所周知的问题是,当数据集处于或几乎处于排序顺序时,性能会严重下降。在这种情况下,通常非常慢的插入排序很容易成为最佳选择。问题是知道何时使用哪个。

是否有一种算法可用于遍历数据集、应用比较因子并返回关于数据集与排序顺序的接近程度的报告?我更喜欢 Delphi/Pascal,但如果示例不太复杂,我可以阅读其他语言。

【问题讨论】:

标签: algorithm delphi sorting analysis


【解决方案1】:

正如您所期望的那样,对此进行了很多思考。 3 的中值技术意味着快速排序的最坏情况行为不会发生在已排序的数据中,而是发生在不太明显的情况下。

Introsort 非常令人兴奋,因为它完全避免了快速排序的二次最坏情况。而不是你的自然问题,“我如何检测到数据接近排序”,它实际上在问自己,“这需要太长时间吗?”。如果答案是肯定的,它会从快速排序切换到堆排序。

Timsort 将合并排序与插入排序结合在一起,在排序或反向排序的数据以及包含排序或反向排序子集的数据上表现非常出色。

因此,您的问题的答案可能是“您不需要预通行分析,您需要自适应排序算法”。

【讨论】:

    【解决方案2】:

    还有 SmoothSort,这显然很难实现,但它在 O(N log N) 到 O(N) 之间变化,具体取决于数据的排序方式。

    http://en.wikipedia.org/wiki/Smoothsort

    冗长而棘手的 PDF: http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF

    但是,如果您的数据确实很大并且您必须连续访问它,那么合并排序可能是最好的。它总是 O(N log N) 并且具有出色的“局部性”属性。

    【讨论】:

      【解决方案3】:

      我没有听说过任何预排序分析,但我的观点是,如果您要通过数据集对其进行分析,那么您已经在削减整体排序时间的性能。

      【讨论】:

      • 这点很好,但是如果分析通过是O(n),它不会支配渐近排序时间。如果它可以帮助避免 O(n^2) 最坏情况的排序时间,那么它可能会为大型数据集的排序时间带来净收益。
      • @ddaa:这对于比较排序来说是正确的,但是使用基数排序或桶排序可以进行 O(n) 排序。如果我们包括这些算法,则排序时间可能由分析时间决定......
      • @Jason:您不会对即将进行桶排序的数据执行此分析。问题是关于在快速排序和插入排序之间进行选择,而您不打算这样做...
      【解决方案4】:

      一种可能的解决方案是取当前排序范围内的第一个、最后一个和中间元素(在快速排序操作期间),并选择中间元素作为枢轴元素。

      【讨论】:

      • 你最好的情况仍然是 O(N log N),其中插入排序是 O(N) 对于几乎排序的数据。
      【解决方案5】:

      为了决定使用哪种算法而进行全面分析,您将几乎完成排序工作。您可以执行一些操作,例如检查一小部分随机但增加的索引的值(即分析项目的小样本)。

      【讨论】:

        【解决方案6】:

        您仍然需要遍历所有记录以确定其是否已排序,因此要提高性能,请从您的第一条记录开始,然后遍历其余记录,直到您发现某些内容未正确排序,或者到达列表。如果您发现未命中,则仅将项目从该位置排序到末尾(因为列表的开头已经排序)。

        在第二部分的每个项目中,查看该项目是否比第一部分中的最后一个元素

        【讨论】:

          【解决方案7】:

          只有当数据集很大并且大部分已经排序时,QuickSort 才会出现问题,我会使用以下启发式方法(等待一个完整的解决方案):

          • 如果数据集大小低于阈值,请不要担心。

          • 如果您可以快速(索引)访问记录(项目),请在每 N 条记录中抽取 1 条记录的样本,并查看它们是否已排序。对于小样本应该足够快,然后您可以决定是否使用快速排序。

          【讨论】:

          • 但是如果每个 N 中的 1 条记录被排序,但每个 N 中的 +1 条记录没有被排序,则样本失败。您可能仍然需要阅读每条记录,以查看其中一条未采样的记录是否有问题。
          • 同意,但是从统计上看,样本与总体人口的偏差很小,尤其是如果你随机化一点 N。
          【解决方案8】:

          提出一个人们尚未提出的概念性观点:快速排序是一种常识性的分而治之的算法,在极少数情况下存在明显的错误。假设您要对一堆学生论文进行排序。 (这与一些规律性有关。)在快速排序算法中,您选择一些纸张,即枢轴。然后根据它们是在枢轴之前还是之后来划分其他论文。然后对两个子堆重复此操作。什么是错误?枢轴可以是靠近列表一端而不是中间的名称,因此将其分成两堆并没有多大作用。

          归并排序是另一种以不同顺序工作的分治算法。您可以在线性时间内合并两个排序列表。将论文分成相等或几乎相等的两堆,然后对每堆进行递归排序,然后合并。合并排序没有任何错误。快速排序比归并排序更受欢迎的一个原因是历史原因:快速排序(通常)速度很快,并且无需任何额外内存即可工作。但是现在,保存比较可能比节省内存更重要,并且实际的重新排列通常通过置换指针抽象出来。如果事情一直是这样,那么我怀疑合并排序会比快速排序更受欢迎。 (也许在名字后面加上“quick”是一种很好的推销技巧。)

          【讨论】:

          • 从我的 POV 来看,就地排序的好处与其说是节省 内存,不如说是节省了内存分配,因此不会失败。所以在对数组进行排序时,快速排序/堆排序/插入排序/冒泡排序都有比归并排序更好的用户界面。如果合并排序优于快速排序,那么您当然可以尝试分配内存,如果失败,则改为进行快速排序。如果您无论如何都要分配一个二级指针数组并对其进行排序,那么您就是在那儿引入了失败的可能性,因此也可能在其他地方允许失败。
          • @SteveJessop 这是一个公平的观点。然而,这种担忧虽然在某些情况下仍然很重要,但也有点过时了。我同意外部环境公平地为每个需要它的客户端程序或函数分配内存并非易事。然而,随着时间的推移,在许多环境中,这种情况已经变得更好了。
          • 我不认为这是一个真正的公平问题,更多的是当你用完时会发生什么,以及你是否对此有足够的信心。如果分配可能失败,那么您以一种方式编写程序。相反,如果操作系统将某些东西从水中吹出来,直到它有足够的内存来满足请求或第一次访问时出现页面错误,那么您以另一种方式编写程序。一些语言采取了中间路径,理论上你可以捕获内存不足的异常并继续,但实际上你没有,你让异常杀死你。我想这可以被认为是“最新”的方式;-)
          猜你喜欢
          • 1970-01-01
          • 2018-02-08
          • 1970-01-01
          • 1970-01-01
          • 2011-07-04
          • 2015-10-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多