Мое мнение: не может, поскольку цель создания алгоритма — достижение результата с определенной затратой времени и памяти — здесь очевидно похерена. получилось нечто напомниающее карго-культ: вроде все похоже на настоящее, но не работает.
Здравствуйте, Basil B, Вы писали:
BB>Предлагаю обменяться мнениями по поводу того, имеет ли право этот "знаменитый двустрочник" на хаскеле называться quicksort-ом, и почему: BB>
BB>qsort [] = []
BB>qsort (x:xs) = qsort [y | y <- xs, y < x] ++ [x] ++ qsort [y | y <- xs, y >= x]
BB>
Наврядли удастся это сделать, пока не будет обозначено четкое определение quicksort
А вообще,
1. Pivot выбран, список разделен относительно pivot, рекурсия для каждого куска
2. Сложность O(N*logN)
В общем ДА, это quicksort
Есть конечно нюансы, но это вопрос реализации:
1. Для "настоящего" quicksort не нужна дополнительная память (in-place сортировка), но в Haskell такое понятие вообще не имеет смысла.
2. В haskell-версии эта сортировка является еще и устойчивой (stable), в отличии от "оригинала",
Одновременно эти два условия и не соблюсти — stable in-place за O(N*logN) пока не придумали
И не забываем, что в Haskell сортируется список, т.е. структура с последовательным доступом (в отличии от массива)
Б>2. В haskell-версии эта сортировка является еще и устойчивой (stable), в отличии от "оригинала", Б>Одновременно эти два условия и не соблюсти — stable in-place за O(N*logN) пока не придумали
У Кнута же был inplace merge sort, если ничего не забыл. Тынц раз, тынц два.
Здравствуйте, Basil B, Вы писали:
BB>Предлагаю обменяться мнениями по поводу того, имеет ли право этот "знаменитый двустрочник" на хаскеле называться quicksort-ом, и почему: BB>
BB>qsort [] = []
BB>qsort (x:xs) = qsort [y | y <- xs, y < x] ++ [x] ++ qsort [y | y <- xs, y >= x]
BB>
Code like the above has been used up and down the tubes to demonstrate the superiority of functional languages. It's a functional-style quicksort written in Haskell that can be understood, at least according to some, without any prior exposure. First line: qsort of the empty list is the empty list. Second line (broken to fit in a narrow space): qsort for a list starting with x followed by some more xs is a concatenation ('++') of three lists: all stuff in xs that's smaller than x sorted, x itself, and all stuff in xs that's greater than or equal to x, again sorted. Try defining quicksort in two lines in your Blub language!
As much as I like Haskell, I look at that example with a mix of admiration and indignation, same as I'd look at an example illustrating C's virtues by means of a buffer overrun exploit. Both cases inspire awe, but I think they illustrate something bad rather than something good, and I could never bring myself to write or use that kind of trick.
There are a few things about qsort that don't quite cut the mustard. For starters, qsort is not really quicksort. Quicksort, as defined by Hoare in his seminal paper [8], is an in-place algorithm. Hoare's in-place partition is his paper's main contribution and a quintessential part of quicksort. qsort is not in-place, so it could be at best considered an exercise inspired by quicksort. (And not a great exercise at that. It does so much ancillary work it's not even funny—did you think that those two passes through the list and all those concatenations come for free?) Second, qsort makes a patently bad choice of pivot by selecting the first element of the sequence, thus nicely guaranteeing quadratic performance for almost sorted data. And the thing is, in reality, input data is seldom really random. It might have been sorted at some point, then augmented with additional elements, to be sorted again later. Choosing a random pivot is the correct solution, but you see, that would make qsort quite a lot longer than three lines, because selecting one element at random from a singly-linked list is a non-trivial undertaking.
...
Помимо того что это не inplace (который был существенной частью оригинальной статьи), и как отметил Александреску тут совсем другой partition, тут ещё не используется приём дающий гарантию на логарифмичестий размер стека worst case, описанный в той же самой оригинальной статье шестидесятых годов.
Здравствуйте, Буравчик, Вы писали:
Б>Наврядли удастся это сделать, пока не будет обозначено четкое определение quicksort
Более-менее чёткое определение выводится из оригинальной статьи. Inplace является одной из главных составляющих скорости сортировки.
Даже название нам указывает на то, что скорость является важным атрибутом этой сортировки.
Б>2. Сложность O(N*logN)
В оригинальном quicksort глубина стэка гарантированно логарифмическая.
Б>Есть конечно нюансы, но это вопрос реализации: Б>1. Для "настоящего" quicksort не нужна дополнительная память (in-place сортировка), но в Haskell такое понятие вообще не имеет смысла.
Есть монадические массивы с inplace модификацией. Вот пример того как это выглядит — уже не так радужно.
Б>Одновременно эти два условия и не соблюсти — stable in-place за O(N*logN) пока не придумали
Есть адаптивные stable сортировки на базе merge. Если доступен дополнительный объём памяти в "несколько процентов" от объёма входного массива — то сложность O(N*ln(N)). Если же нет (полностью inplace) то O(N*ln2(N)). Этот алгоритм подробно разбирает Александров Степанов в Elements of Programming, и он используется в реализациях std::stable_sort.
Б>И не забываем, что в Haskell сортируется список, т.е. структура с последовательным доступом (в отличии от массива)
Не просто последовательным, а ещё и однонаправленным. Более-менее нормальный quicksort можно реализовать для двунаправленных последовательностей.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Помимо того что это не inplace (который был существенной частью оригинальной статьи), и как отметил Александреску тут совсем другой partition, тут ещё не используется приём дающий гарантию на логарифмичестий размер стека worst case, описанный в той же самой оригинальной статье шестидесятых годов.
Это устраняет проблему выбора первого элемента — как и в оригинале теперь выбирается средний элемент. Сложность алгоритма остается та же O(N*logN)
Правда теряется устойчивость сортировки (хотя это можно исправить)
Здравствуйте, Basil B, Вы писали:
BB>Здравствуйте, Basil B, Вы писали:
BB>Мое мнение: не может, поскольку цель создания алгоритма — достижение результата с определенной затратой времени и памяти — здесь очевидно похерена. получилось нечто напомниающее карго-культ: вроде все похоже на настоящее, но не работает.
Inplace-сортировка в хаскеле нереализуема по простой причине — там всё неизменяемое. Так что претензия странная.
Реализация, конечно, "на скорую руку", но суть алгоритма демонстрирует, по крайней мере применительно к иммутабельным спискам. Несложно сделать любую желаемую модификацию, хоть посередине выбирать точку, хоть в случайном месте.
Называть ли это quick sort-ом? Ну если у этого названия нет trademark-а, то называть. Не обязательно название алгоритма должно использоваться исключительно в каноничном виде. В общем вопрос определений.
Здравствуйте, Буравчик, Вы писали:
Б>Это устраняет проблему выбора первого элемента — как и в оригинале теперь выбирается средний элемент. Сложность алгоритма остается та же O(N*logN)
Обычно, когда говорят о сложности, то говорят о сложности в худшем случае. У любого quicksort сложность в худшем случае не O(N * logN).
Здравствуйте, _DAle_, Вы писали:
_DA>Здравствуйте, Буравчик, Вы писали:
Б>>Это устраняет проблему выбора первого элемента — как и в оригинале теперь выбирается средний элемент. Сложность алгоритма остается та же O(N*logN) _DA>Обычно, когда говорят о сложности, то говорят о сложности в худшем случае. У любого quicksort сложность в худшем случае не O(N * logN).
Речь была про то, что когда выбираешь первый элемент в качестве опорного, то это среднее N*logN превращается в худшее N^2 на частично отсортированных данных (коих большинство). Я показал, что эту проблему легко исправить в qsort.
Здравствуйте, Basil B, Вы писали:
BB>Предлагаю обменяться мнениями по поводу того, имеет ли право этот "знаменитый двустрочник" на хаскеле называться quicksort-ом, и почему:
Я считаю, что можно. Главный признак quicksort — это divide and conquer с выбором разделяющего элемента. Конечно, Хоар в свое время придумал не совсем это, но сейчас при всем множестве возможных реализаций quicksort, мне кажется, что и эту реализацию можно так назвать. Да, реализация неэффективная, да с объемом памяти все плохо, но принцип работы тот же.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Помимо того что это не inplace (который был существенной частью оригинальной статьи), и как отметил Александреску тут совсем другой partition, тут ещё не используется приём дающий гарантию на логарифмичестий размер стека worst case, описанный в той же самой оригинальной статье шестидесятых годов.
Можно цитату из оригинальной статьи дающую эту гарантию? В wikipedia это называется Sedgewick variation.
Здравствуйте, Basil B, Вы писали:
BB>Предлагаю обменяться мнениями по поводу того, имеет ли право этот "знаменитый двустрочник" на хаскеле называться quicksort-ом
Называться — конечно имеет, ибо "алгоритм" есть набор высокоуровневых операций. Делим список элементом, сортируем его подсписки — это и есть суть quicksort.
Другой вопрос, что это всё равно хаскелю не поможет — он уже много лет пытается корчить из себя элиту, как бомж в пиджаке "Армани". "Интеллектуальная обочина ИТ", я бы сказал.
При нынешнем развитии императивных языков (когда распараллеливание делается в два клика), вообще нет никакого смысла в функциональщине — за неё и цеплялись только из-за того, что "всё параллелится". Увы, в десктопном софте мало где нужны/полезны десятки параллельных трэдов. Ну так зачем опять раскапывать стюардессу??
Здравствуйте, _DAle_, Вы писали:
EP>>Помимо того что это не inplace (который был существенной частью оригинальной статьи), и как отметил Александреску тут совсем другой partition, тут ещё не используется приём дающий гарантию на логарифмичестий размер стека worst case, описанный в той же самой оригинальной статье шестидесятых годов. _DA>Можно цитату из оригинальной статьи дающую эту гарантию?
Вот статья Хоара 1962 года (там указывается что была статья и до этой — на ALGOL — но не суть).
Вторая страница, левая колонка, последний абзац:
It is important to know in advance the maximum number of locations used by nest; in order to ensure that the number of segments postponed at any one time never exceeds the logarithm (base 2) of the number of items to be sorted, it is sufficient to adopt the rule of always postponing the processing of larger of of the two segments.
Стек он называет nest'ом.
_DA>В wikipedia это называется Sedgewick variation.
Нет. Где в операция обмена? Потому что, быстрая сортировка — это сортировка обменом. А всякие там "выборы опорных элементов" — это уже модификации сортировки обменом. Просто квиксорту повезло с модификацией, которая в среднем на случайных данных имеет сложность N log N. Но в худшем случая она такая же как и пузырьковая сортировка.
Данная реализация это какой симбиоз сортировки слиянием и выбором.
Здравствуйте, _DAle_, Вы писали:
EP>>Вот на этом этапе алгоритм деления должен быть вполне конкретным — Hoare's partition. _DA>Кому должен? Вот у Кормена, например, используется Lomuto partition,
Это их фейл.
Даже Степанов этот момент упоминал, конкретно Lomuto vs Hoare partition в CLRS (34:18): https://youtu.be/d4tFGtGngf4?t=2058
ЕМНИП даже кто-то из CLRS это в своей лекции что-то такое упоминал.
_DA>и что это теперь не Quicksort, а другой алгоритм?
Да, другой алгоритм, с другой сложностью (абсолютной, не асимптотической), с другими скоростными характеристиками.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Это их фейл. EP>Даже Степанов этот момент упоминал, конкретно Lomuto vs Hoare partition в CLRS (34:18): EP>https://youtu.be/d4tFGtGngf4?t=2058 EP>ЕМНИП даже кто-то из CLRS это в своей лекции что-то такое упоминал.
Окей. Я не согласен. Я считаю, что тот конкретный алгоритм, который когда-то описал Хоар — это одна из реализаций того, что сейчас называется Quicksort (а та конкретная называется сортировкой Хоара). На мой взгляд, сейчас (а не в прошлом веке) по термином Quicksort подразумевают семейство алгоритмов (например, в той же википедии это так), с конкретным подходом: divide and conquer + pivot, и вполне определенной временной сложностью по времени в среднем и худшем случае.
Если считать, что, например, Lomuto partition — это уже не Quicksort, не in-place реализация — не Quicksort, тогда отсутствие хвостовой рекурсии — это тоже не Quicksort? А переход на insertion sort для малых интервалов — это Quicksort или нет? Если Quicksort — это только то, что Хоар описал в 60-х, так она тогда на практике и не используется нигде (в модуле turbo pascal разве что.. а хотя стойте, там же нет хвостовой рекурсии ).
В целом, я за позицию wikipedia и Кормена et al.
Б>Это устраняет проблему выбора первого элемента — как и в оригинале теперь выбирается средний элемент. Сложность алгоритма остается та же O(N*logN) Б>Правда теряется устойчивость сортировки (хотя это можно исправить)
В этом варианте вообще теряется гарантия остановки — в нём нет loop variant. Простой контрпример — список эквивалентных элементов.