Прошлой ночью я обсуждал с другим программистом, что, хотя что-то может быть O (1), операция, которая является O (n), может превзойти его, если в алгоритме O (1) есть большая константа. Он не согласен, поэтому я принес это сюда.
Есть ли примеры алгоритмов, которые значительно превосходят те, что в классе ниже? Например, O (n) быстрее, чем O (1), или O (n 2 ) быстрее, чем O (n).
Математически это можно продемонстрировать для функции с асимптотическими верхними границами, когда вы игнорируете постоянные множители, но существуют ли такие алгоритмы в дикой природе? И где я могу найти примеры из них? В каких ситуациях они используются?
algorithms
big-o
KyleWpppd
источник
источник
n
достаточно большим, чтобы компенсировать константу (которая является точкой обозначения big-O).Ответы:
Поиск в очень маленьких, фиксированных таблицах данных. Оптимизированная хеш-таблица может иметь значение O (1) и все же медленнее, чем бинарный поиск или даже линейный поиск из-за стоимости вычисления хеш-функции.
источник
Матричное умножение. Наивный алгоритм O (n ^ 3) часто используется на практике быстрее, чем алгоритм Штрассена O (n ^ 2.8) для матриц малого размера; и Штрассена используется вместо алгоритма O (n ^ 2.3) Копперсмита – Винограда для больших матриц.
источник
Простым примером является разница между различными алгоритмами сортировки. Mergesort, Heapsort и некоторые другие являются O (n log n) . Быстрая сортировка - наихудший случай O (n ^ 2) . Но часто быстрая сортировка выполняется быстрее, и фактически она работает в среднем как O (n log n) . Более подробная информация .
Другим примером является генерация одного числа Фибоначчи. Итерационный алгоритм O (n) , в то время как алгоритм на основе матрицы O (log n) . Тем не менее, для первой пары тысяч чисел Фибоначчи итерационный алгоритм, вероятно, быстрее. Это также зависит от реализации, конечно!
Алгоритмы с лучшей асимптотической эффективностью могут содержать дорогостоящие операции, которые не являются необходимыми для алгоритма с более низкой производительностью, но более простыми операциями. В конце O- примечание говорит нам что-то о производительности, только когда аргумент, с которым он работает, резко возрастает (приближается к бесконечности).
источник
Примечание: Пожалуйста, прочитайте комментарии @ back2dos ниже и других гуру, так как они на самом деле более полезны, чем то, что я написал - спасибо всем авторам.
Я думаю, что из приведенной ниже таблицы (взятой из: обозначение Big O , поиск «Пессимистическая природа алгоритмов:») вы можете видеть, что O (log n) не всегда лучше, чем, скажем, O (n). Итак, я думаю, ваш аргумент верен.
источник
y = 1
,y = log x
и так далее , и пересечениеy = 1
иy = x
на самом деле точка(1,1)
. Если бы это действительно было правильно, чем это говорило бы вам, то алгоритмы более высокой сложности могут быть быстрее для 0 - 2 записей, что людям вряд ли понадобится. То, что граф полностью не учитывает (и из-за чего возникает ощутимая разница в производительности), является постоянными факторами.Для практических ценностей
n
, да. Это очень часто встречается в теории CS. Часто существует сложный алгоритм, который технически имеет лучшую производительность, но постоянные факторы настолько велики, что делают его непрактичным.Однажды мой профессор вычислительной геометрии описал алгоритм триангуляции многоугольника за линейное время, но он закончил словами «очень сложно. Я не думаю, что кто-то на самом деле его реализовал» (!!).
Кроме того, кучи Фибоначчи имеют лучшие характеристики, чем обычные кучи, но не очень популярны, потому что на практике они не так эффективны, как обычные кучи. Это может каскадироваться с другими алгоритмами, которые используют кучи - например, кратчайшие пути Дейкстры математически быстрее с кучей Фибоначчи, но обычно не на практике.
источник
Сравните вставку в связанный список и вставку в массив с изменяемым размером.
Объем данных должен быть достаточно большим, чтобы вставка связанного списка O (1) была полезной.
Связанный список имеет дополнительные накладные расходы для следующих указателей и разыменований. Массив с изменяемым размером должен копировать данные вокруг. Это копирование O (n), но на практике очень быстро.
источник
Обозначение Big-Oh используется для описания скорости роста функции, поэтому возможно, что алгоритм O (1) будет быстрее, но только до определенной точки (постоянный коэффициент).
Общие обозначения:
O (1) - Количество итераций (иногда вы можете называть это временем пользователя, потраченным функцией) не зависит от размера ввода и фактически является постоянным.
O (n) - количество итераций растет линейно пропорционально размеру ввода. Значение: если алгоритм повторяет любые входные данные N, 2 * N раз, он все равно считается O (n).
O (n ^ 2) (квадратичный) - количество итераций равно квадрату входного размера.
источник
Библиотеки Regex, как правило, реализуются для возврата, который имеет экспоненциальное время в худшем случае, а не генерацию DFA, которая имеет сложность
O(nm)
.Наивное обратное отслеживание может быть более эффективным, когда ввод остается на быстром пути или терпит неудачу без необходимости чрезмерного возврата.
(Хотя это решение основано не только на производительности, но и на обратных ссылках.)
источник
O(1)
Алгоритм:O(n)
Алгоритм:Очевидно, что для любого значения
n
гдеn < one_million
, тоO(n)
алгоритм в приведенном примере будет быстрее , чемO(1)
алгоритм.Хотя этот пример немного шутливый, он по духу эквивалентен следующему примеру:
Вы должны знать константы и коэффициенты в своем
O
выражении, и вы должны знать ожидаемый диапазонn
, чтобы априори определить, какой алгоритм будет быстрее.В противном случае вы должны сравнить два алгоритма со значениями
n
в ожидаемом диапазоне, чтобы определить апостериори, какой алгоритм оказался быстрее.источник
Сортировка:
Сортировка вставкой O (n ^ 2), но превосходит другие алгоритмы сортировки O (n * log (n)) для небольшого числа элементов.
По этой причине в большинстве реализаций сортировки используется комбинация двух алгоритмов. Например, используйте сортировку слиянием, чтобы разбивать большие массивы до тех пор, пока они не достигнут определенного размера массива, затем используйте сортировку вставкой, чтобы отсортировать меньшие единицы и снова объединить их с сортировкой слиянием.
См. Timsort о текущей реализации по умолчанию сортировки Python и Java 7, использующей эту технику.
источник
Унификация алгоритм , используемый на практике является экспоненциальным в худшем случае, для некоторых патологических входов.
Существует алгоритм объединения полиномов , но на практике он слишком медленный.
источник
Пузырьковая сортировка в памяти может превзойти быструю сортировку, когда программа выгружается на диск, или при сравнении необходимо считывать каждый элемент с диска.
Это должно быть примером, к которому он может относиться.
источник
Часто более продвинутые алгоритмы предполагают определенную (дорогую) настройку. Если вам нужно запустить его только один раз, вам лучше воспользоваться методом грубой силы.
Например: бинарный поиск и хэш - таблица поиска оба являются гораздо быстрее за поиск то линейный поиск, но они требуют, чтобы отсортировать список или создать хэш - таблицу, соответственно.
Сортировка обойдется вам в N log (N), а хеш-таблица будет стоить как минимум N. Теперь, если вы собираетесь выполнять сотни или тысячи поисков, это все еще амортизированная экономия. Но если вам нужно выполнить только один или два поиска, возможно, имеет смысл просто выполнить линейный поиск и сохранить стоимость запуска.
источник
Расшифровка часто 0 (1). Например, пространство ключей для DES равно 2 ^ 56, поэтому расшифровка любого сообщения является операцией с постоянным временем. Просто у вас есть коэффициент 2 ^ 56, так что это действительно большая константа.
источник
Различные реализации наборов возникают в моей памяти. Одним из наиболее наивных является реализация его по вектору, что означает,
remove
чтоcontains
и, следовательно, такжеadd
все принимают O (N).Альтернативой является реализация этого через некоторый хеш общего назначения, который отображает входные хеш-значения во входные значения. Такой набор выполняет реализацию с O (1) для
add
,contains
иremove
.Если мы предположим, что N около 10 или около того, то первая реализация, вероятно, быстрее. Чтобы найти элемент, достаточно сравнить 10 значений с одним.
Другая реализация должна запустить все виды умных преобразований, которые могут быть намного дороже, чем сделать 10 сравнений. Несмотря на все накладные расходы, вы можете даже иметь ошибки в кэше, и тогда действительно не имеет значения, насколько быстро ваше решение теоретически.
Это не значит, что худшая реализация, о которой вы можете подумать, превзойдет приличную, если N достаточно мало. Для достаточно малого N это просто означает, что наивная реализация с низким занимаемым пространством и накладными расходами может фактически потребовать меньше инструкций и вызывать меньше пропусков кэша, чем реализация, которая ставит масштабируемость на первое место и поэтому будет быстрее.
Вы не можете точно знать, насколько быстро что-либо происходит в сценарии реального мира, пока вы не сложите это в один и просто не измерите. Часто результаты удивляют (по крайней мере, для меня).
источник
Да, для достаточно малого N. Всегда будет N, выше которого у вас всегда будет порядок O (1) <O (lg N) <O (N) <O (N log N) <O (N ^ c ) <O (c ^ N) (где O (1) <O (lg N) означает, что при алгоритме O (1) потребуется меньше операций, когда N достаточно велико и c является некоторой фиксированной константой, которая больше 1 ).
Скажем, конкретный алгоритм O (1) требует ровно f (N) = 10 ^ 100 (гугол) операций, а алгоритм O (N) - ровно g (N) = 2 N + 5 операций. Алгоритм O (N) будет давать большую производительность до тех пор, пока N не станет примерно гуголом (фактически, когда N> (10 ^ 100 - 5) / 2), поэтому, если вы ожидаете, что N будет находиться в диапазоне от 1000 до миллиарда, вы будет страдать от серьезного штрафа с использованием алгоритма O (1).
Или для реалистичного сравнения, скажем, вы умножаете n-значные числа вместе. Алгоритм Карацуба составляет не более 3 п ^ (LG 3) операция (то есть примерно O (N ^ 1,585)) , тогда как алгоритм Шёнхаг-Штрассен является O (N журнал N журнал журнал N) , который представляет собой быстрый порядок , но цитировать википедия:
Так что, если вы умножаете 500-значные числа вместе, не имеет смысла использовать алгоритм, который «быстрее» при больших аргументах O.
РЕДАКТИРОВАТЬ: Вы можете найти определение f (N) по сравнению g (N), взяв предел N-> бесконечность f (N) / g (N). Если предел равен 0, то f (N) <g (N), если предел равен бесконечности, то f (N)> g (N), а если предел - это некоторая другая постоянная, то f (N) ~ g (N) с точки зрения большой нотации.
источник
Симплексный метод линейного программирования может быть экспоненциальным в худшем случае, в то время как относительно новые алгоритмы внутренних точек могут быть полиномиальными.
Однако на практике наихудший экспоненциальный случай для симплекс-метода не подходит - симплекс-метод быстр и надежен, в то время как ранние алгоритмы внутренних точек были слишком медленными, чтобы быть конкурентоспособными. (В настоящее время существуют более современные алгоритмы внутренних точек, которые являются конкурентоспособными, но симплекс-метод тоже ...)
источник
Алгоритм Укконена для построения суффиксных попыток - O (n log n). Преимущество состоит в том, что он «онлайн», то есть вы можете постепенно добавлять больше текста.
В последнее время другие более сложные алгоритмы утверждают, что они быстрее на практике, в основном потому, что их доступ к памяти имеет более высокую локальность, что улучшает использование кэша процессора и позволяет избежать остановок конвейера ЦП. Смотрите, например, этот опрос , в котором утверждается, что 70-80% времени обработки тратится на ожидание памяти, и этот документ описывает алгоритм «wotd».
Суффиксные попытки важны в генетике (для сопоставления последовательностей генов) и, что несколько менее важно, в реализации словарей Scrabble.
источник
Всегда есть любой четко определенной задачи самый быстрый и короткий алгоритм . Это только чисто теоретически (асимптотически) самый быстрый алгоритм.
Учитывая любое описание проблемы P и экземпляр для этой задачи I , он перебирает все возможные алгоритмы и доказательства Pr , проверяя для каждой такой пары ли Pr является допустимым доказательством того, что является асимптотически быстрый алгоритм для P . Если он находит такое доказательство, он затем выполняет A на I .
Поиск этой защищенной от проблем пары имеет сложность O (1) (для фиксированной задачи P ), поэтому вы всегда используете асимптотически быстрый алгоритм для задачи. Однако, поскольку эта константа невероятно огромна почти во всех случаях, этот метод на практике совершенно бесполезен.
источник
Многие языки / платформы используют наивное сопоставление с образцом для сопоставления строк вместо KMP . Мы ищем строку, как Том, Нью-Йорк, а не ababaabababababaababababababab.
источник