Преимущества Quichesort

Я создал эту программу для задания, в котором от нас требовалось создать реализацию Quichesort. Это гибридный алгоритм сортировки, который использует быструю сортировку до тех пор, пока не достигнет определенной глубины рекурсии (log2 (N), где N - длина списка), а затем переключается на Heapsort, чтобы избежать превышения максимальной глубины рекурсии.

При тестировании моей реализации я обнаружил, что, хотя в целом она работает лучше, чем обычная быстрая сортировка, 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)

Код для Heapsort:

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

person Slartibartfast    schedule 21.12.2014    source источник
comment
Quichesort звучит восхитительно.   -  person Ffisegydd    schedule 21.12.2014
comment
Боже мой, мои навыки питона возвращаются ко мне после 2 лет, когда я даже не касался питона!   -  person Irrational Person    schedule 21.12.2014
comment
Может быть, поможет совместное использование heapsort и quicksort? По крайней мере, есть шанс, что вы оба отправитесь в Рочестерский технологический институт, поскольку поиск в Google по запросу quipsort приводит к трем разным URL-адресам в домене rit.edu.   -  person Jonathan Leffler    schedule 21.12.2014
comment
@JonathanLeffler Ага, я иду в RIT. Я тоже видел эти вопросы и почти уверен, что они основаны на том же задании. Однако они не отвечают на мой вопрос. Я пытаюсь выяснить, действительно ли этот алгоритм полезен.   -  person Slartibartfast    schedule 21.12.2014
comment
В некоторых отношениях это сложный вопрос. Вам следует ознакомиться с некоторыми ссылками в Быстрая сортировка: выбор точки поворота. Вам также следует поискать Introsort. Я не уверен, как устроено ваше тестирование, но есть много разных способов вызвать проблемы сортировки: с исходными распределениями, которые уже отсортированы, или обратным порядком сортировки, или в форме V или перевернутой V (орган pipe), либо все равно, либо рандомно. То, что хорошо для одних, может быть плохо для других.   -  person Jonathan Leffler    schedule 21.12.2014
comment
@JonathanLeffler Ага. Я только что поискал Introsort, и мне кажется, что Quichesort - это просто другое слово для обозначения того же алгоритма.   -  person Slartibartfast    schedule 22.12.2014
comment
Что вы имеете в виду, когда говорите «полезный»? Как правило, простые алгоритмы, которые вы изучаете в школе, на самом деле не используются в реальном мире; мы обычно отдаем предпочтение реализациям библиотек, которые, как правило, агрессивно оптимизируются. Например, Timsort.   -  person Kevin    schedule 22.12.2014
comment
Если выбор точки поворота MedianOf3 был ошибочным, это могло объяснить медлительность. Однако, поскольку другой допустимый алгоритм выбора сводной таблицы - это случайный выбор элемента в диапазоне, он не должен нарушать сортировку (данные все равно должны быть отсортированы) - это может просто привести к неоптимальной производительности сортировки.   -  person Jonathan Leffler    schedule 23.12.2014
comment
Сравнивать несколько алгоритмов по такой практической производительности необязательно из-за специфики входных данных, и особенно, вы реализовали некоторые из них, вызывая оптимизированную стороннюю библиотеку для других. Обычно мы сравниваем алгоритмы математически.   -  person Skyler    schedule 26.12.2014


Ответы (1)


Основные факты таковы:

  • Heapsort имеет худшую производительность O (n log (n)), но, как правило, медленнее упражняться.
  • Quicksort в среднем имеет производительность O (n log (n)), но O (n ^ 2 ) в худшем случае, но на практике работает быстро.
  • Introsort предназначен для использования быстрой на практике производительности быстрой сортировки, при этом гарантируя худшее -case O (n log (n)) поведение heapsort.

Один вопрос, который следует задать: почему быстрая сортировка «на практике» быстрее, чем heapsort? Это сложно ответить, но большинство ответов указывают на то, что у быстрой сортировки лучше пространственная локальность, ведущая к меньшему количеству промахов в кэше. Однако я не уверен, насколько это применимо к Python, поскольку он работает в интерпретаторе и имеет гораздо больше мусора, чем другие языки (например, C), которые могут повлиять на производительность кеша.

Что касается того, почему ваша конкретная реализация интросорта медленнее, чем heapsort Python - опять же, это трудно определить. Прежде всего, обратите внимание, что модуль heapq написан на Python, так что это относительно равномерно с вашей реализацией. Возможно, создание и объединение множества меньших списков обходится дорого, поэтому вы можете попробовать переписать свою быструю сортировку, чтобы она действовала на месте, и посмотреть, поможет ли это. Вы также можете попробовать настроить различные аспекты реализации, чтобы увидеть, как это влияет на производительность, или запустить код через профилировщик и посмотреть, есть ли какие-либо «горячие точки». Но в итоге, думаю, однозначного ответа вы вряд ли найдете. Это может просто сводиться к тому, какие операции в интерпретаторе Python выполняются особенно быстро или медленно.

person augurar    schedule 30.12.2014