Что такое простое английское объяснение "Big O"?

Я предпочел бы как можно меньше формального определения и простую математику.

4610
задан Arec Barrwin 28 янв. '09 в 14:10
источник поделиться
38 ответов
  • 1
  • 2

Быстрое замечание, это почти наверняка путает нотацию Big O (которая является верхней границей) с обозначением Theta (который является двухсторонним связанно). По моему опыту, это типично для дискуссий в неакадемических условиях. Извинения за любую путаницу.


С этим графиком можно визуализировать сложность Big O:

Big O Analysis

Простейшим определением, которое я могу дать для обозначения Big-O, является следующее:

Обозначение Big-O является относительным представлением сложности алгоритма.

В этом предложении есть важные и преднамеренно выбранные слова:

  • relative: вы можете сравнивать яблоки с яблоками. Вы не можете сравнить алгоритм с арифметическим умножением на алгоритм, сортирующий список целых чисел. Но сравнение двух алгоритмов для выполнения арифметических операций (одно умножение, одно дополнение) скажет вам что-то значимое;
  • : Big-O (в простейшей форме) уменьшает сравнение алгоритмов с одной переменной. Эта переменная выбирается на основе наблюдений или предположений. Например, алгоритмы сортировки обычно сравниваются на основе операций сравнения (сравнение двух узлов для определения их относительного упорядочения). Это предполагает, что сравнение стоит дорого. Но что, если сравнение дешево, но обмен стоит дорого? Он меняет сравнение; и
  • сложность:, если мне понадобится одна секунда, чтобы отсортировать 10 000 элементов, как долго мне понадобится сортировать миллион? Сложность в этом случае является относительной мерой для чего-то еще.

Вернитесь и перечитайте выше, когда вы прочтете остальное.

Лучший пример Big-O, о котором я могу думать, - это делать арифметику. Возьмите два номера (123456 и 789012). Основными арифметическими операциями, которые мы изучили в школе, были:

  • добавление;
  • вычитание;
  • умножение; и
  • деление.

Каждая из них - это операция или проблема. Метод их решения называется алгоритмом.

Дополнение является самым простым. Вы выводите цифры вверх (справа) и добавляете цифры в столбце, в котором записывается последний номер этого сложения в результате. Часть десятков этого числа переносится в следующий столбец.

Предположим, что добавление этих чисел является самой дорогостоящей операцией в этом алгоритме. Разумеется, чтобы добавить эти два числа вместе, мы должны добавить 6 цифр (и, возможно, нести 7-е). Если мы добавим два 100-значных числа вместе, мы должны сделать 100 дополнений. Если мы добавим два 10 000 цифр, нам нужно сделать 10 000 дополнений.

См. шаблон? сложность (являющаяся числом операций) прямо пропорциональна числу цифр n в большем количестве. Мы называем эту O (n) или линейную сложность.

Вычитание аналогично (за исключением того, что вам может потребоваться заимствовать вместо переноса).

Умножение отличается. Вы выводите цифры вверх, берете первую цифру в нижнем номере и умножаете ее поочередно на каждую цифру в верхнем номере и так далее через каждую цифру. Итак, чтобы умножить наши два 6-значных чисел, мы должны сделать 36 умножений. Нам может понадобиться сделать до 10 или 11 столбцов, чтобы получить конечный результат.

Если у нас есть два 100-значных числа, нам нужно сделать 10 000 умножений и 200 добавлений. Для двух миллионов номеров цифр нам нужно сделать один триллион (10 12) умножений и добавить два миллиона.

Поскольку алгоритм масштабируется с n-квадратом, это O (n 2) или квадратичная сложность. Это подходящее время для введения еще одной важной концепции:

Мы заботимся только о самой значительной части сложности.

Проницательный, возможно, понял, что мы можем выразить число операций как: n 2 + 2n. Но, как вы видели из нашего примера с двумя цифрами по миллиону цифр, второй член (2n) становится несущественным (что составляет 0,0002% от общего количества операций на этом этапе).

Можно заметить, что мы предположили худший сценарий здесь. При умножении шестизначных чисел, если один из них имеет 4 цифры, а другой - 6 цифр, тогда у нас всего 24 умножения. Тем не менее мы вычисляем худший сценарий для этого "n", т.е. когда оба являются 6-значными числами. Следовательно, запись Big-O относится к наихудшему сценарию алгоритма

Телефонная книга

Следующим лучшим примером, о котором я могу думать, является телефонная книга, обычно называемая "Белыми страницами" или подобная, но она будет отличаться в разных странах. Но я говорю о том, что перечисляет людей по фамилии, а затем инициалы или имя, возможно, адрес, а затем номера телефонов.

Теперь, если вы поручили компьютеру найти номер телефона для "Джона Смита" в телефонной книге, содержащей 1 000 000 имен, что бы вы сделали? Игнорируя тот факт, что вы могли догадаться, как далеко в начале S (допустим, вы не можете), что бы вы сделали?

Типичная реализация может заключаться в том, чтобы открыть до середины, взять 500 000 th и сравнить ее с "Смит". Если это "Смит, Джон", нам просто повезло. Скорее всего, "Джон Смит" будет до или после этого имени. Если это после того, как мы разделим последнюю половину телефонной книги пополам и повторим. Если до этого мы разделим первую половину телефонной книги пополам и повторим. И так далее.

Это называется бинарным поиском и используется каждый день при программировании, понимаете ли вы это или нет.

