Что такое простое английское объяснение "Big O"?
Я предпочел бы как можно меньше формального определения и простую математику.
Ответы
Ответ 1
Заметим, что это почти наверняка путает нотацию Big O (верхнюю границу) с тета-нотацией "Θ" (двухстороннюю границу). По моему опыту, это на самом деле типично для дискуссий в неакадемических условиях. Извиняюсь за путаницу.
Сложность Big O может быть визуализирована с помощью этого графика:
Самое простое определение, которое я могу дать для обозначения Big-O, это:
Обозначение Big-O является относительным представлением сложности алгоритма.
В этом предложении есть несколько важных и намеренно выбранных слов:
-
родственник: вы можете сравнить только яблоки с яблоками. Вы не можете сравнить алгоритм для выполнения арифметического умножения с алгоритмом, который сортирует список целых чисел. Но сравнение двух алгоритмов для выполнения арифметических операций (одно умножение, одно сложение) скажет вам что-то значимое;
- представление: Big-O (в простейшем виде) сводит сравнение между алгоритмами к одной переменной. Эта переменная выбирается на основе наблюдений или предположений. Например, алгоритмы сортировки обычно сравниваются на основе операций сравнения (сравнение двух узлов для определения их относительного порядка). Это предполагает, что сравнение стоит дорого. Но что, если сравнение дешево, а обмен дорог? Это меняет сравнение; и
- сложность: если мне понадобится одна секунда, чтобы отсортировать 10000 элементов, сколько времени мне понадобится, чтобы отсортировать миллион? Сложность в этом случае является относительной мерой к чему-то другому.
Вернитесь и перечитайте вышесказанное, когда прочтете остальные.
Лучший пример Big-O, о котором я могу думать, - это арифметика. Возьмите два числа (123456 и 789012). Основными арифметическими операциями, которые мы изучали в школе, были:
-
Добавление;
- вычитание;
- умножение; и
- разделение.
Каждый из них является операцией или проблемой. Метод решения этих проблем называется алгоритмом.
Дополнение самое простое. вы выравниваете числа вверх (вправо) и добавляете цифры в столбце, записывая последний номер этого сложения в результате. Часть "десятки" этого числа переносится в следующий столбец.
Предположим, что сложение этих чисел является самой дорогой операцией в этом алгоритме. само собой разумеется, что для сложения этих двух чисел мы должны сложить вместе 6 цифр (и, возможно, 7). Если мы добавим два 100-значных числа вместе, мы должны сделать 100 добавлений. Если мы добавим два 10000-значных чисел, мы должны сделать 10 000 добавлений.
Видишь шаблон? Сложность complexity (количество операций) прямо пропорциональна количеству цифр n в большем числе. Мы называем это O (n) или линейной сложностью.
Вычитание аналогично (за исключением того, что вам может понадобиться брать взаймы вместо переноса).
Умножение другое. Вы выстраиваете числа вверх, берете первую цифру в нижнем числе и умножаете ее по очереди на каждую цифру в верхнем числе и так далее до каждой цифры. Таким образом, чтобы умножить наши два 6-значных чисел, мы должны сделать 36 умножений. Чтобы получить конечный результат, может потребоваться добавить до 10 или 11 столбцов.
Если у нас есть два 100-значных числа, нам нужно сделать 10 000 умножений и 200 сложений. Для двух миллионных чисел нам нужно сделать триллион (10 12) умножений и два миллиона сложений.
Поскольку алгоритм масштабируется с использованием n-квадрата, это O (n 2) или квадратичная сложность. Настало время представить еще одну важную концепцию:
Мы заботимся только о самой значительной части сложности.
Проницательный, возможно, понял, что мы могли бы выразить количество операций как: n 2 + 2n. Но, как вы видели из нашего примера с двумя числами в миллион цифр за штуку, второй член (2n) становится незначительным (составляя 0,0002% от общего числа операций на этом этапе).
Можно заметить, что мы предположили худший вариант развития событий здесь. При умножении 6-значных чисел, если одно из них является 4-значным, а другое - 6-значным, мы имеем только 24 умножения. Тем не менее мы рассчитываем сценарий наихудшего случая для этого 'n', т.е. когда оба являются 6-значными числами. Следовательно, нотация Big-O относится к наихудшему сценарию алгоритма
Телефонная книга
Следующий лучший пример, который я могу вспомнить, - это телефонная книга, обычно называемая Белыми страницами или похожая, но она будет варьироваться от страны к стране. Но я говорю о том, кто перечисляет людей по фамилии, а затем по инициалам или по имени, возможно, по адресу, а затем по номерам телефонов.
Теперь, если бы вы указали компьютеру найти номер телефона "Джона Смита" в телефонной книге, содержащей 1000000 имен, что бы вы сделали? Игнорируя тот факт, что вы можете догадаться, как далеко в S началось (предположим, вы не можете), что бы вы сделали?
Типичная реализация может состоять в том, чтобы открыть до середины, взять 500 000 th и сравнить его со "Смитом". Если это "Смит, Джон", нам просто повезло. Гораздо более вероятно, что "Джон Смит" будет до или после этого имени. Если это после того, как мы разделим последнюю половину телефонной книги пополам и повторите. Если раньше, то мы разделяем первую половину телефонной книги пополам и повторяем. И так далее.
Это называется двоичным поиском и используется каждый день в программировании, независимо от того, понимаете вы это или нет.
Поэтому, если вы хотите найти имя в телефонной книге из миллиона имен, вы можете найти любое имя, выполнив это не более 20 раз. Сравнивая поисковые алгоритмы, мы решаем, что это сравнение - наше 'n'.
-
Для телефонной книги с 3 именами требуется 2 сравнения (самое большее).
- Для 7 это самое большее 3.
- Для 15 требуется 4.
- ...
- Для 1 000 000 требуется 20.
Это потрясающе хорошо, не так ли?
В терминах Big-O это O (log n) или логарифмическая сложность. Теперь рассматриваемый логарифм может быть ln (база e), log 10, log 2 или какая-либо другая база. Неважно, что это все еще O (log n), точно так же, как O (2n 2) и O (100n 2) по-прежнему оба O (n 2).
На этом этапе стоит объяснить, что Big O можно использовать для определения трех случаев с помощью алгоритма:
-
Лучший случай: В поиске по телефонной книге лучший случай заключается в том, что мы находим имя в одном сравнении. Это O (1) или постоянная сложность;
- Ожидаемый случай: Как обсуждалось выше, это O (log n); и
- В худшем случае: Это также O (log n).
Обычно мы не заботимся о лучшем случае. Нас интересует ожидаемый и наихудший случай. Иногда одно или другое из них будет важнее.
Вернуться к телефонной книге.
Что делать, если у вас есть номер телефона и вы хотите найти имя? У полиции есть обратная телефонная книга, но такие просмотры запрещены для широкой публики. Или они? Технически вы можете изменить поиск номера в обычной телефонной книге. Как?
Вы начинаете с первого имени и сравниваете число. Если это совпадение, отлично, если нет, переходите к следующему. Вы должны сделать это таким образом, потому что телефонная книга неупорядочена (по номеру телефона в любом случае).
Чтобы найти имя по номеру телефона (обратный поиск):
-
Лучший вариант: O (1);
- Ожидаемый случай: O (n) (для 500 000); и
- В худшем случае: O (n) (для 1 000 000).
Путешествующий продавец
Это довольно известная проблема в информатике и заслуживает упоминания. В этой задаче у вас N городов. Каждый из этих городов связан с одним или несколькими другими городами дорогой определенного расстояния. Задача коммивояжера - найти кратчайший тур, который посещает каждый город.
Звучит просто? Подумай еще раз.
Если у вас есть 3 города A, B и C с дорогами между всеми парами, то вы можете пойти:
-
A → B → C
- A → C → B
- B → C → A
- B → A → C
- C → A → B
- C → B → A
Ну, на самом деле там меньше, потому что некоторые из них эквивалентны (A → B → C и C → B → A эквивалентны, например, потому что они используют одни и те же дороги, только наоборот).
На самом деле есть 3 возможности.
-
Отнесите это в 4 города, и у вас будет (iirc) 12 возможностей.
- С 5 это 60.
- 6 становится 360.
Это функция математической операции, называемой факториалом. В основном:
-
5! = 5 × 4 × 3 × 2 × 1 = 120
- 6! = 6 × 5 × 4 × 3 × 2 × 1 = 720
- 7! = 7 × 6 × 5 × 4 × 3 × 2 × 1 = 5040
- ...
- 25! = 25 × 24 ×… × 2 × 1 = 15,511,210,043,330,985,984,000,000
- ...
- 50! = 50 × 49 ×… × 2 × 1 = 3,04140932 × 10 64
Таким образом, главная проблема задачи коммивояжера - O (n!) или факторная или комбинаторная сложность.
К тому времени, как вы доберетесь до 200 городов, во вселенной не останется достаточно времени, чтобы решить проблему с традиционными компьютерами.
Есть о чем подумать.
Полиномиальное время
Еще один момент, который я хотел бы кратко упомянуть, - это то, что любой алгоритм, имеющий сложность O (n a), как говорят, имеет полиномиальную сложность или может быть решен в полиномиальное время.
O (n), O (n 2) и т.д. - все полиномиальное время. Некоторые проблемы не могут быть решены за полиномиальное время. Из-за этого в мире используются определенные вещи. Криптография с открытым ключом является ярким примером. В вычислительном отношении трудно найти два простых фактора очень большого числа. Если бы это было не так, мы не смогли бы использовать системы открытых ключей, которые мы используем.
Во всяком случае, это для моего (надеюсь простого английского) объяснения Big O (исправлено).
Ответ 2
Показывает, как алгоритм масштабируется.
O (n 2): известный как Квадратичная сложность
- 1 элемент: 1 секунда
- 10 элементов: 100 секунд
- 100 предметов: 10000 секунд
Обратите внимание, что количество предметов увеличивается в 10 раз, но время увеличивается в 10 раз 2. В основном, n = 10 и поэтому O (n 2) дает нам масштабный коэффициент n 2 который составляет 10 2.
O (n): известный как Линейная сложность
- 1 элемент: 1 секунда
- 10 предметов: 10 секунд
- 100 предметов: 100 секунд
В этот раз количество предметов увеличивается в 10 раз, а также время. n = 10, поэтому коэффициент масштабирования O (n) равен 10.
O (1): известный как Постоянная сложность
- 1 элемент: 1 секунда
- 10 пунктов: 1 секунда
- 100 предметов: 1 секунда
Количество элементов все еще увеличивается в 10 раз, но коэффициент масштабирования O (1) всегда равен 1.
O (log n): известный как Логарифмическая сложность
- 1 элемент: 1 секунда
- 10 элементов: 2 секунды
- 100 предметов: 3 секунды
- 1000 наименований: 4 секунды
- 10000 элементов: 5 секунд
Количество вычислений увеличивается только на лог входного значения. Поэтому в этом случае, если каждое вычисление занимает 1 секунду, журнал входа n
- это требуемое время, следовательно log n
.
Это суть этого. Они сокращают математику, поэтому она может быть не ровно n 2 или как бы то ни было, но это будет доминирующим фактором в масштабировании.
Ответ 3
Нотация Big-O (также называемая нотацией "асимптотического роста") - это то, на что "похожи" функции, когда вы игнорируете постоянные множители и прочее около начала координат. Мы используем его, чтобы поговорить о масштабах вещей.
Основы
для "достаточно" больших входов...
f(x) ∈ O(upperbound)
означает, что f
"растет не быстрее, чем" upperbound
f(x) ∈ Ɵ(justlikethis)
означает f
"растет точно так же" justlikethis
f(x) ∈ Ω(lowerbound)
означает, что f
"растет не медленнее, чем" lowerbound
нотация big-O не заботится о постоянных факторах: говорят, что функция 9x²
"растет точно так же" 10x²
. Асимптотическая система обозначений big-O также не заботится о неасимптотических вещах ("вещи рядом с источником" или "что происходит, когда размер задачи мал"): говорят, что функция 10x²
"растет точно так же" 10x² - x + 2
.
Почему вы хотите игнорировать меньшие части уравнения? Потому что они сильно затмеваются большими частями уравнения, если учесть все большие и большие масштабы; их вклад становится незначительным и не имеет значения. (См. пример раздела.)
Другими словами, все зависит от отношения по мере приближения к бесконечности. Если вы разделите фактическое время, которое требуется на O(...)
, вы получите постоянный коэффициент в пределе больших входов. Интуитивно это имеет смысл: функции "масштабируются как" друг друга, если вы можете умножить одну, чтобы получить другую. То есть когда мы говорим...
actualAlgorithmTime(N) ∈ O(bound(N))
e.g. "time to mergesort N elements
is O(N log(N))"
... это означает, что для "достаточно больших" размеров задач N (если мы игнорируем вещи возле начала координат), существует некоторая постоянная (например, 2.5, полностью составленная) такая, что:
actualAlgorithmTime(N) e.g. "mergesort_duration(N) "
────────────────────── < constant ───────────────────── < 2.5
bound(N) N log(N)
Есть много вариантов констант; часто "лучший" выбор известен как "постоянный фактор" алгоритма... но мы часто игнорируем его, как игнорируем не самые большие термины (см. раздел "Постоянные факторы", почему они обычно не имеют значения). Вы также можете рассматривать вышеприведенное уравнение как ограничение, говоря: "В худшем случае время, которое оно занимает, никогда не будет хуже, чем примерно N*log(N)
, с коэффициентом 2,5 (постоянный фактор, нас не волнует о)".
В целом, O(...)
является наиболее полезным, потому что мы часто заботимся о поведении в худшем случае. Если f(x)
представляет что-то "плохое", например, использование процессора или памяти, то "f(x) ∈ O(upperbound)
" означает "upperbound
- наихудший сценарий использования процессора/памяти".
Приложения
Как чисто математическая конструкция, запись big-O не ограничивается разговором о времени обработки и памяти. Вы можете использовать это, чтобы обсудить асимптотику всего, где масштабирование имеет смысл, например:
- количество возможных рукопожатий среди
N
людей на вечеринке (Ɵ(N²)
, в частности N(N-1)/2
, но важно то, что оно "масштабируется как" N²
)
- вероятностное ожидаемое число людей, которые видели какой-то вирусный маркетинг как функцию времени
- как масштабируется задержка веб-сайта в зависимости от количества процессорных блоков в процессоре, графическом процессоре или компьютерном кластере
- как тепловая мощность на процессоре умирает в зависимости от количества транзисторов, напряжения и т.д.
- сколько времени нужно запустить алгоритму в зависимости от размера ввода
- сколько места нужно запустить алгоритму в зависимости от размера ввода
Пример
Для приведенного выше примера рукопожатия все в комнате пожимают друг другу руку. В этом примере #handshakes ∈ Ɵ(N²)
. Почему?
Сделайте небольшое резервное копирование: количество рукопожатий в точности равно n-выбирать-2 или N*(N-1)/2
(каждый из N человек пожимает руки другим людям N-1, но это удваивает количество рукопожатий, так что разделите на 2):
Тем не менее, для очень большого числа людей линейный член N
затухает и фактически добавляет 0 к отношению (на диаграмме: доля пустых ящиков на диагонали по отношению к общему числу становится меньше по мере увеличения числа участников), Таким образом, масштабирующим поведением является order N²
, или количество рукопожатий "растет как N²".
#handshakes(N)
────────────── ≈ 1/2
N²
Как будто пустых полей на диагонали диаграммы (N * (N-1)/2 галочки) даже не было (асимптотически N галочки 2).
(временное отступление от "простого английского" :) Если вы хотите доказать это себе, вы можете выполнить некоторую простую алгебру отношения, чтобы разделить его на несколько терминов (lim
означает "рассматривается в пределе", просто игнорируйте) если вы этого не видели, это просто обозначение "а N действительно очень большое"):
N²/2 - N/2 (N²)/2 N/2 1/2
lim ────────── = lim ( ────── - ─── ) = lim ─── = 1/2
N→∞ N² N→∞ N² N² N→∞ 1
┕━━━┙
this is 0 in the limit of N→∞:
graph it, or plug in a really large number for N
tl;dr: количество рукопожатий "выглядит как" x² настолько для больших значений, что, если бы мы записали соотношение # handshakes/x², тот факт, что нам не нужны именно x² рукопожатия, не даже показывать в десятичном виде в течение сколь угодно большого времени.
e.g. for x=1million, ratio #handshakes/x²: 0.499999...
Интуиция здания
Это позволяет нам делать заявления вроде...
"Для достаточно большого входного размера = N, независимо от того, каков постоянный коэффициент, если я удваиваю размер ввода...
- ... Я удваиваю время, затрачиваемое алгоритмом O (N) ("линейное время"). "
N → (2N) = 2(N)
- ... Я удваиваю квадратичное (четырехкратное) время, затрачиваемое алгоритмом O (N²) ("квадратичное время"). "(Например, проблема 100x как большая занимает 100² = 10000x как долго... возможно, неустойчиво)
N² → (2N)² = 4(N²)
- ... Я удваиваю кубы (в восьмерике) времени, которое занимает алгоритм O (N³) ("кубическое время"). "(Например, задача 100x большого размера занимает 100³ = 1000000x длинного... очень неустойчиво)
cN³ → c(2N)³ = 8(cN³)
- ... Я добавляю фиксированное количество ко времени, которое занимает алгоритм O (log (N)) ("логарифмическое время"). "(Дешево!)
c log(N) → c log(2N) = (c log(2))+(c log(N)) = (fixed amount)+(c log(N))
- ... Я не изменяю время, которое занимает алгоритм O (1) ("постоянное время"). "(Самый дешевый!)
c*1 → c*1
- ... Я "(в основном) удваиваю" время, которое занимает алгоритм O (N log (N)). "(Довольно часто)
оно меньше O (N 1.000001), которое вы можете назвать в основном линейным
- ... Я смехотворно увеличиваю время, затрачиваемое алгоритмом O (2 N) ("экспоненциальное время"). "(Вы удвоите (или утроите и т.д.) Время, просто увеличив проблему на единицу)
2N → 22N = (4N)............put another way...... 2N → 2N+1 = 2N21 = 2 2N
[для математически склонных, вы можете навести курсор мыши на спойлеры для незначительных sidenotes]
(с благодарностью fooobar.com/questions/89/...)
(технически постоянный фактор может иметь значение в некоторых более эзотерических примерах, но я сформулировал это выше (например, в журнале (N)) так, что это не так)
Это те залы роста, которые программисты и прикладные компьютерные ученые используют как ориентиры. Они видят это все время. (Таким образом, хотя вы могли бы технически подумать: "Удвоение ввода делает алгоритм O (√N) в 1,414 раз медленнее", лучше думать об этом как "это хуже, чем логарифмический, но лучше, чем линейный".)
Постоянные факторы
Обычно нас не волнует, каковы конкретные постоянные факторы, потому что они не влияют на рост функции. Например, два алгоритма могут занять O(N)
время для завершения, но один может быть в два раза медленнее, чем другой. Мы обычно не слишком заботимся, если фактор не очень велик, так как оптимизация - сложная задача (Когда оптимизация преждевременна?); Кроме того, простое действие по выбору алгоритма с лучшим значением big-O часто повышает производительность на порядки.
Некоторые асимптотически превосходные алгоритмы (например, несопоставимые O(N log(log(N)))
) могут иметь такой большой постоянный коэффициент (например, 100000*N log(log(N))
) или относительно большие издержки, как O(N log(log(N)))
со скрытым + 100*N
, что они редко стоит использовать даже на "больших данных".
Почему O (N) иногда является лучшим, что вы можете сделать, то есть, почему нам нужны структуры данных
Алгоритмы O(N)
в некотором смысле являются "лучшими" алгоритмами, если вам нужно прочитать все ваши данные. Самый акт чтения группы данных - это операция O(N)
. Загрузка его в память обычно O(N)
(или быстрее, если у вас есть аппаратная поддержка, или совсем нет времени, если вы уже прочитали данные). Однако если вы прикоснетесь или даже посмотрите на каждый фрагмент данных (или даже на любой другой фрагмент данных), вашему алгоритму потребуется O(N)
время для выполнения этого просмотра. Не важно, сколько времени займет ваш фактический алгоритм, он будет по крайней мере O(N)
, потому что он потратил это время на просмотр всех данных.
То же самое можно сказать и о самом акте написания. Все алгоритмы, которые распечатывают N вещей, будут занимать N времени, потому что выходной результат, по крайней мере, такой длинный (например, распечатка всех перестановок (способов перестановки) набора из N игральных карт является факториальной: O(N!)
).
Это мотивирует использование структур данных: структура данных требует чтения данных только один раз (обычно O(N)
время), плюс некоторое произвольное количество предварительной обработки (например, O(N)
или O(N log(N))
или O(N²)
) которые мы стараемся держать маленькими. После этого изменение структуры данных (вставки/удаления/и т.д.) И выполнение запросов к данным занимают очень мало времени, например O(1)
или O(log(N))
. Затем вы приступаете к выполнению большого количества запросов! В общем, чем больше работы вы готовы выполнить раньше времени, тем меньше работы вам придется выполнять позже.
Например, скажем, у вас были координаты широты и долготы миллионов сегментов дорог, и вы хотели найти все перекрестки улиц.
- Наивный метод. Если у вас есть координаты пересечения улиц и вы хотите исследовать близлежащие улицы, вам придется каждый раз проходить миллионы отрезков и проверять каждый из них на смежность.
- Если вам нужно было сделать это только один раз, не было бы проблемой, если бы наивный метод
O(N)
работал только один раз, но если вы хотите сделать это много раз (в данном случае, N
раз, один раз). для каждого сегмента) нам нужно выполнить O(N²)
работу или 1000000² = 1000000000000 операций. Не хорошо (современный компьютер может выполнять около миллиарда операций в секунду).
- Если мы используем простую структуру, называемую хеш-таблицей (справочная таблица с мгновенной скоростью, также известная как хэш-карта или словарь), мы платим небольшую плату за предварительную обработку всего за время
O(N)
. После этого в среднем требуется только постоянное время, чтобы найти что-то по его ключу (в этом случае наш ключ - это координаты широты и долготы, округленные в сетку; мы ищем соседние сеточные пространства, которых всего 9, что является константа).
- Наша задача перешла от неосуществимого
O(N²)
к управляемому O(N)
, и все, что нам нужно было сделать, это заплатить небольшую плату за создание хеш-таблицы.
- аналогия: аналогия в данном конкретном случае - это головоломка: мы создали структуру данных, которая использует некоторые свойства данных. Если наши сегменты дороги похожи на кусочки головоломки, мы группируем их по цвету и рисунку. Затем мы используем это, чтобы избежать дополнительной работы позже (сравнивая кусочки головоломки одинакового цвета друг с другом, а не со всеми остальными кусочками головоломки).
Мораль этой истории: структура данных позволяет нам ускорить работу. Даже более продвинутые структуры данных позволяют невероятно умным образом комбинировать, задерживать или даже игнорировать операции. Разные проблемы будут иметь разные аналогии, но все они будут включать в себя организацию данных таким образом, чтобы использовать некоторую структуру, которая нам небезразлична, или которую мы искусственно навязали ей для учета. Мы работаем раньше времени (в основном, планируя и организуя), и теперь повторять задачи намного проще!
Практический пример: визуализация порядка роста при кодировании
Асимптотическая запись по своей сути совершенно отделена от программирования. Асимптотическая нотация - это математическая основа для размышлений о масштабировании вещей, и она может использоваться во многих различных областях. Тем не менее... это то, как вы применяете асимптотические обозначения для кодирования.
Основы: всякий раз, когда мы взаимодействуем с каждым элементом в коллекции размера A (таким как массив, набор, все ключи карты и т.д.), Или выполняем итерации цикла, то есть множитель множителя размера A Почему я говорю "мультипликативный фактор"? Циклы и функции --because (почти по определению) имеют мультипликативное время выполнения: количество итераций, время выполнения работы в цикле (или для функций: количество раз, которое вы вызываете функция, время работы сделано в функции). (Это справедливо, если мы не делаем ничего сложного, например, пропускаем циклы или рано выходим из цикла, или меняем поток управления в функции на основе аргументов, что очень часто встречается.) Вот несколько примеров методов визуализации с сопровождающим псевдокодом.
(здесь x
представляет единицы работы постоянного времени, инструкции процессора, коды операций интерпретатора и т.д.)
for(i=0; i<A; i++) // A * ...
some O(1) operation // 1
--> A*1 --> O(A) time
visualization:
|<------ A ------->|
1 2 3 4 5 x x ... x
other languages, multiplying orders of growth:
javascript, O(A) time and space
someListOfSizeA.map((x,i) => [x,i])
python, O(rows*cols) time and space
[[r*c for c in range(cols)] for r in range(rows)]
Пример 2:
for every x in listOfSizeA: // A * (...
some O(1) operation // 1
some O(B) operation // B
for every y in listOfSizeC: // C * (...
some O(1) operation // 1))
--> O(A*(1 + B + C))
O(A*(B+C)) (1 is dwarfed)
visualization:
|<------ A ------->|
1 x x x x x x ... x
2 x x x x x x ... x ^
3 x x x x x x ... x |
4 x x x x x x ... x |
5 x x x x x x ... x B <-- A*B
x x x x x x x ... x |
................... |
x x x x x x x ... x v
x x x x x x x ... x ^
x x x x x x x ... x |
x x x x x x x ... x |
x x x x x x x ... x C <-- A*C
x x x x x x x ... x |
................... |
x x x x x x x ... x v
Пример 3:
function nSquaredFunction(n) {
total = 0
for i in 1..n: // N *
for j in 1..n: // N *
total += i*k // 1
return total
}
// O(n^2)
function nCubedFunction(a) {
for i in 1..n: // A *
print(nSquaredFunction(a)) // A^2
}
// O(a^3)
Если мы сделаем что-то немного сложное, вы все равно сможете визуально представить, что происходит:
for x in range(A):
for y in range(1..x):
simpleOperation(x*y)
x x x x x x x x x x |
x x x x x x x x x |
x x x x x x x x |
x x x x x x x |
x x x x x x |
x x x x x |
x x x x |
x x x |
x x |
x___________________|
Здесь важен наименьший узнаваемый контур, который вы можете нарисовать; треangularьник - это двумерная форма (0,5 A ^ 2), точно так же, как квадрат - это двумерная форма (A ^ 2); постоянный множитель два здесь остается в асимптотическом соотношении между ними, однако мы игнорируем его, как и все факторы... (У этой техники есть некоторые прискорбные нюансы, в которые я не вхожу; она может ввести вас в заблуждение.)
Конечно, это не означает, что циклы и функции плохие; напротив, они являются строительными блоками современных языков программирования, и мы их любим. Однако мы видим, что способ, которым мы плетем циклы, функции и условия вместе с нашими данными (поток управления и т.д.), Имитирует использование нашей программы во времени и пространстве! если использование времени и пространства становится проблемой, то тогда мы прибегаем к хитрости и находим простой алгоритм или структуру данных, которые мы не рассматривали, чтобы каким-то образом уменьшить порядок роста. Тем не менее, эти методы визуализации (хотя они не всегда работают) могут дать вам наивное предположение в худшем случае.
Вот еще одна вещь, которую мы можем узнать визуально:
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
Мы можем просто изменить это и увидеть это O (N):
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x|x x x x x x x x|x x x x|x x|x
Или, может быть, вы делаете журнал (N) проходов данных, за O (N * log (N)) общее время:
<----------------------------- N ----------------------------->
^ x x x x x x x x x x x x x x x x|x x x x x x x x x x x x x x x x
| x x x x x x x x|x x x x x x x x|x x x x x x x x|x x x x x x x x
lgN x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x
| x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x
v x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x
Не имеет значения, но стоит упомянуть еще раз: если мы выполним хэш (например, поиск по словарю/хеш-таблице), то это фактор O (1). Это довольно быстро.
[myDictionary.has(x) for x in listOfSizeA]
\----- O(1) ------/
--> A*1 --> O(A)
Если мы делаем что-то очень сложное, например, с помощью рекурсивной функции или алгоритма "разделяй и властвуй", вы можете использовать основную теорему (обычно работает) или в смешных случаях Akra - Теорема Бацци (почти всегда работает) вы смотрите время выполнения вашего алгоритма в Википедии.
Но программисты так не думают, потому что в конечном итоге интуиция алгоритма становится второй натурой. Вы начнете кодировать что-то неэффективное и сразу же подумаете: "Я делаю что-то крайне неэффективное?". Если ответ "да" И вы предвидите, что это действительно имеет значение, тогда вы можете сделать шаг назад и подумать о различных хитростях, чтобы заставить вещи работать быстрее (ответ почти всегда "использовать хеш-таблицу", редко "использовать дерево", и очень редко что-то более сложное).
Амортизированная и средняя сложность
Существует также понятие "амортизированный" и/или "средний случай" (обратите внимание, что они разные).
Средний регистр: это не более, чем использование нотации big-O для ожидаемого значения функции, а не самой функции. В обычном случае, когда вы считаете, что все входные данные одинаково вероятны, средний случай - это просто среднее время выполнения. Например, для быстрой сортировки, даже если наихудший случай O(N^2)
для некоторых действительно плохих входов, средний случай - обычный O(N log(N))
(действительно плохих входов очень мало, поэтому их мало, и мы их не замечаем) в среднем случае).
Амортизированный наихудший случай. Некоторые структуры данных могут иметь большую сложность в худшем случае, но гарантируют, что при выполнении многих из этих операций средний объем выполняемой вами работы будет лучше, чем в худшем случае. Например, у вас может быть структура данных, которая обычно занимает постоянное время O(1)
. Тем не менее, иногда он будет "икать" и тратит время O(N)
на одну случайную операцию, потому что, возможно, ему нужно будет провести какую-то бухгалтерию или сборку мусора или что-то в этом роде... но он обещает вам, что если он икнет, он не будет икать снова для N больше операций. В худшем случае стоимость по-прежнему составляет O(N)
на операцию, но амортизированная стоимость для многих прогонов составляет O(N)/N
= O(1)
на операцию. Поскольку крупные операции достаточно редки, можно считать, что огромное количество случайных работ сливается с остальной работой как постоянный фактор. Мы говорим, что работа "амортизируется" из-за достаточно большого количества вызовов, что она исчезает асимптотически.
Аналогия для амортизированного анализа:
Вы водите машину. Иногда вам нужно потратить 10 минут на АЗС, а затем потратить 1 минуту, заправляя бак газом. Если вы делали это каждый раз, когда ездили на машине куда угодно (потратить 10 минут езды до заправки, потратьте несколько секунд на заполнение доля галлона), это было бы очень неэффективно. Но если вы заполните один раз в несколько дней, 11 минут потратили на АЗС "амортизируется" за достаточно большое количество поездок, что вы можете игнорировать это и делать вид, что все ваши поездки были, возможно, на 5% длиннее.
Сравнение среднего и амортизированного наихудшего случая:
- Средний случай: мы делаем некоторые предположения о наших входных данных; то есть, если наши входы имеют разные вероятности, то наши выходы/время выполнения будут иметь разные вероятности (из которых мы берем среднее значение). Обычно мы предполагаем, что все наши входные данные одинаково вероятны (равномерная вероятность), но если реальные входные данные не соответствуют нашим предположениям о "среднем входном сигнале", вычисления среднего выходного значения/времени выполнения могут быть бессмысленными. Однако если вы ожидаете равномерно случайных входных данных, об этом полезно подумать!
- Амортизированный наихудший случай: если вы используете амортизированную структуру данных наихудшего случая, производительность гарантированно будет находиться в пределах амортизированного наихудшего случая... в конце концов (даже если входные данные выбираются злым демоном, который знает все и пытается пошутил) Обычно мы используем это для анализа алгоритмов, которые могут быть очень "нестабильными" по производительности с неожиданными большими сбоями, но со временем работают так же, как и другие алгоритмы. (Однако, если ваша структура данных не имеет верхних пределов для большой выдающейся работы, на которую она готова откладывать, злой злоумышленник, возможно, заставит вас наверстать упущенное на максимальном объеме откладываемой работы одновременно.
Хотя, если вы беспокоитесь о злоумышленнике, есть много других алгоритмических векторов атаки, о которых следует беспокоиться, кроме амортизации и среднего случая.)
И средний случай, и амортизация являются невероятно полезными инструментами для размышлений и проектирования с учетом масштабирования.
(См. Разница между средним случаем и амортизированным анализом, если вы заинтересованы в этой подтеме.)
Многомерный биг-о
В большинстве случаев люди не понимают, что на работе более одной переменной. Например, в алгоритме поиска строки ваш алгоритм может занять время O([length of text] + [length of query])
, то есть он является линейным по двум переменным, таким как O(N+M)
. Другими более наивными алгоритмами могут быть O([length of text]*[length of query])
или O(N*M)
. Игнорирование нескольких переменных является одним из наиболее распространенных недостатков, которые я вижу при анализе алгоритмов, и может помешать вам при разработке алгоритма.
Вся история
Имейте в виду, что big-O - это еще не все. Вы можете значительно ускорить некоторые алгоритмы, используя кеширование, делая их не обращающими внимания на кеш, избегая узких мест, работая с оперативной памятью вместо диска, используя распараллеливание или опережая работу - эти методы часто не зависят от порядка роста нотация big-O, хотя вы часто будете видеть количество ядер в нотации big-O параллельных алгоритмов.
Также имейте в виду, что из-за скрытых ограничений вашей программы вы можете не заботиться об асимптотическом поведении. Возможно, вы работаете с ограниченным числом значений, например:
- Если вы сортируете что-то вроде 5 элементов, вы не хотите использовать быструю быструю сортировку
O(N log(N))
; Вы хотите использовать сортировку вставкой, которая хорошо работает на небольших входах. Такие ситуации часто встречаются в алгоритмах "разделяй и властвуй", где вы разбиваете задачу на все более мелкие подзадачи, такие как рекурсивная сортировка, быстрые преобразования Фурье или умножение матриц.
- Если некоторые значения эффективно ограничены из-за какого-то скрытого факта (например, среднее человеческое имя мягко ограничено, возможно, 40 буквами, а человеческий возраст мягко ограничен около 150). Вы также можете наложить ограничения на ввод, чтобы эффективно сделать условия постоянными.
На практике даже среди алгоритмов, которые имеют одинаковую или сходную асимптотическую производительность, их относительные достоинства могут фактически зависеть от других факторов, таких как: другие факторы производительности (quicksort и mergesort оба O(N log(N))
, но быстрая сортировка использует преимущества кэшей ЦП); соображения неэффективности, такие как простота реализации; доступна ли библиотека, насколько авторитетна и поддерживается библиотека.
Программы также будут работать медленнее на компьютере с частотой 500 МГц и 2 ГГц. На самом деле мы не рассматриваем это как часть границ ресурса, потому что мы думаем о масштабировании в терминах машинных ресурсов (например, за тактовый цикл), а не за реальную секунду. Однако есть похожие вещи, которые могут "тайно" влиять на производительность, например, работаете ли вы под эмуляцией или оптимизировал код компилятор или нет. Это может заставить некоторые базовые операции занимать больше времени (даже относительно друг друга) или даже асимптотически ускорять или замедлять некоторые операции (даже относительно друг друга). Эффект может быть небольшим или большим между различными реализациями и/или средой. Вы переключаете языки или машины, чтобы выполнить эту небольшую дополнительную работу? Это зависит от сотни других причин (необходимость, навыки, коллеги, производительность труда программиста, денежная ценность вашего времени, знакомство, обходные пути, почему бы не сборка или графический процессор и т.д.), Которые могут быть важнее производительности.
Вышеупомянутые проблемы, такие как язык программирования, почти никогда не рассматриваются как часть постоянного фактора (и не должны быть); все же нужно знать о них, потому что иногда (хотя редко) они могут влиять на вещи. Например, в cpython реализация очереди с собственным приоритетом асимптотически неоптимальна (O(log(N))
, а не O(1)
для вашего выбора вставки или find-min); Вы используете другую реализацию? Вероятно, нет, так как реализация C, вероятно, быстрее, и, возможно, есть другие подобные проблемы в других местах. Есть компромиссы; иногда они имеют значение, а иногда нет.
(редактировать: на этом "простое английское" объяснение заканчивается.)
Математические дополнения
Для полноты точное определение обозначения big-O выглядит следующим образом: f(x) ∈ O(g(x))
означает, что "f асимптотически upper- ограничено const * g": игнорируя все ниже некоторого конечного значения x, существует постоянная такая, что |f(x)| ≤ const * |g(x)|
. (Другие символы следующие: точно так же, как O
означает ≤, Ω
означает ≥. Существуют строчные варианты: o
означает & lt;, а ω
означает>.) f(x) ∈ Ɵ(g(x))
означает оба f(x) ∈ O(g(x))
и f(x) ∈ Ω(g(x))
(upper- и ограничен снизу g): существуют некоторые константы, такие что f всегда будет лежать в "полосе" между const1*g(x)
и const2*g(x)
. Это самое сильное асимптотическое утверждение, которое вы можете сделать и примерно эквивалентное ==
. (Извините, я решил отложить упоминание символов абсолютного значения до сих пор, для ясности; особенно потому, что я никогда не видел, чтобы отрицательные значения возникали в контексте компьютерной науки.)
Люди будут часто использовать = O(...)
, что, возможно, является более правильной нотацией "comp-sci" и совершенно законно для использования; "f = O (...)" читается как "f - порядок... /f ограничен ххх..." и рассматривается как "f - некоторое выражение, асимптотика которого...". Меня учили использовать более строгие ∈ O(...)
. ∈
означает "является элементом" (все еще читается как прежде). В этом конкретном случае O(N²)
содержит такие элементы, как {2 N²
, 3 N²
, 1/2 N²
, 2 N² + log(N)
, - N² + N^1.9
,...}, и является бесконечно большим, но это все еще набор.
O и Ω не являются симметричными (n = O (n²), но n² не является O (n)), но Ɵ симметрична, и (поскольку все эти отношения транзитивны и рефлексивны), следовательно, симметрична, транзитивна и рефлексивна, и поэтому разбивает множество всех функций на классы эквивалентности. Класс эквивалентности - это набор вещей, которые мы считаем одинаковыми. Иными словами, для любой функции, которую вы можете придумать, вы можете найти канонического/уникального "асимптотического представителя" класса (обычно беря предел... я думаю); точно так же, как вы можете сгруппировать все целые числа в шансы или четности, вы можете сгруппировать все функции с помощью Ɵ в x-ish, log (x) ^ 2-ish и т.д., в основном игнорируя более мелкие термины (но иногда вы можете застрять с более сложные функции, которые являются отдельными классами для себя).
Нотация =
может быть более распространенной и даже используется в работах всемирно известных компьютерных ученых. Кроме того, часто случается, что в случайной обстановке люди говорят O(...)
, когда они имеют в виду Ɵ(...)
; это технически верно, поскольку набор вещей Ɵ(exactlyThis)
является подмножеством O(noGreaterThanThis)
... и его легче набирать. ;-)
Ответ 4
РЕДАКТИРОВАТЬ: Быстрое замечание, это почти наверняка смущает нотация Big O (которая является верхней границей) с обозначением Theta (что является одновременно и верхняя и нижняя границы). По моему опыту, это типично для дискуссий в неакадемических условиях. Извинения за любую путаницу.
В одном предложении: по мере увеличения размера вашей работы, сколько времени потребуется, чтобы завершить ее?
Очевидно, что только использование "размера" в качестве входного сигнала и "время, принятое" в качестве вывода — эта же идея применяется, если вы хотите поговорить об использовании памяти и т.д.
Вот пример, где у нас есть N футболок, которые мы хотим высушить. Мы предположим, что невероятно быстро получить их в положении сушки (т.е. Взаимодействие человека незначительно). Это, конечно, не в реальной жизни...
-
Использование стиральной линии снаружи: при условии, что у вас бесконечно большой задний двор, мытье высыхает в течение O (1). Как бы то ни было, у него будет такое же солнце и свежий воздух, поэтому размер не влияет на время высыхания.
-
Использование сушилки для сушки: вы накладываете 10 рубашек на каждую нагрузку, а затем через час. (Игнорируйте фактические цифры здесь, они не имеют значения.) Таким образом, сушка 50 рубашек занимает примерно в 5 раз дольше, чем высушивание 10 рубашек.
-
Вкладывая все в шкафчик для воздуха: если мы поместим все в одну большую кучу и просто дадим общей теплоте, это займет много времени, пока средние рубашки не высохнут. Я не хотел бы догадываться о деталях, но я подозреваю, что это, по крайней мере, O (N ^ 2) — по мере увеличения нагрузки на стирку, время сушки увеличивается быстрее.
Одним из важных аспектов нотации "большой O" является то, что он не говорит, какой алгоритм будет быстрее для заданного размера. Возьмите хэш-таблицу (строковый ключ, целочисленное значение) и массив пар (строка, целое число). Быстрее найти ключ в хэш-таблице или элемент в массиве на основе строки? (т.е. для массива "найдите первый элемент, в котором строковая часть соответствует заданному ключу".) Хэш-таблицы обычно амортизируются (~ = "в среднем" ) O (1) — после того, как они настроены, для поиска записи в таблице с 100 входами потребуется примерно одно и то же время, как в таблице ввода в 1000 000. Поиск элемента в массиве (на основе контента, а не индекса) является линейным, то есть O (N) — в среднем вам придется посмотреть на половину записей.
Это делает хэш-таблицу быстрее, чем массив для поиска? Не обязательно. Если у вас очень маленькая коллекция записей, массив может быть быстрее и быстрее; вы можете проверить все строки в то время, которое требуется, чтобы просто вычислить хэш-код того, на который вы смотрите. Однако, поскольку набор данных становится все больше, хэш-таблица в конечном итоге будет бить массив.
Ответ 5
Big O описывает верхний предел поведения роста функции, например время выполнения программы, когда входы становятся большими.
Примеры:
-
O (n): если я удваиваю размер ввода, время выполнения удваивается
-
O (n 2): Если размер ввода удваивает количество циклов выполнения
-
O (log n): Если размер ввода удваивается, время выполнения увеличивается на один
-
O (2 n): если размер ввода увеличивается на единицу, время выполнения удваивается
Размер ввода обычно представляет собой пробел в битах, необходимых для представления ввода.
Ответ 6
Обозначение Big O чаще всего используется программистами как приблизительная мера того, как долго будет выполняться вычисление (алгоритм) для полного выражения в зависимости от размера входного набора.
Big O полезен для сравнения того, насколько хорошо будут увеличиваться два алгоритма по мере увеличения количества входов.
Более точно Знак Big O используется для выражения асимптотического поведения функции. Это означает, что функция ведет себя по мере приближения к бесконечности.
Во многих случаях "O" алгоритма попадает в один из следующих случаев:
- O (1). Время завершения одинаково независимо от размера входного набора. Примером является доступ к элементу массива по индексу.
- O (Log N). Время завершения увеличивается примерно в соответствии с log2 (n). Например, 1024 элемента занимают примерно вдвое длиннее 32 элементов, так как Log2 (1024) = 10 и Log2 (32) = 5. Примером является поиск элемента в двоичное дерево поиска (BST).
- O (N) - время завершения, которое масштабируется линейно с размером входного набора. Другими словами, если вы удвоите количество элементов во входном наборе, алгоритм займет примерно вдвое больше. Примером является подсчет количества элементов в связанном списке.
- O (N Log N) - время завершения увеличивается на количество элементов, умноженное на результат Log2 (N). Примером этого является куча сортировки и быстрая сортировка.
- O (N ^ 2). Время завершения примерно равно квадрату числа элементов. Примером этого является сортировка пузырьков.
- O (N!). Время завершения - это факторный набор входных данных. Примером этого является решение проблемы коммивояжера.
Big O игнорирует факторы, которые не вносят существенного вклада в кривую роста функции, поскольку размер ввода увеличивается в сторону бесконечности. Это означает, что константы, которые добавляются или умножаются функцией, просто игнорируются.
Ответ 7
Big O - это просто способ "выразить" себя обычным способом: "Сколько времени и пространства требуется для запуска моего кода?".
Вы можете часто видеть O (n), O (n 2), O (nlogn) и т.д., все это просто способы показать; Как изменяется алгоритм?
O (n) означает, что Big O является n, и теперь вы можете подумать: "Что такое n!?" Ну "n" - количество элементов. Imaging вы хотите искать элемент в массиве. Вам нужно будет посмотреть на каждый элемент и как "Вы правильный элемент/элемент?" в худшем случае элемент находится по последнему индексу, а это значит, что потребовалось столько же времени, сколько есть элементов в списке, поэтому, чтобы быть общим, мы говорим: "О, эй, я - справедливое заданное количество ценностей!".
Итак, вы можете понять, что означает "n 2", но, чтобы быть более конкретным, играйте с мыслью, что у вас есть простой, самый простой алгоритм сортировки; BubbleSort. Этот алгоритм должен просматривать весь список для каждого элемента.
Мой список
Здесь поток:
- Сравните 1 и 6, что является самым большим? Ок 6 находится в правильном положении, двигаясь вперед!
- Сравните 6 и 3, о, 3 меньше! Давайте переместим это, Ok, список изменился, нам нужно начать с самого начала!
Это O n 2 потому что вам нужно посмотреть на все элементы в списке, где есть "n". Для каждого элемента вы снова смотрите на все предметы, для сравнения это также "n", поэтому для каждого элемента вы смотрите "n" раз, что означает n * n = n 2
Надеюсь, это так просто, как вы этого хотите.
Но помните, что Big O - это просто способ, чтобы вы себя вписывали в манеру времени и пространства.
Ответ 8
Big O описывает фундаментальный масштабный алгоритм.
Существует много информации о том, что Big O не говорит вам о данном алгоритме. Он сокращается до кости и дает только информацию о масштабирующем характере алгоритма, в частности, как использование ресурсов (время мысли или память) алгоритма масштабируется в ответ на "размер ввода".
Рассмотрим разницу между паровым двигателем и ракетой. Они не просто разные разновидности одного и того же (как, скажем, двигатель Prius и двигатель Lamborghini), но они представляют собой совершенно разные виды силовых установок. Паровой двигатель может быть быстрее, чем игрушечная ракета, но паровой поршневой двигатель не сможет достичь скорости орбитальной ракеты-носителя. Это связано с тем, что эти системы имеют разные масштабирующие характеристики в отношении соотношения требуемого топлива ( "использование ресурсов" ) для достижения заданной скорости ( "размер входного сигнала" ).
Почему это так важно? Потому что программное обеспечение занимается проблемами, которые могут отличаться по размеру до трех триллионов. Подумайте об этом на мгновение. Соотношение между скоростью, необходимой для перемещения на Луну и скоростью ходьбы человека, составляет менее 10 000: 1, и это абсолютно крошечный по сравнению с диапазоном программного обеспечения с размерами входных данных. И поскольку программное обеспечение может столкнуться с астрономическим диапазоном в размерах входных данных, существует потенциал для сложности алгоритма Big O, его фундаментальный масштабный характер, чтобы превзойти любые детали реализации.
Рассмотрим пример канонической сортировки. Bubble-sort - это O (n 2), а merge-sort - O (n log n). Скажем, у вас есть два приложения для сортировки, приложение A, которое использует сортировку пузырьков и приложение B, которое использует сортировку слиянием, и пусть говорят, что для размеров ввода около 30 элементов приложение A на 1000 раз быстрее, чем приложение B при сортировке. Если вам никогда не придется сортировать более 30 элементов, то очевидно, что вам следует отдать предпочтение приложению A, поскольку оно намного быстрее при этих размерах ввода. Однако, если вы обнаружите, что вам, возможно, придется сортировать десять миллионов элементов, то то, что вы ожидаете, - это то, что приложение B фактически заканчивается в тысячи раз быстрее, чем приложение A в этом случае, полностью из-за того, как каждый алгоритм масштабируется.
Ответ 9
Вот простой английский бестиарий, который я обычно использую при объяснении общих разновидностей Big-O
Во всех случаях предпочитайте алгоритмы выше в списке тем, которые ниже в списке. Однако стоимость перехода на более дорогой класс сложности значительно варьируется.
O (1):
Нет роста. Независимо от того, насколько велика проблема, вы можете решить ее за такое же время. Это несколько аналогично вещанию, где требуется столько же энергии для трансляции на заданном расстоянии, независимо от количества людей, которые находятся в диапазоне вещания.
O (log n):
Эта сложность такая же, как O (1), за исключением того, что она немного хуже. Для всех практических целей вы можете рассматривать это как очень большое постоянное масштабирование. Разница в работе между обработкой 1 тыс. И 1 млрд. Единиц составляет всего лишь шесть.
О (п):
Стоимость решения проблемы пропорциональна размеру проблемы. Если ваша проблема удваивается по размеру, стоимость решения удваивается. Поскольку большинство проблем необходимо каким-то образом отсканировать в компьютер, как ввод данных, чтение дисков или сетевой трафик, это, как правило, доступный коэффициент масштабирования.
O (n log n):
Эта сложность очень похожа на O (n). Для всех практических целей они эквивалентны. Этот уровень сложности, как правило, по-прежнему считается масштабируемым. Путем уточнения предположений некоторые алгоритмы O (n log n) могут быть преобразованы в алгоритмы O (n). Например, ограничение размера клавиш уменьшает сортировку от O (n log n) до O (n).
О (п 2):
Растет как квадрат, где n - длина стороны квадрата. Это тот же самый темп роста, что и "сетевой эффект", где каждый в сети может знать всех остальных в сети. Рост дорог. Большинство масштабируемых решений не могут использовать алгоритмы с таким уровнем сложности, не выполняя значительную гимнастику. Это обычно относится ко всем другим многочленам сложности - O (n k) - также.
О (2 п):
Не масштабируется. У вас нет надежды на решение какой-либо нестандартной проблемы. Полезно знать, чего следует избегать, и для экспертов найти приближенные алгоритмы, которые находятся в O (n k).
Ответ 10
Big O - это показатель того, сколько времени/пространства использует алгоритм относительно размера его ввода.
Если алгоритм O (n), то время/пространство будет увеличиваться с той же скоростью, что и его вход.
Если алгоритм O (n 2), то время/пространство увеличивается со скоростью его квадрата ввода.
и т.д.
Ответ 11
Что такое простое английское объяснение Big O? С как можно меньшим формальным определением и простой математикой.
Простое английское объяснение необходимости нотации Big-O:
Когда мы программируем, мы пытаемся решить проблему. То, что мы кодируем, называется алгоритмом. Обозначение Big O позволяет стандартизовать худшую производительность наших алгоритмов. Спецификации оборудования меняются со временем, а усовершенствования аппаратного обеспечения могут сократить время, затрачиваемое на выполнение алгоритмов. Но замена аппаратного обеспечения не означает, что наш алгоритм будет лучше или улучшен с течением времени, так как наш алгоритм все тот же. Поэтому, чтобы позволить нам сравнивать разные алгоритмы, чтобы определить, лучше ли это или нет, мы используем нотацию Big O.
Обычный английский Объяснение того, что такое Big O Notation:
Не все алгоритмы работают за один и тот же промежуток времени и могут варьироваться в зависимости от количества элементов на входе, которые мы будем называть n. Исходя из этого, мы рассматриваем анализ худшего случая или верхнюю границу времени выполнения, когда n становится больше и больше. Мы должны знать, что такое n, потому что многие из нот Big O ссылаются на него.
Ответ 12
Очень сложно измерить скорость работы программ, и когда мы попытаемся, ответы могут быть очень сложными и заполнены исключениями и особыми случаями. Это большая проблема, потому что все эти исключения и особые случаи отвлекают и бесполезны, когда мы хотим сравнить две разные программы друг с другом, чтобы узнать, что является "самым быстрым".
В результате всей этой бесполезной сложности люди пытаются описать скорость программ, используя наименьшие и наименее сложные (математические) выражения. Эти выражения являются очень грубыми приближениями: хотя, с некоторой удачей, они поймут "суть" того, быстро ли или медленно работает часть программного обеспечения.
Поскольку они являются приближениями, мы используем в выражении букву "O" (Big Oh) в качестве условного обозначения, чтобы сообщить читателю, что мы делаем грубое упрощение. (И чтобы убедиться, что никто не ошибочно думает, что выражение в любом случае точнее).
Если вы читаете "О" как значение "по порядку" или "приблизительно", вы не ошибетесь. (Я думаю, что выбор Big-Oh мог быть попыткой юмора).
Единственное, что эти выражения "Big-Oh" пытаются сделать, это описать, насколько программное обеспечение замедляется, поскольку мы увеличиваем объем данных, которые программное обеспечение должно обрабатывать. Если мы удваиваем объем данных, которые необходимо обработать, требуется ли в два раза больше программного обеспечения, чтобы завершить работу? Десять раз? На практике существует очень ограниченное количество выражений большого О-О, с которыми вы столкнетесь и о чем беспокоиться:
Хорошее:
-
O(1)
Константа. Программа выполняет одно и то же время для запуска независимо от того, насколько велик вход.
-
O(log n)
Логарифмическая: время выполнения программы увеличивается только медленно, даже при большом увеличении размера ввода.
Плохо:
-
O(n)
Линейный: время выполнения программы увеличивается пропорционально размеру ввода.
-
O(n^k)
Полиномиальный: - время обработки растет быстрее и быстрее - как функция полинома - по мере увеличения размера ввода.
... и уродливый:
-
O(k^n)
Экспоненциальный Время выполнения программы очень быстро увеличивается даже при умеренном увеличении размера проблемы - практично обрабатывать небольшие наборы данных с помощью экспоненциальных алгоритмов.
-
O(n!)
Факториал Продолжительность программы будет больше, чем вы можете позволить себе ждать чего угодно, кроме самых маленьких и самых тривиальных, кажущихся наборов данных.
Ответ 13
Хорошо, мои 2центы.
Big-O, скорость увеличения ресурса, потребляемого программой, w.r.t. Проблема-экземпляр размера
Ресурс: может быть общее время процессора, может быть максимальным объемом оперативной памяти. По умолчанию используется процессорное время.
Скажем, что проблема заключается в "Найти сумму",
int Sum(int*arr,int size){
int sum=0;
while(size-->0)
sum+=arr[size];
return sum;
}
problem-instance = {5,10,15} == > problem-instance-size = 3, iterations-in-loop = 3
problem-instance = {5,10,15,20,25} == > problem-instance-size = 5 iterations-in-loop = 5
Для ввода размера "n" программа растет со скоростью "n" итераций в массиве. Следовательно, Big-O есть N, выраженное как O (n)
Скажем, что проблема заключается в "Найти комбинацию",
void Combination(int*arr,int size)
{ int outer=size,inner=size;
while(outer -->0) {
inner=size;
while(inner -->0)
cout<<arr[outer]<<"-"<<arr[inner]<<endl;
}
}
problem-instance = {5,10,15} == > problem-instance-size = 3, total-iterations = 3 * 3 = 9
problem-instance = {5,10,15,20,25} == > problem-instance-size = 5, total-iterations = 5 * 5 = 25
Для ввода размера "n" программа растет со скоростью "n * n" итераций в массиве. Следовательно, Big-O является N 2 выраженным как O (n 2)
Ответ 14
Простым простым ответом может быть:
Big O представляет собой наихудшее возможное время/пространство для этого алгоритма. Алгоритм никогда не будет занимать больше места/времени выше этого предела. Big O представляет сложность времени/пространства в крайнем случае.
Ответ 15
Обозначение Big O - это способ описания верхней границы алгоритма в терминах пространства или времени выполнения. N - количество элементов в задаче (размер массива, количество узлов в дереве и т.д.). Нам интересно описать время выполнения, когда n становится большим.
Когда мы говорим, что какой-то алгоритм O (f (n)), мы говорим, что время выполнения (или требуемое пространство) этим алгоритмом всегда ниже, чем некоторые постоянные времена f (n).
Сказать, что бинарный поиск имеет время работы O (logn), означает, что существует некоторая константа c, которую вы можете умножить на log (n), которая всегда будет больше, чем время работы бинарного поиска. В этом случае вы всегда будете иметь постоянный коэффициент сопоставления log (n).
Другими словами, где g (n) - время работы вашего алгоритма, мы говорим, что g (n) = O (f (n)), когда g (n) <= c * f (n), когда n > k, где c и k - некоторые константы.
Ответ 16
"Что такое простое английское объяснение Big O? С небольшим формальным как возможная и простая математика".
Такой красивый и короткий вопрос кажется, по крайней мере, заслуживающим столь же короткого ответа, как ученик может получить во время обучения.
Обозначение Big O просто говорит, сколько времени * алгоритм может работать внутри, в терминах только количества входных данных **.
(* в прекрасном, без единого чувства смысле!)
(** что важно, потому что люди всегда хотят больше, живут ли они сегодня или завтра)
Хорошо, что так замечательно в нотации Big O, если это то, что он делает?
-
Практически говоря, анализ Big O настолько полезен и важен, что Big O ставит фокус прямо на собственную сложность алгоритма и полностью игнорирует все, что является просто константой пропорциональности, например движком JavaScript, скоростью процессора, ваше интернет-соединение и все те вещи, которые быстро становятся столь же смехотворными устаревшими, как Model T. Big O фокусируется на производительности только так, как это важно для людей, живущих в настоящем или будущем.
-
Обозначение Big O также освещает прожектор непосредственно на самом важном принципе компьютерного программирования/инжиниринга, что побуждает всех хороших программистов продолжать думать и мечтать: единственный способ добиться результатов за пределами медленного маршевого движения технология заключается в разработке лучшего алгоритма.
Ответ 17
Пример алгоритма (Java):
// given a list of integers L, and an integer K
public boolean simple_search(List<Integer> L, Integer K)
{
// for each integer i in list L
for (Integer i : L)
{
// if i is equal to K
if (i == K)
{
return true;
}
}
return false;
}
Описание алгоритма:
-
Этот алгоритм выполняет поиск списка, по элементам, ищет ключ,
-
Итерация по каждому элементу в списке, если он затем возвращает True,
-
Если цикл завершился без поиска ключа, верните False.
Обозначение Big-O представляет собой верхнюю границу сложности (время, пространство,..)
Чтобы найти сложность Big-O по времени:
-
Рассчитайте, сколько времени (относительно размера ввода) наихудший случай:
-
Худший случай: ключ не существует в списке.
-
Время (худший случай) = 4n + 1
-
Время: O (4n + 1) = O (n) | в Big-O, пренебрегают константами
-
O (n) ~ Линейный
Там также Big-Omega, которые представляют собой сложность Best-Case:
Ответ 18
Big O
f (x) = O (g (x)), когда x переходит в (например, a = + ∞), означает, что существует такая функция k, что:
-
f (x) = k (x) g (x)
-
k ограничена в некоторой окрестности точки a (если a = + ∞, это означает, что найдутся числа N и M такие, что для любого x > N, | k (x) | < M).
Другими словами, на простом английском языке: f (x) = O (g (x)), x → a, означает, что в окрестности a f разбивается на произведение g и некоторую ограниченную функцию.
Малый o
Кстати, здесь для сравнения определение малых o.
f (x) = o (g (x)), когда x переходит в a, означает, что существует такая функция k, что:
Примеры
-
sin x = O (x), когда x → 0.
-
sin x = O (1), когда x → + ∞,
-
x 2 + x = O (x), когда x → 0,
-
x 2 + x = O (x 2) при x → + ∞,
-
ln (x) = o (x) = O (x), когда x → + ∞.
Внимание! Обозначение с равным знаком "=" использует "поддельное равенство": верно, что o (g (x)) = O (g (x)), но false что O (g (x)) = o (g (x)). Точно так же нормально писать "ln (x) = o (x) при x → + ∞", но формула "o (x) = ln (x)" не имеет смысла.
Другие примеры
-
O (1) = O (n) = O (n 2) при n → + ∞ (но не наоборот, равенство является "поддельным" ),
-
O (n) + O (n 2) = O (n 2) при n → + ∞
-
O (O (n 2)) = O (n 2), когда n → + ∞
-
O (n 2) O (n 3) = O (n 5) при n → + ∞
Вот статья Википедии: https://en.wikipedia.org/wiki/Big_O_notation
Ответ 19
Обозначение Big O - это способ описания того, как быстро алгоритм будет работать с учетом произвольного количества входных параметров, которые мы будем называть "n". Это полезно в информатике, потому что разные машины работают на разных скоростях и просто говорят, что алгоритм занимает 5 секунд, не говорит вам многого, потому что, хотя вы можете запускать систему с окто-ядерным процессором 4,5 ГГц, я могу работать 15-летняя, 800 МГц система, которая может занять больше времени, независимо от алгоритма. Поэтому вместо указания того, насколько быстро алгоритм работает с точки зрения времени, мы говорим, как быстро он работает в терминах количества входных параметров или "n". Таким образом, описывая алгоритмы, мы можем сравнивать скорости алгоритмов, не принимая во внимание скорость самого компьютера.
Ответ 20
Не уверен, что я буду вносить свой вклад в эту тему, но все же думал, что буду делиться: однажды я нашел этот пост в блоге, чтобы иметь довольно полезные (хотя и очень простые) объяснения и примеры на Big O:
На примерах это помогло получить простые основы в моем черепахообразном черепе, поэтому я думаю, что это приятный спуск на 10 минут, чтобы вы направились в правильном направлении.
Ответ 21
Вы хотите знать все, что нужно знать о большом O? Я тоже.
Чтобы поговорить о большом O, я буду использовать слова, которые имеют только один удар в них. Один звук за слово. Маленькие слова быстры. Вы знаете эти слова, и я тоже. Мы будем использовать слова одним звуком. Они маленькие. Я уверен, что вы будете знать все слова, которые мы будем использовать!
Теперь давайте поговорим о работе. Большую часть времени мне не нравится работа. Вам нравится работать? Возможно, это так, но я уверен, что нет.
Мне не нравится идти на работу. Я не люблю тратить время на работу. Если бы у меня был свой путь, я бы хотел просто поиграть и повеселиться. Вы чувствуете то же, что и я?
Теперь время от времени мне приходится идти на работу. Это печально, но верно. Итак, когда я нахожусь на работе, у меня есть правило: я стараюсь делать меньше работы. Как можно ближе к работе. Затем я играю!
Итак, вот большая новость: большой O может помочь мне не делать работу! Я могу играть больше времени, если знаю большой О. Меньше работы, больше играю! Это то, что помогает мне большое О.
Теперь у меня есть работа. У меня есть этот список: один, два, три, четыре, пять, шесть. Я должен добавить все в этот список.
Ничего себе, я ненавижу работу. Но, хорошо, я должен это сделать. Итак, я иду.
Один плюс два - три... плюс три - шесть... и четыре... Я не знаю. Я потерял. Мне слишком тяжело это делать в моей голове. Я не очень забочусь о такой работе.
Так что давайте не будем делать работу. Пусть мы с тобой подумаем, как это тяжело. Сколько работы я должен сделать, чтобы добавить шесть номеров?
Хорошо, посмотрим. Я должен добавить один и два, а затем добавить это к трем, а затем добавить это к четырем... В общем, я считаю шесть добавлений. Я должен сделать шесть добавлений, чтобы решить эту проблему.
Здесь идет большой O, чтобы рассказать нам, насколько тяжело эта математика.
Big O говорит: мы должны сделать шесть добавлений, чтобы решить эту проблему. Один добавить, для каждой вещи от одного до шести. Шесть небольших бит работы... каждый бит работы - это одно добавление.
Ну, я не буду делать работу, чтобы добавить их сейчас. Но я знаю, как это было бы тяжело. Это будет шесть добавлений.
О нет, теперь у меня больше работы. Sheesh. Кто делает такие вещи?!
Теперь они просят меня добавить от одного до десяти! Зачем мне это делать? Я не хотел добавлять от одного до шести. Чтобы добавить от одного до десяти... ну... это будет еще сложнее!
Насколько тяжелее это было бы? Сколько еще работы я должен был сделать? Нужно ли больше или меньше шагов?
Ну, я думаю, мне пришлось бы сделать десять добавлений... по одному для каждой вещи от одного до десяти. Десять - более шести. Мне пришлось бы работать гораздо больше, чтобы добавить от одного до десяти, от одного до шести!
Я не хочу добавлять прямо сейчас. Я просто хочу подумать о том, как сложно это добавить. И, надеюсь, играть, как только смогу.
Чтобы добавить от одного до шести, это некоторая работа. Но видите ли вы, чтобы добавить от одного до десяти, это больше работает?
Big O - ваш друг и мой. Big O помогает нам думать о том, как много работы мы должны делать, поэтому мы можем планировать. И, если мы дружим с большим O, он может помочь нам выбрать работу, которая не так сложна!
Теперь мы должны сделать новую работу. О нет. Мне вообще не нравится эта работа.
Новая работа: добавить все вещи от одного до n.
Подождите! Что такое n? Я пропустил это? Как я могу добавить от одного до n, если вы не скажете мне, что такое n?
Ну, я не знаю, что такое n. Мне не сказали. Вы были? Нет? Ну что ж. Поэтому мы не можем выполнять эту работу. Уф.
Но, хотя мы не будем делать эту работу сейчас, мы можем догадаться, насколько это было бы тяжело, если бы мы знали n. Мы должны были бы добавить n вещей, правильно? Конечно!
Теперь вот большой О, и он расскажет нам, как тяжело эта работа. Он говорит: добавить все вещи от одного к N, один за другим, это O (n). Чтобы добавить все эти вещи, я знаю, что должен добавить n раз.] [1] Это большой O! Он рассказывает нам, как трудно выполнять какую-то работу.
Для меня, я думаю о большом О, как о большом, медленном, босс-мужчине. Он думает о работе, но он этого не делает. Он мог бы сказать: "Эта работа идет быстро". Или он мог бы сказать: "Эта работа настолько медленная и трудная!" Но он не выполняет эту работу. Он просто смотрит на работу, а затем рассказывает нам, сколько времени это займет.
Мне очень нравятся большие О. Почему? Я не люблю работать! Никто не любит работать. Вот почему мы все любим большой O! Он рассказывает нам, как быстро мы можем работать. Он помогает нам думать о том, как тяжелая работа.
Ух, больше работы. Теперь давайте не будем делать работу. Но давайте сделаем план сделать это шаг за шагом.
Они дали нам колоду из десяти карт. Все они перепутаны: семь, четыре, два, шесть... не прямые. И теперь... наша задача - сортировать их.
Ergh. Это звучит как большая работа!
Как мы можем сортировать эту колоду? У меня есть план.
Я посмотрю на каждую пару карт, пару по паре, через колоду, от первого до последнего. Если первая карта в одной паре большая, а следующая карта в этой паре мала, я меняю ее. Иначе я иду к следующей паре и т.д. И т.д.... и вскоре колода будет выполнена.
Когда колода закончена, я спрашиваю: я сделал обмен карточками в этом проходе? Если это так, я должен сделать это еще раз, сверху.
В какой-то момент, в какое-то время, не будет никаких свопов, и наша колода будет сделана. Так много работы!
Хорошо, сколько будет работы, чтобы сортировать карты с этими правилами?
У меня есть десять карт. И большую часть времени - то есть, если мне не повезло - я должен пройти через всю колоду до десяти раз, каждый раз, используя колоду, до десяти карточных свопов.
Big O, помогите мне!
Входит Big O и говорит: для колоды n карт, сортировать их таким образом будет сделано в O (N квадрат) времени.
Почему он говорит в квадрате?
Ну, вы знаете, что n квадратов n раз n. Теперь, я понимаю: n проверенных карт, вплоть до того, что может быть n раз через колоду. Это две петли, каждая из которых имеет n шагов. Это означает, что нужно много работы. Очень много работы, конечно!
Теперь, когда большой O говорит, что это займет O (n квадрат), он не означает, что n квадратов добавляет, на носу. В некоторых случаях это может быть немного меньше. Но в худшем случае это будет около n квадратов шагов для сортировки колоды.
Теперь вот где большой O - наш друг.
Big O указывает на это: по мере того, как n становится большим, когда мы сортируем карты, работа становится МНОГО БОЛЬШЕ ЖЕСТКО, чем старая операция "просто добавьте эти вещи". Как мы это знаем?
Хорошо, если n становится действительно большим, нам все равно, что мы можем добавить к n или n квадрату.
При больших n квадрат n больше n.
Big O говорит нам, что сортировать вещи сложнее, чем добавлять вещи. O (n квадрат) больше, чем O (n) при больших n. Это означает: если n становится действительно большим, для сортировки смешанной колоды n вещей ДОЛЖНО занимать больше времени, чем просто добавлять n смешанных вещей.
Big O не решает эту работу для нас. Big O рассказывает нам, как тяжело работать.
У меня колода карт. Я их сортировал. Вы помогли. Спасибо.
Есть ли более быстрый способ сортировки карт? Может ли большой О помочь нам?
Да, есть более быстрый путь! Это займет некоторое время, чтобы учиться, но это работает... и работает очень быстро. Вы можете попробовать это, но не спешите с каждым шагом и не теряйте свое место.
В этом новом способе сортировки колоды мы не проверяем пары карт так, как мы это делали некоторое время назад. Вот ваши новые правила для сортировки этой колоды:
Один: я выбираю одну карту в той части колоды, на которой мы сейчас работаем. Вы можете выбрать один для меня, если хотите. (В первый раз, когда мы это делаем, "часть колоды, в которой мы работаем сейчас", - это вся колода, конечно.)
Два: я играю колоду на той карте, которую вы выбрали. Что это за игра? как я могу играть? Ну, я иду от стартовой карты вниз, один за другим, и я искал карту, которая выше, чем игровая карта.
Три: я иду с торцевой карты вверх, и я ищу карту, которая ниже, чем у игровой карты.
Как только я найду эти две карты, я поменяю их и продолжаю искать больше карт для обмена. То есть, я вернусь к шагу 2 и перейду на карту, которую вы выбрали еще.
В какой-то момент этот цикл (от двух до трех) закончится. Это заканчивается, когда обе половины этого поиска встречаются на игровой карте. Затем мы просто разделили колоду с картой, которую вы выбрали на первом шаге. Теперь все карты рядом с стартом более низкие, чем карта игры; и карты ближе к концу более высокие, чем игра-карта. Прохладный трюк!
Четыре (и это забавная часть): теперь у меня две маленькие колоды, еще одна низкая, чем у игровой карты, и еще одна высокая. Теперь я иду на первый шаг, на каждую маленькую колоду! То есть, я начинаю с первого шага на первой маленькой колоде, и когда эта работа завершена, я начинаю с шага Один на следующей небольшой колоде.
Я разбиваю колоду по частям и сортирую каждую часть, более маленькую и более маленькую, и в какой-то момент у меня больше нет работы. Теперь это может показаться медленным, со всеми правилами. Но поверьте мне, это совсем не медленно. Это гораздо меньше, чем первый способ сортировки!
Что это называется? Он называется Quick Sort! Этот вид был сделан человеком под названием C. A. R. Hoare, и он назвал его Quick Sort. Теперь Quick Sort используется все время!
Quick Sort разбивает большие колоды в маленьких. То есть он разбивает большие задачи в маленьких.
Хммм. Думаю, там может быть правило. Чтобы сделать большие задачи небольшими, раскройте их.
Это довольно быстро. Как быстро? Big O говорит нам: в этом случае нужна O (n log n) работа, в среднем случае.
Является ли это более или менее быстрым, чем первый вид? Big O, пожалуйста, помогите!
Первый вид был O (n квадрат). Но Quick Sort - O (n log n). Вы знаете, что n log n меньше n квадратов, для больших n, правильно? Ну, вот как мы знаем, что Quick Sort быстро!
Если вам нужно сортировать колоду, что лучше? Ну, вы можете делать то, что хотите, но я бы выбрал Quick Sort.
Почему я выбираю Quick Sort? Конечно, я не люблю работать! Я хочу, чтобы работа была выполнена, как только я смогу это сделать.
Как узнать, что Quick Sort меньше работает? Я знаю, что O (n log n) меньше, чем O (n квадрат). O меньше, поэтому Quick Sort работает меньше!
Теперь ты знаешь моего друга, Биг О. Он помогает нам делать меньше работы. И если вы знаете большой O, вы можете делать меньше работы тоже!
Ты узнал все это со мной! Ты такой умный! Большое вам спасибо!
Теперь, когда работа выполнена, отпустите игру!
[1]: Есть способ обмануть и добавить все вещи от одного до n, все в одно время. Некоторый парень по имени Гаусс узнал об этом, когда ему было восемь. Я не настолько умный, поэтому не спрашивайте меня, как он это сделал.
Ответ 22
Предположим, мы говорим об алгоритме A, который должен что-то делать с набором данных размером n.
Тогда O( <some expression X involving n> )
означает на простом английском языке:
Если вам не повезло при выполнении A, это может потребовать целых X (n) операций для полный.
Как это случается, есть определенные функции (представьте их как реализации X (n)), которые обычно встречаются довольно часто. Они хорошо известны и их легко сравнивать (Примеры: 1
, Log N
, N
, N^2
, N!
и т.д.)
Сравнивая их, когда речь идет о A и других алгоритмах, легко ранжировать алгоритмы в соответствии с количеством операций, которые они могут (в худшем случае) выполнить.
В целом, нашей целью будет найти или структурировать алгоритм A таким образом, чтобы он имел функцию X(n)
, которая возвращает как можно меньшее число.
Ответ 23
У меня более простой способ понять временную сложность
он наиболее распространенный показатель для вычисления временной сложности - это обозначение Big O. Это устраняет все постоянные факторы, так что время работы можно оценить по отношению к N, когда N приближается к бесконечности. В общем, вы можете думать об этом так:
statement;
Постоянно. Время выполнения инструкции не изменится относительно N
for ( i = 0; i < N; i++ )
statement;
Является линейным. Время работы петли прямо пропорционально N. Когда N удваивается, время работы также выполняется.
for ( i = 0; i < N; i++ )
{
for ( j = 0; j < N; j++ )
statement;
}
Квадратично. Время работы двух петель пропорционально квадрату N. Когда N удваивается, время работы увеличивается на N * N.
while ( low <= high )
{
mid = ( low + high ) / 2;
if ( target < list[mid] )
high = mid - 1;
else if ( target > list[mid] )
low = mid + 1;
else break;
}
Логарифмически. Время работы алгоритма пропорционально количеству раз, когда N можно разделить на 2. Это связано с тем, что алгоритм разделяет рабочую область пополам с каждой итерацией.
void quicksort ( int list[], int left, int right )
{
int pivot = partition ( list, left, right );
quicksort ( list, left, pivot - 1 );
quicksort ( list, pivot + 1, right );
}
Является N * log (N). Время выполнения состоит из N циклов (итеративных или рекурсивных), которые являются логарифмическими, поэтому алгоритм представляет собой комбинацию линейных и логарифмических.
В общем, что-то с каждым элементом в одном измерении линейно, что-то с каждым элементом в двух измерениях квадратично, а разделение рабочей области пополам логарифмическое. Существуют и другие измерения Big O, такие как кубический, экспоненциальный и квадратный корень, но они не так распространены. Обозначение Big O описывается как O(), где - мера. Алгоритм быстрой сортировки будет описываться как O (N * log (N)).
Примечание. Ничто из этого не учитывало лучшие, средние и худшие меры. У каждого будет своя нотация Big O. Также обратите внимание, что это ОЧЕНЬ упрощенное объяснение. Big O является наиболее распространенным, но он также более сложным, чем я показал. Существуют и другие обозначения, такие как большая омега, маленькая о и большая тета. Вероятно, вы не столкнетесь с ними вне курса анализа алгоритмов.
Ответ 24
Скажите, что вы заказываете Гарри Поттера: заполните 8-кинематографическую коллекцию [Blu-ray] из Amazon и одновременно загрузите одну и ту же коллекцию фильмов в Интернете. Вы хотите проверить, какой метод выполняется быстрее. Доставка занимает почти один день, и загрузка завершена примерно на 30 минут раньше. Большой! Так что это плотная гонка.
Что делать, если я закажу несколько Blu-ray фильмов, таких как The Lord of the Rings, Twilight, The Dark Knight Trilogy и т.д. и загружаю все фильмы онлайн одновременно? На этот раз доставка по-прежнему занимает целый день, но онлайн-загрузка занимает 3 дня.
Для покупок в Интернете количество приобретенных товаров (входных данных) не влияет на время доставки. Выход постоянный. Мы называем это O (1).
Для онлайн-загрузки время загрузки прямо пропорционально размерам файлов видео (вход). Мы называем это O (n).
Из экспериментов мы знаем, что онлайн-магазины масштабируются лучше, чем онлайн-загрузка. Очень важно понимать нотацию O, потому что она помогает анализировать алгоритмы масштабируемости и эффективности.
Примечание. Обозначение Big O представляет собой наихудший сценарий алгоритма. Предположим, что O (1) и O (n) являются наихудшими сценариями примера выше.
Ссылка: http://carlcheo.com/compsci
Ответ 25
Если у вас есть подходящее понятие бесконечности в голове, то есть очень краткое описание:
Обозначение Big O говорит вам о стоимости решения бесконечно большой проблемы.
И более того
Постоянные факторы незначительны
Если вы перейдете на компьютер, который может запустить ваш алгоритм в два раза быстрее, большая нотация O не заметит этого. Усовершенствования с постоянным коэффициентом слишком малы, чтобы даже быть замеченными в масштабе, с которым работает большая нотация O. Обратите внимание, что это преднамеренная часть дизайна большой записи O.
Тем не менее, может быть обнаружено что-либо "большее", чем постоянный фактор.
Когда вы заинтересованы в выполнении вычислений, размер которых "большой" достаточно, чтобы считаться приблизительно бесконечным, тогда большая нотация O примерно равна стоимости решения вашей проблемы.
Если вышеизложенное не имеет смысла, то у вас нет совместимого интуитивного понятия бесконечности в вашей голове, и вы, вероятно, должны игнорировать все вышеперечисленное; единственный способ, которым я знаю, чтобы эти идеи были строгими или объяснить их, если они еще не являются интуитивно полезными, - это научить вас большой нотации O или что-то подобное. (хотя, как только вы хорошо поймете большую нотацию O в будущем, может быть целесообразно пересмотреть эти идеи)
Ответ 26
Что такое простое английское объяснение "Big O"?
Очень быстрое примечание:
O в "Big O" означает "Order" (или точно "порядок")
так что вы могли бы получить его идею буквально, что он использовал, чтобы заказать что-то, чтобы сравнить их.
Предположим, вы получили нотацию O(N^2)
, и вы не только поняли, что метод принимает N * N шагов для выполнения задачи, также вы видите, что это не хорошо, как O(NlogN)
из своего рейтинга.
Пожалуйста, обратите внимание на заказ в конце строки, только для вашего лучшего понимания. Здесь есть более 7 обозначений, если все возможности рассмотрены.
В CS набор шагов для выполнения задачи называется алгоритмом.
В терминологии нотация Big O используется для описания производительности или сложности алгоритма.
Кроме того, Big O устанавливает худший вариант или измеряет шаги Upper-Bound.
Вы можете обратиться к Big- Ω (Big- Omega) для лучшего случая.
Big- Ω (Big- Omega) нотация (статья) | Ханская академия
-
Резюме
"Big O" описывает производительность алгоритма и оценивает его.
или обращаться к нему формально, "Big O" классифицирует алгоритмы и стандартизирует процесс сравнения.
Ответ 27
Простейший способ взглянуть на него (на простом английском языке)
Мы пытаемся увидеть, как количество входных параметров влияет на время работы алгоритма. Если время работы вашего приложения пропорционально количеству входных параметров, то говорят, что оно находится в Big O of n.
Вышеприведенное утверждение является хорошим началом, но не полностью истинным.
Более точное объяснение (математическое)
Предположим, что
n = количество входных параметров
T (n) = Фактическая функция, которая выражает время работы алгоритма как функцию n
c = константа
f (n) = приближенная функция, выражающая время работы алгоритма как функцию n
Тогда, что касается Big O, аппроксимация f (n) считается достаточно хорошей, пока выполняется условие ниже.
lim T(n) ≤ c×f(n)
n→∞
Уравнение читается как
Поскольку n стремится к бесконечности, T n, меньше или равно c раз f из n.
В большой записи O это написано как
T(n)∈O(n)
Это читается как T из n в большом O из n.
Назад на английский
Основываясь на приведенном выше математическом определении, если вы говорите, что ваш алгоритм является большим O из n, это означает, что он является функцией n (количество входных параметров) или быстрее. Если ваш алгоритм Big O of n, то он также автоматически является большим O из n квадрата.
Big O of n означает, что мой алгоритм работает как минимум так быстро. Вы не можете взглянуть на ноту Big O на свой алгоритм и сказать, что он медленный. Вы можете сказать только быстро.
Отметьте этот для видеоурока на Big O от UC Berkley. На самом деле это простая концепция. Если вы услышите, что профессор Шелчук (он же учитель уровня Бога) объясняет это, вы скажете: "О, это все!".
Ответ 28
Я нашел по-настоящему отличное объяснение большой нотации O, особенно для человека, который не слишком увлекается математикой.
https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
Обозначение Big O используется в компьютерных науках для описания производительности или сложность алгоритма. Большой О конкретно описывает наихудший сценарий, и может быть использован для описания времени выполнения или пространство, используемое (например, в памяти или на диске) Алгоритм.
Любой, кто читает Programming Pearls или любую другую информатику книги и не имеет оснований по математике ударит в стену когда они достигли глав, в которых упоминается O (N log N) или другие, казалось бы, сумасшедший синтаксис. Надеюсь, эта статья поможет вам получить понимание основ большого О и логарифмов.
Как программист первый и второй математик (или, может быть, третий или в-четвертых) я нашел лучший способ полностью понять Биг О: привести несколько примеров в коде. Итак, ниже приведены некоторые общие порядки рост вместе с описаниями и примерами, где это возможно.
O (1)
O (1) описывает алгоритм, который всегда будет выполняться в одно и то же время (или пробел) независимо от размера набора входных данных.
bool IsFirstElementNull(IList<string> elements) {
return elements[0] == null;
}
O (N)
O (N) описывает алгоритм, производительность которого будет расти линейно и прямо пропорционально размеру входного набора данных. Пример ниже также демонстрирует, как Big O способствует производительности в худшем случае сценарий; соответствующая строка может быть найдена во время любой итерации для цикла и функция вернется рано, но запись Big O будет всегда принимайте верхний предел, где алгоритм будет выполнять максимальное количество итераций.
bool ContainsValue(IList<string> elements, string value) {
foreach (var element in elements)
{
if (element == value) return true;
}
return false;
}
O (N 2)
O (N 2) представляет алгоритм, производительность которого напрямую пропорционально квадрату размера входного набора данных. Это общий с алгоритмами, которые включают вложенные итерации по данным установлен. Более глубокие вложенные итерации приведут к O (N 3), O (N 4) и т.д.
bool ContainsDuplicates(IList<string> elements) {
for (var outer = 0; outer < elements.Count; outer++)
{
for (var inner = 0; inner < elements.Count; inner++)
{
// Don't compare with self
if (outer == inner) continue;
if (elements[outer] == elements[inner]) return true;
}
}
return false;
}
вывода (2 N)
O (2 N) обозначает алгоритм, рост которого удваивается с каждым дополнением к набор входных данных. Кривая роста функции O (2 N) экспоненциальный - начиная с очень мелкого, затем поднимаясь метеорически. Примером функции O (2 N) является рекурсивное вычисление Фибоначчи номера:
int Fibonacci(int number) {
if (number <= 1) return number;
return Fibonacci(number - 2) + Fibonacci(number - 1);
}
Логарифмы
Логарифмы немного сложнее объяснить, поэтому я буду использовать общий Пример:
Бинарный поиск - это метод, используемый для поиска в отсортированных наборах данных. Оно работает выбрав средний элемент набора данных, по существу медиана, и сравнивает его с целевым значением. Если значения соответствуют ему вернет успех. Если целевое значение выше, чем значение элемент зонда это займет верхнюю половину набора данных и выполнить ту же операцию против него. Аналогично, если целевое значение ниже, чем значение элемента зонда, он будет выполнять операция против нижней половины. Это будет продолжать вдвое данных установить для каждой итерации, пока значение не будет найдено или пока оно не может больше не разбивать набор данных.
Этот тип алгоритма описывается как O (log N). Итеративное деление пополам наборов данных, описанных в примере двоичного поиска, приводит к росту кривая, которая достигает пика в начале и медленно выравнивается как размер наборов данных увеличивается, например, набор входных данных, содержащий 10 элементов занимает одну секунду, набор данных, содержащий 100 элементов занимает две секунды, и набор данных, содержащий 1000 элементов, займет три секунд. Удвоение размера набора входных данных мало влияет на его рост как после одной итерации алгоритма набора данных будет вдвое меньше и, следовательно, наравне с входными данными, установленными на половину размер. Это делает алгоритмы, такие как бинарный поиск, чрезвычайно эффективными при работе с большими наборами данных.
Ответ 29
Это очень упрощенное объяснение, но я надеюсь, что оно охватывает самые важные детали.
Скажем, ваш алгоритм, связанный с проблемой, зависит от некоторых "факторов", например, пусть он делает N и X.
В зависимости от N и X для вашего алгоритма потребуются некоторые операции, например, в случае WORST это операции 3(N^2) + log(X)
.
Так как Big-O не слишком заботится о постоянном множителе (aka 3), Big-O вашего алгоритма O(N^2 + log(X))
. В основном это означает "количество операций, которые ваш алгоритм требует для наихудших шкал с этим".
Ответ 30
Введение
алгоритм: процедура/формула для решения проблемы
Как анализировать алгоритмы и как мы можем сравнивать алгоритмы друг с другом?
Пример : вам и другу предлагается создать функцию для суммирования чисел от 0 до N. Вы получаете f (x), а ваш друг придумывает g (x). Обе функции имеют одинаковый результат, но другой алгоритм. Чтобы объективно сравнить эффективность алгоритмов, мы используем нотацию Big-O.
Обозначение Big-O: описывает, как быстро время выполнения будет расти относительно ввода, так как вход становится сколь угодно большим.
3 ключевых вылета:
- Сравните, как быстро среда выполнения растет НЕ сравнивает точное время выполнения (зависит от аппаратного обеспечения)
- Только связанные с временем выполнения растут относительно ввода (n)
- По мере того, как n становится сколь угодно большим, сосредоточьтесь на терминах, которые будут расти быстрее, чем когда n станет большим (подумайте бесконечность) AKA асимптотический анализ
Сложность пространства:, кроме временной сложности, мы также заботимся о сложности пространства (сколько памяти/пространства используется алгоритмом). Вместо проверки времени операций мы проверяем размер выделения памяти.