【问题标题】:Benefits of QuichesortQuichesort 的好处
【发布时间】:2015-02-19 22:48:12
【问题描述】:

我为一项任务创建了这个程序,在该任务中我们需要创建 Quichesort 的实现。这是一种混合排序算法,使用 Quicksort 直到达到一定的递归深度(log2(N),其中 N 是列表的长度),然后切换到 Heapsort,以避免超过最大递归深度。

在测试我的实现时,我发现虽然它的性能通常优于常规快速排序,但堆排序始终优于两者。 谁能解释为什么 Heapsort 表现更好,以及在什么情况下 Quichesort 会比 Quicksort Heapsort 更好?

请注意,由于某种原因,该作业将算法称为“Quipsort”。

编辑:显然,“Quichesort”实际上与 Introsort.

我还注意到我的 medianOf3() 函数中的逻辑错误是 导致它为某些输入返回错误的值。这是一个改进的 函数版本:

def medianOf3(lst):
    """
    From a lst of unordered data, find and return the the median value from
    the first, middle and last values.
    """

    first, last = lst[0], lst[-1]
    if len(lst) <= 2:
        return min(first, last)
    middle = lst[(len(lst) - 1) // 2]
    return sorted((first, middle, last))[1]

这能解释算法相对较差的性能吗?

Quichesort 代码:

import heapSort             # heapSort
import math                 # log2 (for quicksort depth limit)

def medianOf3(lst):
    """
    From a lst of unordered data, find and return the the median value from
    the first, middle and last values.
    """

    first, last = lst[0], lst[-1]
    if len(lst) <= 2:
        return min(first, last)
    median = lst[len(lst) // 2]
    return max(min(first, median), min(median, last))

def partition(pivot, lst):
   """
   partition: pivot (element in lst) * List(lst) -> 
        tuple(List(less), List(same, List(more))).  
   Where:
        List(Less) has values less than the pivot
        List(same) has pivot value/s, and
        List(more) has values greater than the pivot

   e.g. partition(5, [11,4,7,2,5,9,3]) == [4,2,3], [5], [11,7,9]
   """

   less, same, more = [], [], []
   for val in lst:
      if val < pivot:
         less.append(val)
      elif val > pivot:
         more.append(val)
      else:
         same.append(val)
   return less, same, more

def quipSortRec(lst, limit):
    """
    A non in-place, depth limited quickSort, using median-of-3 pivot.
    Once the limit drops to 0, it uses heapSort instead.
    """

    if lst == []:
        return []

    if limit == 0:
        return heapSort.heapSort(lst)

    limit -= 1
    pivot = medianOf3(lst)
    less, same, more = partition(pivot, lst)
    return quipSortRec(less, limit) + same + quipSortRec(more, limit)

def quipSort(lst):
    """
    The main routine called to do the sort.  It should call the
    recursive routine with the correct values in order to perform
    the sort
    """

    depthLim = int(math.log2(len(lst)))
    return quipSortRec(lst, depthLim)

堆排序代码:

import heapq    # mkHeap (for adding/removing from heap)

def heapSort(lst):
    """
    heapSort(List(Orderable)) -> List(Ordered)
        performs a heapsort on 'lst' returning a new sorted list
    Postcondition: the argument lst is not modified
    """

    heap = list(lst)
    heapq.heapify(heap)
    result = []
    while len(heap) > 0:
        result.append(heapq.heappop(heap))
    return result

【问题讨论】:

标签: python sorting quicksort heapsort


【解决方案1】:

基本事实如下:

  • Heapsort 具有最坏情况 O(n log(n)) 的性能,但在实践中往往很慢。
  • Quicksort 的平均性能为 O(n log(n)),但在最坏的情况下为 O(n^2),但在实践中很快。
  • Introsort 旨在利用快速排序的快速实践性能,同时仍保证堆排序的最坏情况 O(n log(n)) 行为。

要问的一个问题是,why is quicksort faster "in practice" than heapsort? 这是一个很难回答的问题,但大多数答案都指向快速排序如何更好地spatial locality,从而减少缓存未命中。但是,我不确定这对 Python 有多大的适用性,因为它在解释器中运行,并且与其他可能干扰缓存性能的语言(例如 C)相比,它在后台运行的垃圾要多得多。

至于为什么您的特定 introsort 实现比 Python 的 heapsort 慢 - 同样,这很难确定。首先,请注意 heapq 模块是written in Python,因此它与您的实现相对平衡。可能是创建和连接许多较小的列表成本很高,因此您可以尝试重写快速排序以就地操作,看看是否有帮助。您还可以尝试调整实现的各个方面以查看它如何影响性能,或者通过分析器运行代码并查看是否有任何热点。但最后我认为你不太可能找到明确的答案。它可能只是归结为 Python 解释器中哪些操作特别快或特别慢。

【讨论】:

    猜你喜欢
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 2014-06-20
    相关资源
    最近更新 更多