Итак, если вы хотите найти имя в телефонной книге из миллиона имен, вы можете найти любое имя, сделав это не более 20 раз. При сравнении алгоритмов поиска мы решаем, что это сравнение является нашим "n".

  • Для телефонной книги из 3 имен требуется 2 сравнения (не более).
  • Для 7 требуется не более 3.
  • Для 15 требуется 4.
  • ...
  • Для 1,000,000 требуется 20.

Это потрясающе хорошо, не так ли?

В терминах Big-O это O (log n) или логарифмическая сложность. Теперь рассматриваемый логарифм может быть ln (base 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 городов. Каждый из этих городов связан с одним или несколькими другими городами по дороге на определенном расстоянии. Задача Traveling Salesman - найти самый короткий тур, который посещает каждый город.

Звучит просто? Подумайте еще раз.

Если у вас есть 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.

Это функция математической операции, называемая factorial. В основном:

  • 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

Таким образом, проблема Big-O от продавца: O (n!) или факторная или комбинаторная сложность.

К тому времени, когда вы доберетесь до 200 городов, во вселенной не останется времени, чтобы решить проблему с использованием традиционных компьютеров.

Что-то о чем подумать.

Полиномиальное время

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

O (n), O (n 2) и т.д. - все полиномиальное время. Некоторые проблемы не могут быть решены в полиномиальное время. Из-за этого некоторые вещи используются в мире. Криптография с открытым ключом является ярким примером. Вычислительно сложно найти два простых фактора очень большого числа. Если бы это не так, мы не могли использовать системы открытых ключей, которые мы используем.

Во всяком случае, это для моего (надеюсь, простого английского) объяснения Big O (пересмотрено).

6181
ответ дан cletus 28 янв. '09 в 14:18
источник поделиться

Показывает, как алгоритм масштабируется.

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 или как бы то ни было, но это будет доминирующим фактором в масштабировании.

676
ответ дан Ray Hidayat 28 янв. '09 в 14:28
источник поделиться

Обозначение обозначений Big-O (также называемое "асимптотическим ростом") - это то, что функции "выглядят", когда вы игнорируете постоянные факторы и вещи рядом с источником. Мы используем его, чтобы поговорить о том, как шкала вещей.


основы

для "достаточно больших вводов"...

  • f(x) ∈ O(upperbound) означает, что f "растет не быстрее, чем upperbound
  • f(x) ∈ Ɵ(justlikethis) означает, что f "растет точно так же, как" justlikethis
  • f(x) ∈ Ω(lowerbound) означает, что f "растет не медленнее, чем" lowerbound

нотация большой О не заботится о постоянных факторах: функция 9x² называется "вырастает точно так же, как" 10x². Также асимптотическая нотация Big-O не заботится о неасимптотических вещах ("материал рядом с источником" или "что происходит, когда размер проблемы мал"): 10x² что функция 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 - наихудший сценарий использования процессора/памяти ".


Приложения

Как чисто математическая конструкция, нотация большого О не ограничивается разговором о времени обработки и памяти. Вы можете использовать его для обсуждения асимптотики всего, где масштабирование имеет смысл, например:

  • количество возможных рукопожатий среди N человек на вечеринке (Ɵ(N²), в частности N(N-1)/2, но важно то, что оно "масштабируется как" )
  • вероятностное ожидаемое число людей, которые видели некоторый вирусный маркетинг как функцию времени
  • как масштабирование латентности веб-сайта с количеством процессоров в процессоре или графическом процессоре или компьютерном кластере
  • как шкала тепловыделения на процессоре умирает в зависимости от количества транзисторов, напряжения и т.д.
  • сколько времени должен выполняться алгоритм, как функция размера ввода
  • сколько пространства должен выполняться алгоритм в зависимости от размера ввода

пример

В приведенном выше примере рукопожатия все в комнате встряхивают всех остальных. В этом примере #handshakes ∈ Ɵ(N²). Зачем?

Резервное копирование: количество рукопожатий - это точно n-choose-2 или N*(N-1)/2 (каждый из N людей встряхивает руки N-1 других людей, но эти двукратные рукопожатия так делятся на 2):

everyone handshakes everyone else. Image credit and license per wikipedia/wikimedia commons "complete graph" article. adjacency matrix

Однако для очень большого числа людей линейный член 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² рукопожатий, даже не появится в десятичном значении для сколь угодно большого времени.

например, для x = 1million, отношение # рукопожатия /x²: 0.499999...


Строительная интуиция

Это позволяет нам делать заявления вроде...

"Для достаточно больших входов = N, независимо от того, что является постоянным фактором, если я удваиваю размер ввода...

  • ... Я удваиваю время, которое принимает алгоритм O (N) ("линейное время") ".

    N → (2N) = 2 (N)

  • ... Я удваиваю квадрат (в четыре раза) время, в течение которого выполняется алгоритм O (N²) ("квадратичное время") "(например, проблема 100x как большая занимает 100 ² = 10000x, как это долго... возможно, неустойчиво)

    N2 → (2N) ² = 4 (N2)

  • ... Я дважды кубически (octuple) время, в течение которого выполняется алгоритм 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)) = (фиксированная сумма) + (c log (N))

  • ... Я не изменяю время, которое принимает алгоритм O (1) ("постоянное время") "(самый дешевый!)

    c * 1c * 1

  • ... я "(в основном) double" время, которое принимает алгоритм O (N log (N)). "(Довольно распространенный)

    это меньше, чем O (N 1.000001), который вы, возможно, захотите назвать в основном линейным

  • ... Я смехотворно увеличиваю время, в течение которого выполняется алгоритм O (2 N) ("экспоненциальное время"). (Вы удвоили (или утроили и т.д.) Время, просто увеличив проблему на единицу)

    2 N → 2 2N= (4 N)............ введите другой путь...... 2 N → 2 N + 1= 2 N 2 1= 2 2 N

