【问题标题】:Why is the constant factor of quicksort better than that of heapsort?为什么快速排序的常数因子优于堆排序?
【发布时间】:2013-08-29 08:10:04
【问题描述】:

根据我的计算:

  • 快速排序成本 = n + (n/2 + n/2) + (n/4 + n/4 + n/4 + n/4) + ... = n * log(n) = log(n n)
  • 堆排序成本 = sum [log(i)] for i = n, n-1, n-2, ..., 1 = log(n!)

为什么说快速排序比堆排序有更好的常数因子,因此快速排序平均优于堆排序?不是log(nn) > log(n!)吗?

【问题讨论】:

标签: performance algorithm sorting


【解决方案1】:

我认为这里的问题是您对快速排序和堆排序的分析不够精确,无法说明为什么常数因子会有所不同。

您确实可以证明,平均而言,快速排序将比堆排序进行更多的比较(对于快速排序,n log2 n 与堆排序大约为 1.44 n log2 n)。但是,比较并不是堆排序和快速排序运行时的唯一决定因素。

快速排序更快的主要原因是locality of reference由于内存缓存的工作方式,在彼此相邻的位置上的数组访问往往比数组访问快得多分散在一个阵列中。在快速排序中,分区步骤通常在数组的末端进行所有读取和写入,因此数组访问彼此紧密排列。另一方面,堆排序在数组上下移动时会在数组周围跳跃。因此,平均而言,快速排序中的数组访问比堆排序中的数组访问花费的时间要少得多。差异足够大,以至于快速排序中 n log n 项前面的常数因子低于堆排序中 n log n 项前面的常数因子,这就是快速排序比堆排序快得多的原因之一。

简而言之 - 如果我们只关心比较,那么堆排序是比快速排序更好的选择。但由于内存系统使用缓存并且缓存未命中的代价很高,因此快速排序通常是更好的选择。

另外,请注意 log(nn) = n log n 和 log (n!) = n log n - n + O(log n) 通过Stirling's approximation。这意味着 log (n!) 并不比 n log n 小很多,即使 n 变得非常大。肯定有区别,但还不足以单独造成巨大的影响。

希望这会有所帮助!

【讨论】:

  • 我的信息总结:1)常数因子不仅来自理论比较,还来自实现细节。 Qsort 的常数因子很小,因为它是缓存友好的。对于 Qsort:要访问的下一个元素通常在内存中靠近您刚刚查看的元素。对于Hsort,访问需要上下跳转...
  • @templatetypdef:感谢您提供非常详细和有用的解释。由于像你这样的人,stackoverflow 越来越受欢迎。
  • 我相信您在这里混淆了合并排序和堆排序。当通过堆冒泡时,您必须将可能的 2 个父母相互比较以找到较小的父母,然后将其与冒泡的父母进行比较以确定是否冒泡。这是 log_2 级别的 2 次比较,使其成为 2 n log_2(n) 比较,这比快速排序更差。与归并排序相比,每次比较都会将某些内容向上移动到 log_2(n) 级别之一,从而导致 n log_2(n) 比快速排序更好。
【解决方案2】:

以下是 Steven S. Skiena 的《算法设计手册》中的段落,其中谈到了三种 O(nlogn) 排序算法之间的速度:

但是我们如何比较两个 Θ(n log n) 算法来决定哪个是 更快?我们如何证明快速排序真的很快?很遗憾, RAM 模型和 Big Oh 分析提供了一组过于粗糙的工具, 做出这种区分。 当面对相同的算法时 渐近复杂度、实现细节和系统怪癖等 因为缓存性能和内存大小很可能被证明是决定性的 因素。

我们可以说的是,实验表明 a 正确实施的快速排序实施得很好,通常是 比合并排序或堆排序快 2-3 倍。 主要原因是 最内层循环中的操作更简单。但我不能 当我说快速排序更快时,如果您不相信我,请与您争论。 这个问题的解决方案超出了我们的分析工具 正在使用。最好的判断方法是同时实现算法和 实验。

-4.6.3 “快速排序真的很快吗?”,算法设计手册

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-28
    • 2015-05-26
    • 2017-03-18
    • 2011-11-30
    • 1970-01-01
    相关资源
    最近更新 更多