Comments 17
Совершенно непонятна причина утверждения, что сортировка расчёской (comb sort) лучше сортировки Шелла (Shell sort). Вики говорит обратное: для Shell sort есть последовательность с O(n log²n), а для comb sort — нет.
И это логично, если учитывать, что у них два отличия — обмены вместо вставок (обменами — дороже) и отсутствие грамотно подобранной последовательности шагов у comb sort. Если для comb sort вместо обменов применить вставки, получится плохой Shell sort с возможностью до O(n²), а если изменить последовательность gapʼов на любую правильную от Shell sort, получится просто вариант Shell sort чуть дороже, ибо на обменах.
У вас же Shell sort применена в самом тупом варианте, который устарел полностью.
И это логично, если учитывать, что у них два отличия — обмены вместо вставок (обменами — дороже) и отсутствие грамотно подобранной последовательности шагов у comb sort. Если для comb sort вместо обменов применить вставки, получится плохой Shell sort с возможностью до O(n²), а если изменить последовательность gapʼов на любую правильную от Shell sort, получится просто вариант Shell sort чуть дороже, ибо на обменах.
У вас же Shell sort применена в самом тупом варианте, который устарел полностью.
+1
Вызываю Вас на дуэль.
Вы выставляете лучшую реализацию Shell sort (желательно на Python, но можем обсудить язык). Я со своей стороны подберу реализацию Comb sort. Проверим, кто окажется быстрее.
Вы выставляете лучшую реализацию Shell sort (желательно на Python, но можем обсудить язык). Я со своей стороны подберу реализацию Comb sort. Проверим, кто окажется быстрее.
0
Не принимаю: вы уходите от конкретной темы и при этом пытаетесь брать на «слабо».
Покажите тот случай, для которого comb sort по данным, по которым сформированы статьи википедии, имеет O(n²), и что вы его обошли, достигнув хотя бы сравнимого O(n log²n). Тогда это будет предметный разговор именно на ту тему, что я поднял.
Покажите тот случай, для которого comb sort по данным, по которым сформированы статьи википедии, имеет O(n²), и что вы его обошли, достигнув хотя бы сравнимого O(n log²n). Тогда это будет предметный разговор именно на ту тему, что я поднял.
+1
Давайте снова внимательно почитаем Википедию.
Сортировка расчёской
Лучшее время — O(n log n)
Сравнение алгоритма с quick sort (цитата) — "… конкурирует с алгоритмами, подобными быстрой сортировке ..."
Сортировка Шелла
Лучшее время — O(n log2 n) — ввиду квадратичности это худший, показатель чем у Comb sort.
Сравнение алгоритма с quick sort (цитата) — "… Невзирая на то, что сортировка Шелла во многих случаях медленнее, чем быстрая сортировка ...".
То есть, сама Википедия указывает, что у Comb sort более скоростные показатели чем у Shell sort.
Что касается «покажите тот случай», то я готов Вам всё показать, просто дайте мне наилучшую по Вашему мнению реализацию Shell sort на том языке программирования, который сочтёте нужным. Я проведу тесты и пришлю Вам все итоговые материалы.
Сортировка расчёской
Лучшее время — O(n log n)
Сравнение алгоритма с quick sort (цитата) — "… конкурирует с алгоритмами, подобными быстрой сортировке ..."
Сортировка Шелла
Лучшее время — O(n log2 n) — ввиду квадратичности это худший, показатель чем у Comb sort.
Сравнение алгоритма с quick sort (цитата) — "… Невзирая на то, что сортировка Шелла во многих случаях медленнее, чем быстрая сортировка ...".
То есть, сама Википедия указывает, что у Comb sort более скоростные показатели чем у Shell sort.
Что касается «покажите тот случай», то я готов Вам всё показать, просто дайте мне наилучшую по Вашему мнению реализацию Shell sort на том языке программирования, который сочтёте нужным. Я проведу тесты и пришлю Вам все итоговые материалы.
0
> Давайте снова внимательно почитаем Википедию.
Давайте. Только вы или это не сделали, или невнимательно читали, что я писал. Речь про худший случай, для которого утверждается квадратичная зависимость.
> То есть, сама Википедия указывает, что у Comb sort более скоростные показатели чем у Shell sort.
Лучший случай меня _пока_ тут не интересует. Интересует худший, как его детектировать и обходить.
Например, для quick sort это сделано более 30 лет назад — и формальное определение, и детект, и методы обхода. А тут как?
Если худший случай невозможно адекватно отлавливать, то сортировка неприменима в случаях, где можно устроить DoS по скорости соответствующим подбором данных (ситуация, реально известная с quick sort с простейшими методами выбора разделяющего элемента).
А таких случаев всё больше и больше.
> то я готов Вам всё показать, просто дайте мне наилучшую по Вашему мнению реализацию Shell sort на том языке программирования
Никакая — ни лучшая, ни обычная — реализация Shell sort не имеет отношения к O(n²) в худшем случае comb sort. Вы уводите разговор в сторону.
После того, когда (если) этот случай будет отловлен и нейтрализован, можно будет сравнивать скорость на типовых применениях.
Давайте. Только вы или это не сделали, или невнимательно читали, что я писал. Речь про худший случай, для которого утверждается квадратичная зависимость.
> То есть, сама Википедия указывает, что у Comb sort более скоростные показатели чем у Shell sort.
Лучший случай меня _пока_ тут не интересует. Интересует худший, как его детектировать и обходить.
Например, для quick sort это сделано более 30 лет назад — и формальное определение, и детект, и методы обхода. А тут как?
Если худший случай невозможно адекватно отлавливать, то сортировка неприменима в случаях, где можно устроить DoS по скорости соответствующим подбором данных (ситуация, реально известная с quick sort с простейшими методами выбора разделяющего элемента).
А таких случаев всё больше и больше.
> то я готов Вам всё показать, просто дайте мне наилучшую по Вашему мнению реализацию Shell sort на том языке программирования
Никакая — ни лучшая, ни обычная — реализация Shell sort не имеет отношения к O(n²) в худшем случае comb sort. Вы уводите разговор в сторону.
После того, когда (если) этот случай будет отловлен и нейтрализован, можно будет сравнивать скорость на типовых применениях.
+1
Сложности в худшем случае (по данным той же Википедии) у этих алгоритмов одинаковы — O(n2). Причём, подозреваю, что вырожденные случаи у Comb sort и Shell sort совершенно разные. На каких-то наборах данных один алгоритм будет зависать, а второй щёлкать как орешки. А универсальных вырожденных случаев (чтобы были одновременно вырожденными для обоих алгоритмов) может запросто и не оказаться. А если так, то какой смысл сравнения по худшему случаю?
В целом Вы подняли интересную тему. К сожалению, у меня сейчас банально нет времени проводить обстоятельное научное исследование, по отлавливанию и нейтрализации худших случаев дляShell sort / Comb sort. Если Вас так волнует эта тема, то почему бы Вам этим и не заняться за счёт своего, а не моего времени? Я сейчас пишу серию из около 30-ти обзорных (подчёркиваю — обзорных, а не уровня Arxiv.org) статей по 80+ алгоритмам, принадлежащим к 6 основным классам сортировок. Я не буду бросать эту работу только для того, чтобы Вам что-то доказывать по поводу Шелла и расчёски. Так что прошу меня извинить.
В целом Вы подняли интересную тему. К сожалению, у меня сейчас банально нет времени проводить обстоятельное научное исследование, по отлавливанию и нейтрализации худших случаев для
0
> Сложности в худшем случае (по данным той же Википедии) у этих алгоритмов одинаковы — O(n2).
Нет. Например, у Шелла на 3-smooth numbers гарантируется Θ(N(log N)^2).
> А если так, то какой смысл сравнения по худшему случаю?
Затем, что в худший случай пусть и сложно попасть без намерения, но с намерением — можно натворить дел.
> Так что прошу меня извинить.
Обзорность допускает упрощение, но не искажение, а именно так следует оценивать утверждения вида «сортировке расчёской — удалось преодолеть барьер O(n²) и выйти по времени на заслуживающие уважение O(n log n)», или «У неё нет рекурсии и поэтому в отличие от своих коллег она успешно обработывает массивы, состоящие из миллионов элементов».
Можете игнорировать, но не удивляйтесь потом оценкам результатов.
Нет. Например, у Шелла на 3-smooth numbers гарантируется Θ(N(log N)^2).
> А если так, то какой смысл сравнения по худшему случаю?
Затем, что в худший случай пусть и сложно попасть без намерения, но с намерением — можно натворить дел.
> Так что прошу меня извинить.
Обзорность допускает упрощение, но не искажение, а именно так следует оценивать утверждения вида «сортировке расчёской — удалось преодолеть барьер O(n²) и выйти по времени на заслуживающие уважение O(n log n)», или «У неё нет рекурсии и поэтому в отличие от своих коллег она успешно обработывает массивы, состоящие из миллионов элементов».
Можете игнорировать, но не удивляйтесь потом оценкам результатов.
+1
Есть подозрение, что я двусмысленно выразился в статье и мы вообще говорим о разных вещах.
O(n2)) в сортировку расчёской, то получаем алгоритм со сложностью O(n log n).
А вот если похожей модификацией сортировку вставками (имеющую сложностьO(n2)) преобразовать в сортировку Шелла, то получаем алгоритм со сложностью O(n log2 n).
То есть и сортировка Шелла и сортировка расчёской — это аналогичные модификации из более простых алгоритмов со сложностьюO(n2). И хоть это и похожие модификации, сортировка расчёской всё-таки в общем случае достигает лучших результатов, чем Шелл.
Когда я упомянул сложностьO(n2) то речь шла о сортировке пузырьком и сортировке вставками. А не о худшей сложности расчёски и Шелла, о чём мы и ведём эту странную дискуссию.
Сортировка расчёской по похожему принципу улучшает пузырьковую сортировку, благодаря чему временна́я сложность алгоритма сЯ имел ввиду, что если модифицировать сортировку пузырьком (имеющую сложностьO(n2 ) подскакивает аж доO(n log n). Увы, но Шеллу этот подвиг повторить не удаётся — лучшая временна́я сложность достигаетO(n log2 n).
А вот если похожей модификацией сортировку вставками (имеющую сложность
То есть и сортировка Шелла и сортировка расчёской — это аналогичные модификации из более простых алгоритмов со сложностью
Когда я упомянул сложность
0
Что касается вот этого:
У неё нет рекурсии и поэтому в отличие от своих коллег она успешно обработывает массивы, состоящие из миллионов элементовУ меня при тестировании действительно различные варианты быстрой сортировки не могут осилить массивы, состоящие из миллионов элементов. А вот расчёска сортирует быстро и без проблем.
0
> У меня при тестировании действительно различные варианты быстрой сортировки не могут осилить массивы, состоящие из миллионов элементов.
Хм… интересно, _как_ так надо было писать. Потому что осиливает. На Питоне, конечно, времена будут чудовищные (у меня на 10000 элементов — единицы секунд, а на 10 миллионов это будет два часа пахать), и вообще там больше память сожрёт и всех в своп загонит, но на C++ проблем никаких.
Вот моя версия тестов — простая, прямая, но работающая. Можете сравнить со своим quicksort.
Хм… интересно, _как_ так надо было писать. Потому что осиливает. На Питоне, конечно, времена будут чудовищные (у меня на 10000 элементов — единицы секунд, а на 10 миллионов это будет два часа пахать), и вообще там больше память сожрёт и всех в своп загонит, но на C++ проблем никаких.
Вот моя версия тестов — простая, прямая, но работающая. Можете сравнить со своим quicksort.
ptest
параметры регулирую через окружение (если у вас Windows, возможно, проще поменять умолчания в коде)
#!/usr/bin/env python3
import time, random, os, math
shell_gaps = []
def shell_generate_gaps():
global shell_gaps
max_gap = int(os.environ.get("ALEN", "1000"))
c3 = 1
while c3 <= max_gap:
c32 = c3
while c32 <= max_gap:
shell_gaps.append(c32)
#- print("__: appended {}".format(c32))
c32 *= 2
c3 *= 3
shell_gaps.sort(reverse = True)
def shell_sort(arr):
n = len(arr)
#- gaps = generate_gaps(n)
for gap in shell_gaps:
#print("__: gap={}".format(gap))
#print("__: arr={}".format(arr))
for i in range(gap, n):
temp = arr[i]
j = i
while j >= gap and arr[j-gap] > temp:
arr[j] = arr[j-gap]
j -= gap
arr[j] = temp
#print("__: final: arr={}".format(arr))
def comb_sort(arr):
coeff = 0.8017118471377938
n = len(arr)
gap = n
while True:
if gap > 1:
gap = int(gap*coeff)
#- print("__: comb_sort: run for gap={}".format(gap))
is_sorted = True
for i in range(0, n - gap):
if arr[i] > arr[i+gap]:
temp = arr[i]
arr[i] = arr[i+gap]
arr[i+gap] = temp
is_sorted = False
if gap == 1 and is_sorted:
break
def rec_qsort(arr, li, ri):
while True: ## instead of recursion
if ri <= li:
return
if ri - li < 10:
for i in range(li+1, ri+1):
temp = arr[i]
j = i
while j >= li+1 and arr[j-1] > temp:
arr[j] = arr[j-1]
j -= 1
arr[j] = temp
return
si = random.randint(li, ri)
temp = arr[li]
arr[li] = arr[si]
arr[si] = temp
lp = li
rp = ri
leading_lp = True
while lp < rp:
cv = arr[lp] - arr[rp]
if cv > 0:
temp = arr[lp]
arr[lp] = arr[rp]
arr[rp] = temp
if cv >= 0:
leading_lp = not leading_lp
if leading_lp:
rp -= 1
else:
lp += 1
if lp != rp:
raise AssertionError('lp == rp')
lwx = lp - li
rwx = ri - rp
if lwx > rwx:
rec_qsort(arr, rp+1, ri)
ri = lp - 1
else:
rec_qsort(arr, li, lp-1)
li = rp+1
def qsort(arr):
rec_qsort(arr, 0, len(arr) - 1)
def embedded_sort(arr):
arr.sort()
def generate_array():
arr_len = int(os.environ.get("ALEN", "1000"))
akind = os.environ.get("AKIND", "almost_sorted")
if akind == 'sorted':
return [i for i in range(arr_len)]
elif akind == 'reverse':
return [arr_len-i for i in range(arr_len)]
elif akind == 'almost_sorted':
arr = []
psize = min(int(math.sqrt(arr_len)), 1)
while len(arr) < arr_len:
psize1 = min(psize, arr_len - len(arr))
curr_len = len(arr)
portion = [i+curr_len+1 for i in range(psize1)]
random.shuffle(portion)
arr.extend(portion)
return arr
else:
arr = [i for i in range(arr_len)]
random.shuffle(arr)
return arr
def run(sort_func):
tsum = 0
for arr in g_arrays:
arrx = arr[:]
t0 = time.time()
sort_func(arrx)
tsum += time.time() - t0
for j in range(len(arrx) - 1):
if arrx[j] > arrx[j+1]:
print("__: unsorted: j={} a1={} a2={}".format(j, arr[j], arr[j+1]))
raise RuntimeError("assertion: not sorted")
return tsum
if __name__ == "__main__":
shell_generate_gaps()
g_arrays = [generate_array() for i in range(100)]
print("shell_sort tsum:", run(shell_sort))
print("comb_sort tsum:", run(comb_sort))
print("qsort tsum:", run(qsort))
print("embedded_sort tsum:", run(embedded_sort))
параметры регулирую через окружение (если у вас Windows, возможно, проще поменять умолчания в коде)
+1
А вот тут большое Вам спасибо. На досуге протестирую и даже, скорее всего, возьму эти реализации себе как основные для данных сортировок.
А то в комментариях мода такая — пишут: «у вас реализации неэффективные», но хоть бы кто привёл «правильные» варианты алгоритмов.
А то в комментариях мода такая — пишут: «у вас реализации неэффективные», но хоть бы кто привёл «правильные» варианты алгоритмов.
0
Для tree sort я бы ещё добавил вырожденный случай с отсортированным (или почти отсортированным) массивом, когда дерево выстраивается в «лесенку».
+1
Я просто в этой статье галопом по Европам прошёлся по общеизвестным вставочным сортировкам, не хотел перегружать и так большой материал вырожденными случаями.
Через несколько статей будет рассказ про сортировку с помощью splay tree, которая как раз преодолевает вырожденные случаи, возникающие у простого binary tree. Там как раз всё и покажу.
Через несколько статей будет рассказ про сортировку с помощью splay tree, которая как раз преодолевает вырожденные случаи, возникающие у простого binary tree. Там как раз всё и покажу.
0
В самой первой вставке кода ошибка:
-1 не нужно ставить, иначе проход не будет охватывать последний элемент, проверено.
Исправленный вариант:
for i in range(len(data) - 1):
-1 не нужно ставить, иначе проход не будет охватывать последний элемент, проверено.
Исправленный вариант:
for i in range(len(data)):
0
Возможно при визуализации более точным будет показывать массив в дебагируемом режиме. В отличии от приведенных (а также имеющих в вики) визуализаций при дебагировании видно, что "сдвиг" это просто запись поверх существующего т.е. на каждом шагу возникают повторяющие элементы, а не пустые места.
+1
Sign up to leave a comment.
Сортировки вставками