[для математически склонных, вы можете навести курсор мыши на спойлеры для незначительных побочных эффектов]

(с кредитом qaru.site/questions/89/...)

(технически постоянный фактор мог бы иметь значение в некоторых более эзотерических примерах, но я сформулировал вещи выше (например, в log (N)), так что это не так)

Это ориентиры роста хлеба и масла, которые программисты и прикладные компьютерные ученые используют в качестве ориентиров. Они все это видят. (Итак, хотя вы можете технически подумать: "Удвоение ввода делает алгоритм O (√N) в 1,414 раза медленнее", лучше подумать об этом как "это хуже логарифмического, но лучше линейного").


Постоянные факторы

Обычно нам все равно, каковы конкретные постоянные факторы, потому что они не влияют на способ роста функции. Например, два алгоритма могут оба выполнить время O(N), но в два раза медленнее, чем другое. Обычно нам неинтересно, если фактор очень велик, поскольку оптимизация - сложный бизнес (когда оптимизация преждевременна?); также простой акт выбора алгоритма с лучшими большими-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) для выполнения этого поиска. Nomatter, сколько времени займет ваш фактический алгоритм, он будет по крайней мере 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 x ...
    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 x ...
    some O(1) operation         // 1
    some O(B) operation         // B
    for every y in listOfSizeC: // C x ...
        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 x
        for j in 1..n:      // N x
            total += i*k      // 1
    return total
}
// O(n^2)

