2012-06-17 2 views
15

Кто-нибудь знает как ожидаемое время работы, так и наихудшее время работы для разных реализаций std::nth_element? Я использую этот алгоритм почти каждый день.Сложности реализации nth_element

Меня особенно интересуют версии STL, поставляемые с недавними компиляторами Microsoft, но любая информация по этой теме полезна.

Please note that this is not a duplicate of this question. Я понимаю, какие алгоритмы существуют, но мне интересно, какие реализации используют алгоритмы.

Для фона существуют общеизвестные алгоритмы для этого. Один из них - это O (n) средний случай и O (n log n) наихудший случай, один - O (n) наихудший случай, но медленный на практике (медиана медианов). Также обратите внимание, что there is talk of interesting implementation strategies to get worst-case O(n) running times that are fast in practice. В стандарте говорится, что это должно быть хуже O (n) среднего времени.

+0

Стандарт говорит * Сложность: Линейная в среднем. * Вы искали заголовок для реализации? Это может быть началом. – dirkgently

+0

Хорошо, я разъясняю вопрос, основанный на этом. –

+0

Связанная [ошибка] (https://connect.microsoft.com/VisualStudio/feedback/details/184518/incorrect-implementation-of-c-stl-nth-element-algorithm), где вы можете получить некоторое представление об оптимизации в VS. – dirkgently

ответ

16

Ожидаемое время работы: O (N) Наихудшее время работы для большинства реализаций - O (N * N), потому что в большинстве реализаций используется QuickSelect, и может быть, QuickSelect работает с плохими разделами. Это справедливо для Microsoft VS2008, VS2010 & VS2012.

Теперь с новым стандартом ISO C++ 2011 сложность для std :: sort была затянута - гарантируется O (N * log N) и не имеет худшего случая, поскольку используется IntroSort Дэвида Муссера: используйте QuickSort, и если части массива испытывают плохое разделение, замените его на heapsort.

В идеале то же самое должно применяться std :: nth_element, но стандарт ISO C++ 2011 не затягивает требования сложности. Таким образом, std :: nth_element может быть O (N * N) в худшем случае. Это может быть связано с тем, что в оригинальной работе Дэвида Муссера (см. here) он не упомянул, какой алгоритм должен быть заменен, если QuickSelect плохо работает.

В худшем случае можно использовать медианы медианов, использующих группы по 5 (я видел документ, рекомендованный группой из 7, но не могу найти его). Таким образом, качественная реализация std :: nth_element может использовать QuickSelect и обмениваться с медианными медианами, если разбиение на разделы плохое. Это гарантировало бы поведение O (N). QuickSelect можно улучшить, используя выборку, что делает наихудший случай маловероятным, но не невозможным.

+0

Отличный ответ, я только что увидел его. Когда вы говорите «и не имеет худшего случая, поскольку используется IntroSort Дэвида Муссера: используйте QuickSort, и если части массива испытывают плохое разбиение на разделы, замените их на heapsort». вы имеете в виду худший случай O (N * log N) правильно? Или я неправильно понял? –

+0

Привет, Крис, я имею в виду – SJHowe

+0

IntraSelect: использует QuickSelect и свопирует в медианы медианов в группах по 5 элементов, если QS плохо. Средний и худший случай будет O (N). MIcrosoft не проверяет наличие ошибок и свопит их на M-of-M, поэтому их nth_element может быть O (N * N) в худшем случае в прошлый раз, когда я смотрел VS2012. Мне еще предстоит увидеть код VS2013. – SJHowe

0

cppreference говорит, сначала сортирует, а затем находит п-й элемент, но этим способ в среднем должна быть O(n log n) (по сравнению на основе алгоритмов сортировки), но они писали средний O (п), представляется неправильным, за исключением использования сортировки, как радикс sort, ... но поскольку у него есть общий ввод на основе сравнения, кажется, что невозможно использовать сортировку radix или любой другой вид, который не основан на сравнении. во всяком случае, использование быстрых алгоритмов сортировки лучше, чем использование обычного алгоритма выбора на практике (как памяти, так и среднего времени).

+1

Нет, он говорит, что 'std :: nth_element' __partially__ сортирует диапазон' [первый, последний] ', поэтому элемент' nth' находится в правильном месте _as if_ весь диапазон был полностью отсортирован. То, что он делает, ближе к рекурсивному разделу, чем полный сорт. – Blastfurnace

+0

@SaeedAmiri Это, конечно, не полный сорт. Я [написал Wiki стека переполнения] (http://stackoverflow.com/tags/nth-element/info) для 'nth_element', который, я думаю, кратко описывает условия вывода. –

+0

@Blastfurnace, он частично сортируется, но эта сортировка занимает ** O (n logn) ** в ** в среднем **, если это трудно понять, скажите мне, я добавлю доказательство. –

7

Реализация в GCC 4.7 использует introspective selection Дэвида Муссера (здесь у вас есть его paper с подробным описанием интросорта и introselect). Согласно этим документам, наихудшим временем выполнения является O (n).

+0

[Этот gcc bugzilla] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35968), вероятно, имеет значение, поскольку он утверждает, что текущая реализация в libstdC++ не соответствует требованиям стандарта. –

+1

Это просто неправильно. Наихудший случай - O (n log n). Он написан на той же записи в википедии, которую вы связали. – Nate

Смежные вопросы