Сложности реализации nth_element
Кто-нибудь знает как ожидаемое время работы, так и наихудшее время выполнения для разных реализаций std::nth_element
? Я использую этот алгоритм почти каждый день.
Меня особенно интересуют версии STL, поставляемые с недавними компиляторами Microsoft, но любая информация по этой теме полезна.
Обратите внимание, что это не дубликат этого вопроса. Я понимаю, какие алгоритмы существуют, но мне интересно, какие реализации используют алгоритмы.
Для фона существуют общеизвестные алгоритмы для этого. Один из них - это O (n) средний случай и O (n log n) наихудший случай, один - O (n) наихудший случай, но медленный на практике (медиана медианов).
Также обратите внимание, что говорят о интересных стратегиях реализации, чтобы добиться наихудшего времени работы O (n), которые бывают быстрыми на практике. В стандарте говорится, что это должно быть хуже O (n) среднего времени.
Ответы
Ответ 1
Ожидаемое время работы - O (N)
Самое худшее время выполнения для большинства исполняемых файлов - O (N * N), потому что в большинстве реализаций используется QuickSelect, и может быть, QuickSelect работает с плохими разделами.
Это справедливо для Microsoft VS2008, VS2010 и VS2012.
Теперь с новым стандартом ISO С++ 2011 сложность для std:: sort была затянута - гарантировано будет O (N * log N) и не имеет худшего случая, так как используется IntroSort David Musser: - используйте QuickSort, и если части массива испытывают плохое разбиение на разделы, замените их на heapsort.
В идеале то же самое должно применяться std:: nth_element, но стандарт ISO С++ 2011 не затягивает требования сложности. Таким образом, std:: nth_element может быть O (N * N) в худшем случае. Это может быть связано с тем, что в оригинальной статье Дэвида Муссера (см. здесь) он не упомянул, какой алгоритм должен быть заменен, если QuickSelect плохо работает.
В худшем случае можно использовать медианы медианов, использующих группы из 5 (я видел документ, рекомендованный группой из 7, но не могу найти его). Таким образом, качественная реализация std:: nth_element может использовать QuickSelect и обмениваться с медианными медианами, если разбиение на разделы плохое. Это гарантировало бы поведение O (N). QuickSelect можно улучшить, используя выборку, что делает наихудший случай маловероятным, но не невозможным.
Ответ 2
Реализация в GCC 4.7 использует интроспективный выбор Дэвида Муссера (здесь у вас есть , где подробно рассказывается об интросорте и introselect). Согласно этим документам наихудшее время исполнения - O (n).
Ответ 3
cppreference говорит, сначала он сортирует, а затем находит n-й элемент, но таким образом среднее должно быть O(n log n)
(для сравнения основанные на алгоритмах сортировки), но они написали среднее значение O (n), кажется неправильным, за исключением использования сортировки, такой как сортировка radix,... но поскольку у нее есть общий ввод на основе сравнения, кажется, что невозможно использовать сортировку radix или любой другой вид, который не является сравнение на основе. во всяком случае, использование быстрых алгоритмов сортировки лучше, чем использование обычного алгоритма выбора на практике (как памяти, так и среднего времени).