function nCubedFunction(a) {
    for i in 1..n:                // A x
        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___________________|

Здесь самый маленький узнаваемый набросок, который вы можете сделать, имеет значение; треугольник представляет собой двумерную форму (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

Или, может быть, вы выполняете log (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)

Если мы делаем что-то очень сложное, например, с помощью рекурсивной функции или алгоритма разделения и покоя, вы можете использовать Мастер-теорему (обычно работает) или в нелепых случаях теорема Акра-Бацци (почти всегда работает), вы смотрите на время работы вашего алгоритма в Википедии.

Но программисты так не думают, потому что в конечном итоге алгоритмическая интуиция просто становится второй натурой. Вы начнете кодировать что-то неэффективное и сразу подумаете: "Я делаю что-то очень неэффективное? ". Если ответ "да", и вы предвидите, что это имеет значение, тогда вы можете сделать шаг назад и подумать о различных трюках, чтобы заставить все работать быстрее (ответ почти всегда "использует хэш-таблицу", редко "использует дерево", и очень редко что-то более сложное).


Амортизированная и средняя сложность

Существует также концепция "амортизированного" и/или "среднего случая" (обратите внимание, что это разные).

Средний случай: это не более чем использование записи большого О для ожидаемого значения функции, а не самой функции. В обычном случае, когда вы считаете все входы одинаково вероятными, средний случай - это всего лишь среднее время работы. Например, с быстрой сортировкой, хотя в худшем случае 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

Большую часть времени люди не понимают, что на работе работает более одной переменной. Например, в алгоритме строкового поиска ваш алгоритм может занять время O([length of text] + [length of query]), то есть он является линейным по двум переменным, таким как O(N+M). Другими более наивными алгоритмами могут быть O([length of text]*[length of query]) или O(N*M). Игнорирование нескольких переменных является одним из наиболее распространенных явлений, которые я вижу в анализе алгоритмов, и может помешать вам при разработке алгоритма.


Вся история

Имейте в виду, что big-O - это не вся история. Вы можете резко ускорить некоторые алгоритмы, используя кеширование, делая их кэширующими, избегая узких мест, работая с ОЗУ вместо диска, используя распараллеливание или выполняя работу загодя - эти методы часто не зависят от порядка роста "big-O", хотя вы часто увидите количество ядер в многоточечной нотации параллельных алгоритмов.

Также имейте в виду, что из-за скрытых ограничений вашей программы вам может не понравиться асимптотическое поведение. Возможно, вы работаете с ограниченным количеством значений, например:

  • Если вы сортируете что-то вроде 5 элементов, вы не хотите использовать быстродействующую сортировку O(N log(N)); вы хотите использовать сортировку вставки, которая хорошо работает на небольших входах. Эти ситуации часто возникают в алгоритмах разделения и покорения, где вы делите проблему на более мелкие и малые подзадачи, такие как рекурсивная сортировка, быстрые преобразования Фурье или умножение матриц.
  • Если некоторые значения эффективно ограничены из-за какого-то скрытого факта (например, среднее человеческое имя мягко ограничено, возможно, 40 буквами, а человеческий возраст мягко ограничен примерно в 150). Вы также можете наложить ограничения на свой вклад, чтобы эффективно сделать условия постоянными.

На практике даже среди алгоритмов, которые имеют одинаковую или сходную асимптотическую производительность, их относительная заслуга может быть фактически обусловлена другими вещами, такими как: другие коэффициенты производительности (quicksort и mergesort - это O(N log(N)), но quicksort принимает преимущество кэшей CPU); неэффективные соображения, такие как простота внедрения; доступна ли библиотека и насколько авторитетна и поддерживается библиотека.

Программы также будут работать медленнее на компьютере с частотой 500 МГц и 2 ГГц. Мы не рассматриваем это как часть ограничений ресурсов, потому что мы думаем о масштабировании с точки зрения машинных ресурсов (например, за такт), а не за секунду. Тем не менее, есть похожие вещи, которые могут "тайно" влиять на производительность, например, работают ли вы под эмуляцией или же оптимизирован код компилятора или нет. Это может привести к тому, что некоторые основные операции занимают больше времени (даже относительно друг друга) или даже ускоряют или замедляют некоторые операции асимптотически (даже относительно друг друга). Эффект может быть небольшим или большим между различными реализациями и/или средой. Вы переключаете языки или машины, чтобы выучить эту небольшую дополнительную работу? Это зависит от сотен других причин (необходимость, навыки, коллеги, производительность программиста, денежная ценность вашего времени, знакомство, обходные пути, почему не сборка или графический процессор и т.д.), Что может быть более важным, чем производительность.

Вышеупомянутые проблемы, такие как язык программирования, почти никогда не рассматриваются как часть постоянного фактора (и не должны быть); но их следует знать, потому что иногда (хотя и редко) они могут влиять на вещи. Например, в cpython реализация очереди приоритетных приоритетов асимптотически неоптимальна (O(log(N)) а не O(1) для вашего выбора вставки или find-min); вы используете другую реализацию? Наверное, нет, так как реализация C, вероятно, быстрее, и, вероятно, есть другие подобные проблемы в другом месте. Есть компромиссы; иногда они имеют значение, а иногда и нет.


(edit: объяснение "простого английского" заканчивается здесь.)

Математические дополнения

Для полноты точное определение обозначения большой O состоит в следующем: f(x) ∈ O(g(x)) означает, что "f асимптотически ограничено const * g", игнорируя все ниже некоторого конечного значения x, существует такая константа, что |f(x)| ≤ const * |g(x)| |f(x)| ≤ const * |g(x)| , (Другие символы следующие: точно так же, как O означает ≤, Ω означает ≥. Существуют варианты нижнего регистра: o означает <, а ω означает>.) 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" и полностью законно использовать... но нужно понимать = не используется как равенство; это сложная нотация, которую нужно читать как идиому. Меня научили использовать более строгое ∈ O(...). означает, что "является элементом". O(N²) на самом деле является классом эквивалентности, т. O(N²) собой набор вещей, которые мы считаем одинаковыми. В этом конкретном случае O(N²) содержит такие элементы, как { 2 N², 3 N², 1/2 N², 2 N² + log(N), - N² + N^1.9 ,...} и бесконечно велика, но это еще набор. = Обозначение может быть более распространенным, и даже используется в работах всемирно известных компьютерных ученых. Кроме того, часто бывает, что в непринужденной обстановке люди будут говорить O(...) когда они означают Ɵ(...); это технически верно, поскольку множество вещей Ɵ(exactlyThis) является подмножеством O(noGreaterThanThis)... и его легче вводить. ;-)

367
ответ дан ninjagecko 08 июля '11 в 7:46
источник поделиться

РЕДАКТИРОВАТЬ: Быстрое замечание, это почти наверняка смущает нотация Big O (которая является верхней границей) с обозначением Theta (что является одновременно и верхняя и нижняя границы). По моему опыту, это типично для дискуссий в неакадемических условиях. Извинения за любую путаницу.

В одном предложении: по мере увеличения размера вашей работы, сколько времени потребуется, чтобы завершить ее?

Очевидно, что только использование "размера" в качестве входного сигнала и "время, принятое" в качестве вывода — эта же идея применяется, если вы хотите поговорить об использовании памяти и т.д.

Вот пример, где у нас есть N футболок, которые мы хотим высушить. Мы предположим, что невероятно быстро получить их в положении сушки (т.е. Взаимодействие человека незначительно). Это, конечно, не в реальной жизни...

  • Использование стиральной линии снаружи: при условии, что у вас бесконечно большой задний двор, мытье высыхает в течение O (1). Как бы то ни было, у него будет такое же солнце и свежий воздух, поэтому размер не влияет на время высыхания.

  • Использование сушилки для сушки: вы накладываете 10 рубашек на каждую нагрузку, а затем через час. (Игнорируйте фактические цифры здесь, они не имеют значения.) Таким образом, сушка 50 рубашек занимает примерно в 5 раз дольше, чем высушивание 10 рубашек.

  • Вкладывая все в шкафчик для воздуха: если мы поместим все в одну большую кучу и просто дадим общей теплоте, это займет много времени, пока средние рубашки не высохнут. Я не хотел бы догадываться о деталях, но я подозреваю, что это, по крайней мере, O (N ^ 2) — по мере увеличения нагрузки на стирку, время сушки увеличивается быстрее.

Одним из важных аспектов нотации "большой O" является то, что он не говорит, какой алгоритм будет быстрее для заданного размера. Возьмите хэш-таблицу (строковый ключ, целочисленное значение) и массив пар (строка, целое число). Быстрее найти ключ в хэш-таблице или элемент в массиве на основе строки? (т.е. для массива "найдите первый элемент, в котором строковая часть соответствует заданному ключу".) Хэш-таблицы обычно амортизируются (~ = "в среднем" ) O (1) — после того, как они настроены, для поиска записи в таблице с 100 входами потребуется примерно одно и то же время, как в таблице ввода в 1000 000. Поиск элемента в массиве (на основе контента, а не индекса) является линейным, то есть O (N) — в среднем вам придется посмотреть на половину записей.

Это делает хэш-таблицу быстрее, чем массив для поиска? Не обязательно. Если у вас очень маленькая коллекция записей, массив может быть быстрее и быстрее; вы можете проверить все строки в то время, которое требуется, чтобы просто вычислить хэш-код того, на который вы смотрите. Однако, поскольку набор данных становится все больше, хэш-таблица в конечном итоге будет бить массив.

237
ответ дан Jon Skeet 28 янв. '09 в 14:16
источник поделиться

Big O описывает верхний предел поведения роста функции, например время выполнения программы, когда входы становятся большими.

Примеры:

  • O (n): если я удваиваю размер ввода, время выполнения удваивается

  • O (n 2): Если размер ввода удваивает количество циклов выполнения

  • O (log n): Если размер ввода удваивается, время выполнения увеличивается на один

  • O (2 n): если размер ввода увеличивается на единицу, время выполнения удваивается

Размер ввода обычно представляет собой пробел в битах, необходимых для представления ввода.

120
ответ дан starblue 28 янв. '09 в 14:23
источник поделиться

Обозначение 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 игнорирует факторы, которые не вносят существенного вклада в кривую роста функции, поскольку размер ввода увеличивается в сторону бесконечности. Это означает, что константы, которые добавляются или умножаются функцией, просто игнорируются.

97
ответ дан cdiggins 05 сент. '11 в 19:31
источник поделиться

Big O - это просто способ "выразить" себя обычным способом: "Сколько времени и пространства требуется для запуска моего кода?".

Вы можете часто видеть O (n), O (n 2), O (nlogn) и т.д., все это просто способы показать; Как изменяется алгоритм?

O (n) означает, что Big O является n, и теперь вы можете подумать: "Что такое n!?" Ну "n" - количество элементов. Imaging вы хотите искать элемент в массиве. Вам нужно будет посмотреть на каждый элемент и как "Вы правильный элемент/элемент?" в худшем случае элемент находится по последнему индексу, а это значит, что потребовалось столько же времени, сколько есть элементов в списке, поэтому, чтобы быть общим, мы говорим: "О, эй, я - справедливое заданное количество ценностей!".

Итак, вы можете понять, что означает "n 2", но, чтобы быть более конкретным, играйте с мыслью, что у вас есть простой, самый простой алгоритм сортировки; BubbleSort. Этот алгоритм должен просматривать весь список для каждого элемента.

Мой список

  • 1
  • 6
  • 3

Здесь поток:

  • Сравните 1 и 6, что является самым большим? Ок 6 находится в правильном положении, двигаясь вперед!
  • Сравните 6 и 3, о, 3 меньше! Давайте переместим это, Ok, список изменился, нам нужно начать с самого начала!

Это O n 2 потому что вам нужно посмотреть на все элементы в списке, где есть "n". Для каждого элемента вы снова смотрите на все предметы, для сравнения это также "n", поэтому для каждого элемента вы смотрите "n" раз, что означает n * n = n 2

Надеюсь, это так просто, как вы этого хотите.

Но помните, что Big O - это просто способ, чтобы вы себя вписывали в манеру времени и пространства.

77
ответ дан Filip Ekberg 28 янв. '09 в 14:14
источник поделиться

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 в этом случае, полностью из-за того, как каждый алгоритм масштабируется.

52
ответ дан Wedge 28 янв. '09 в 16:12
источник поделиться

Вот простой английский бестиарий, который я обычно использую при объяснении общих разновидностей 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).

35
ответ дан Andrew Prock 28 янв. '14 в 2:09
источник поделиться

Big O - это показатель того, сколько времени/пространства использует алгоритм относительно размера его ввода.

Если алгоритм O (n), то время/пространство будет увеличиваться с той же скоростью, что и его вход.

Если алгоритм O (n 2), то время/пространство увеличивается со скоростью его квадрата ввода.

и т.д.

34
ответ дан Brownie 28 янв. '09 в 14:19
источник поделиться

Очень сложно измерить скорость работы программ, и когда мы попытаемся, ответы могут быть очень сложными и заполнены исключениями и особыми случаями. Это большая проблема, потому что все эти исключения и особые случаи отвлекают и бесполезны, когда мы хотим сравнить две разные программы друг с другом, чтобы узнать, что является "самым быстрым".

В результате всей этой бесполезной сложности люди пытаются описать скорость программ, используя наименьшие и наименее сложные (математические) выражения. Эти выражения являются очень грубыми приближениями: хотя, с некоторой удачей, они поймут "суть" того, быстро ли или медленно работает часть программного обеспечения.

Поскольку они являются приближениями, мы используем в выражении букву "O" (Big Oh) в качестве условного обозначения, чтобы сообщить читателю, что мы делаем грубое упрощение. (И чтобы убедиться, что никто не ошибочно думает, что выражение в любом случае точнее).

Если вы читаете "О" как значение "по порядку" или "приблизительно", вы не ошибетесь. (Я думаю, что выбор Big-Oh мог быть попыткой юмора).

Единственное, что эти выражения "Big-Oh" пытаются сделать, это описать, насколько программное обеспечение замедляется, поскольку мы увеличиваем объем данных, которые программное обеспечение должно обрабатывать. Если мы удваиваем объем данных, которые необходимо обработать, требуется ли в два раза больше программного обеспечения, чтобы завершить работу? Десять раз? На практике существует очень ограниченное количество выражений большого О-О, с которыми вы столкнетесь и о чем беспокоиться:

Хорошее:

  • O(1) Константа. Программа выполняет одно и то же время для запуска независимо от того, насколько велик вход.
  • O(log n) Логарифмическая: время выполнения программы увеличивается только медленно, даже при большом увеличении размера ввода.

Плохо:

  • O(n) Линейный: время выполнения программы увеличивается пропорционально размеру ввода.
  • O(n^k) Полиномиальный: - время обработки растет быстрее и быстрее - как функция полинома - по мере увеличения размера ввода.

... и уродливый:

  • O(k^n) Экспоненциальный Время выполнения программы очень быстро увеличивается даже при умеренном увеличении размера проблемы - практично обрабатывать небольшие наборы данных с помощью экспоненциальных алгоритмов.
  • O(n!) Факториал Продолжительность программы будет больше, чем вы можете позволить себе ждать чего угодно, кроме самых маленьких и самых тривиальных, кажущихся наборов данных.
30
ответ дан William Payne 29 мая '13 в 16:51
источник поделиться

Что такое простое английское объяснение Big O? С как можно меньшим формальным определением и простой математикой.

Простое английское объяснение необходимости нотации Big-O:

Когда мы программируем, мы пытаемся решить проблему. То, что мы кодируем, называется алгоритмом. Обозначение Big O позволяет стандартизовать худшую производительность наших алгоритмов. Спецификации оборудования меняются со временем, а усовершенствования аппаратного обеспечения могут сократить время, затрачиваемое на выполнение алгоритмов. Но замена аппаратного обеспечения не означает, что наш алгоритм будет лучше или улучшен с течением времени, так как наш алгоритм все тот же. Поэтому, чтобы позволить нам сравнивать разные алгоритмы, чтобы определить, лучше ли это или нет, мы используем нотацию Big O.

Обычный английский Объяснение того, что такое Big O Notation:

Не все алгоритмы работают за один и тот же промежуток времени и могут варьироваться в зависимости от количества элементов на входе, которые мы будем называть n. Исходя из этого, мы рассматриваем анализ худшего случая или верхнюю границу времени выполнения, когда n становится больше и больше. Мы должны знать, что такое n, потому что многие из нот Big O ссылаются на него.

30
ответ дан James Oravec 22 февр. '13 в 4:00
источник поделиться

Простым простым ответом может быть:

Big O представляет собой наихудшее возможное время/пространство для этого алгоритма. Алгоритм никогда не будет занимать больше места/времени выше этого предела. Big O представляет сложность времени/пространства в крайнем случае.

27
ответ дан AlienOnEarth 13 нояб. '13 в 13:23
источник поделиться

Хорошо, мои 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)

27
ответ дан Ajeet Ganga 23 авг. '11 в 7:06
источник поделиться

Обозначение 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 - некоторые константы.

24
ответ дан John C Earls 17 июля '10 в 5:29
источник поделиться

"Что такое простое английское объяснение Big O? С небольшим формальным как возможная и простая математика".

Такой красивый и короткий вопрос кажется, по крайней мере, заслуживающим столь же короткого ответа, как ученик может получить во время обучения.

Обозначение Big O просто говорит, сколько времени * алгоритм может работать внутри, в терминах только количества входных данных **.

(* в прекрасном, без единого чувства смысле!)
(** что важно, потому что люди всегда хотят больше, живут ли они сегодня или завтра)

Хорошо, что так замечательно в нотации Big O, если это то, что он делает?

  • Практически говоря, анализ Big O настолько полезен и важен, что Big O ставит фокус прямо на собственную сложность алгоритма и полностью игнорирует все, что является просто константой пропорциональности, например движком JavaScript, скоростью процессора, ваше интернет-соединение и все те вещи, которые быстро становятся столь же смехотворными устаревшими, как Model T. Big O фокусируется на производительности только так, как это важно для людей, живущих в настоящем или будущем.

  • Обозначение Big O также освещает прожектор непосредственно на самом важном принципе компьютерного программирования/инжиниринга, что побуждает всех хороших программистов продолжать думать и мечтать: единственный способ добиться результатов за пределами медленного маршевого движения технология заключается в разработке лучшего алгоритма.

22
ответ дан Joseph Myers 15 авг. '13 в 4:57
источник поделиться

Пример алгоритма (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:

  • Лучший случай: ключ является первым элементом.

  • Время (Best-Case) = 4

  • Время: Ω (4) = O (1) ~ Instant\Constant

20
ответ дан Khaled.K 23 марта '13 в 18:19
источник поделиться

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, что:

  • f (x) = k (x) g (x)

  • k (x) переходит в 0, когда x переходит в a.

Примеры

  • 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

18
ответ дан Alexey 16 марта '13 в 0:18
источник поделиться

Обозначение Big O - это способ описания того, как быстро алгоритм будет работать с учетом произвольного количества входных параметров, которые мы будем называть "n". Это полезно в информатике, потому что разные машины работают на разных скоростях и просто говорят, что алгоритм занимает 5 секунд, не говорит вам многого, потому что, хотя вы можете запускать систему с окто-ядерным процессором 4,5 ГГц, я могу работать 15-летняя, 800 МГц система, которая может занять больше времени, независимо от алгоритма. Поэтому вместо указания того, насколько быстро алгоритм работает с точки зрения времени, мы говорим, как быстро он работает в терминах количества входных параметров или "n". Таким образом, описывая алгоритмы, мы можем сравнивать скорости алгоритмов, не принимая во внимание скорость самого компьютера.

17
ответ дан Brian 25 июня '14 в 23:32
источник поделиться

Вы хотите знать все, что нужно знать о большом 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, все в одно время. Некоторый парень по имени Гаусс узнал об этом, когда ему было восемь. Я не настолько умный, поэтому не спрашивайте меня, как он это сделал.

11
ответ дан johnwbyrd 27 дек. '15 в 13:34
источник поделиться

Не уверен, что я буду вносить свой вклад в эту тему, но все же думал, что буду делиться: однажды я нашел этот пост в блоге, чтобы иметь довольно полезные (хотя и очень простые) объяснения и примеры на Big O:

На примерах это помогло получить простые основы в моем черепахообразном черепе, поэтому я думаю, что это приятный спуск на 10 минут, чтобы вы направились в правильном направлении.

11
ответ дан Priidu Neemre 29 сент. '12 в 23:54
источник поделиться

У меня более простой способ понять временную сложность он наиболее распространенный показатель для вычисления временной сложности - это обозначение 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 является наиболее распространенным, но он также более сложным, чем я показал. Существуют и другие обозначения, такие как большая омега, маленькая о и большая тета. Вероятно, вы не столкнетесь с ними вне курса анализа алгоритмов.

9
ответ дан nitin kumar 30 янв. '15 в 10:00
источник поделиться

Скажите, что вы заказываете Гарри Поттера: заполните 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

9
ответ дан raaz 06 дек. '15 в 9:01
источник поделиться

Предположим, что мы говорим об алгоритме A, который должен что-то делать с набором данных размера n.

Тогда O( <some expression X involving n> ) означает простой английский:

Если вам не повезло при выполнении A, это может привести к операциям X (n) полная.

Как бы то ни было, существуют определенные функции (считайте их реализацией X (n)), которые имеют тенденцию возникать довольно часто. Они хорошо известны и легко сравниваются (примеры: 1, Log N, N, N^2, N! и т.д.)

Сравнивая их при разговоре о A и других алгоритмах, легко ранжировать алгоритмы в соответствии с количеством операций, которые они могут (в худшем случае) завершить.

В общем, нашей целью будет найти или структурировать алгоритм A таким образом, чтобы он имел функцию X(n), которая возвращает как можно меньшее число.

9
ответ дан Kjartan 25 окт. '13 в 18:11
источник поделиться

Если у вас есть подходящее понятие бесконечности в голове, то есть очень краткое описание:

Обозначение Big O говорит вам о стоимости решения бесконечно большой проблемы.

И более того

Постоянные факторы незначительны

Если вы перейдете на компьютер, который может запустить ваш алгоритм в два раза быстрее, большая нотация O не заметит этого. Усовершенствования с постоянным коэффициентом слишком малы, чтобы даже быть замеченными в масштабе, с которым работает большая нотация O. Обратите внимание, что это преднамеренная часть дизайна большой записи O.

Тем не менее, может быть обнаружено что-либо "большее", чем постоянный фактор.

Когда вы заинтересованы в выполнении вычислений, размер которых "большой" достаточно, чтобы считаться приблизительно бесконечным, тогда большая нотация O примерно равна стоимости решения вашей проблемы.


Если вышеизложенное не имеет смысла, то у вас нет совместимого интуитивного понятия бесконечности в вашей голове, и вы, вероятно, должны игнорировать все вышеперечисленное; единственный способ, которым я знаю, чтобы эти идеи были строгими или объяснить их, если они еще не являются интуитивно полезными, - это научить вас большой нотации O или что-то подобное. (хотя, как только вы хорошо поймете большую нотацию O в будущем, может быть целесообразно пересмотреть эти идеи)

8
ответ дан Hurkyl 16 мая '15 в 19:02
источник поделиться

Простейший способ взглянуть на него (на простом английском языке)

Мы пытаемся увидеть, как количество входных параметров влияет на время работы алгоритма. Если время работы вашего приложения пропорционально количеству входных параметров, то говорят, что оно находится в 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. На самом деле это простая концепция. Если вы услышите, что профессор Шелчук (он же учитель уровня Бога) объясняет это, вы скажете: "О, это все!".

6
ответ дан developer747 16 авг. '15 в 23:38
источник поделиться

Что такое простое английское объяснение "Big O"?

Очень быстрое примечание:

O в "Big O" означает "Order" (или точно "порядок")
так что вы могли бы получить его идею буквально, что он использовал, чтобы заказать что-то, чтобы сравнить их.

  • "Big O" делает две вещи:

    1. Оценивает количество шагов, которые ваш компьютер применяет для выполнения задачи.
    2. Содействовать процессу сравнить с другими, чтобы определить, хорошо это или нет?
    3. "Big O" достигает вышеупомянутых двух со стандартными Notations.
  • Есть семь наиболее используемых обозначений

    1. O (1), означает, что ваш компьютер выполняет задание с 1 шагом, отлично, заказано №1
    2. O (logN), означает, что ваш компьютер выполняет задачу с шагами logN, это хорошо, logN № 2
    3. O (N), завершите задачу с N шагов, ее справедливость, заказ № 3
    4. O (NlogN), завершает задачу с O(NlogN) шагов O(NlogN), это не хорошо, Order No.4
    5. O (N ^ 2), выполнить задание с N^2 шагами, это плохо, Приказ №5
    6. O (2 ^ N), выполните задачу с шагами 2^N, это ужасно, Приказ №6
    7. O (N!), Выполните задачу с N! шаги, это ужасно, Приказ № 7

enter image description here

Предположим, вы получили нотацию 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" классифицирует алгоритмы и стандартизирует процесс сравнения.

6
ответ дан JawSaw 13 апр. '18 в 15:36
источник поделиться

Это очень упрощенное объяснение, но я надеюсь, что оно охватывает самые важные детали.

Скажем, ваш алгоритм, связанный с проблемой, зависит от некоторых "факторов", например, пусть он делает N и X.

В зависимости от N и X для вашего алгоритма потребуются некоторые операции, например, в случае WORST это операции 3(N^2) + log(X).

Так как Big-O не слишком заботится о постоянном множителе (aka 3), Big-O вашего алгоритма O(N^2 + log(X)). В основном это означает "количество операций, которые ваш алгоритм требует для наихудших шкал с этим".

4
ответ дан nkt 11 окт. '15 в 21:00
источник поделиться

Определение: - нотация Big O - это обозначение, в котором указано, как производительность алгоритма будет выполняться, если увеличивается ввод данных.

Когда мы говорим об алгоритмах, есть 3 важных столпа ввода, вывода и обработки алгоритма. Big O - это символическая нотация, в которой говорится, что если ввод данных увеличивается, в какой скорости производительность будет отличаться от обработки алгоритма.

Я бы посоветовал вам увидеть это видео с YouTube, которое подробно объясняет Big O Notation с примерами кода.

Основные элементы алгоритма

Так, например, предположим, что алгоритм берет 5 записей, а время, требуемое для обработки, составляет 27 секунд. Теперь, если мы увеличиваем записи до 10, алгоритм занимает 105 секунд.

В простых словах время занимает квадрат числа записей. Обозначим это через O (n ^ 2). Это символическое представление обозначается как обозначение Big O.

Теперь обратите внимание, что единицы могут быть любыми входами, это могут быть байты, бит количества записей, производительность может быть измерена в любом устройстве, таком как секунда, минуты, дни и т.д. Таким образом, это не точная единица, а скорее взаимосвязь.

Знаки больших O

Например, посмотрите на приведенную ниже функцию "Function1", которая берет коллекцию и обрабатывает первую запись. Теперь для этой функции производительность будет одинаковой, независимо от того, вы ставите 1000, 10000 или 100000 записей. Таким образом, мы можем обозначить его O (1).

void Function1(List<string> data)
{
string str = data[0];
}

Теперь см. ниже функцию "Function2()". В этом случае время обработки увеличится с количеством записей. Мы можем обозначить производительность этого алгоритма, используя O (n).

void Function2(List<string> data)
        {
            foreach(string str in data)
            {
                if (str == "shiv")
                {
                    return;
                }
            }
        }

Когда мы видим нотацию Big O для любого алгоритма, мы можем классифицировать их по трем категориям производительности: -

  • Журнал и постоянная категория: - Любой разработчик хотел бы увидеть их производительность в этой категории.
  • Линейный: - Разработчик не захочет видеть алгоритмы в этой категории, пока не останется последний вариант или единственный вариант.
  • Экспоненциальный: - Здесь мы не хотим видеть наши алгоритмы, и требуется переделка.

Таким образом, просматривая нотацию Big O, мы классифицируем хорошие и плохие зоны для алгоритмов.

Классификация Bog O

Я бы порекомендовал вам посмотреть это 10-минутное видео, в котором обсуждается Big O с образцом кода

https://www.youtube.com/watch?v=k6kxtzICG_g

4
ответ дан Shivprasad Koirala 11 мая '18 в 11:33
источник поделиться

Введение

алгоритм: процедура/формула для решения проблемы


Как анализировать алгоритмы и как мы можем сравнивать алгоритмы друг с другом?

Пример

: вам и другу предлагается создать функцию для суммирования чисел от 0 до N. Вы получаете f (x), а ваш друг придумывает g (x). Обе функции имеют одинаковый результат, но другой алгоритм. Чтобы объективно сравнить эффективность алгоритмов, мы используем нотацию Big-O.

Обозначение Big-O: описывает, как быстро время выполнения будет расти относительно ввода, так как вход становится сколь угодно большим.

3 ключевых вылета:

  • Сравните, как быстро среда выполнения растет НЕ сравнивает точное время выполнения (зависит от аппаратного обеспечения)
  • Только связанные с временем выполнения растут относительно ввода (n)
  • По мере того, как n становится сколь угодно большим, сосредоточьтесь на терминах, которые будут расти быстрее, чем когда n станет большим (подумайте бесконечность) AKA асимптотический анализ

Сложность пространства:, кроме временной сложности, мы также заботимся о сложности пространства (сколько памяти/пространства используется алгоритмом). Вместо проверки времени операций мы проверяем размер выделения памяти.

3
ответ дан Ryan Efendy 10 окт. '17 в 6:15
источник поделиться
  • 1
  • 2

Другие вопросы по меткам