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

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

4630
задан Arec Barrwin 28 янв. '09 в 14:10
источник поделиться
39 ответов
  • 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 (пересмотрено).

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

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

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


основы

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

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

нотация big-O не заботится о постоянных факторах: 9x² что функция 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 - наихудший сценарий использования процессора/памяти".


Приложения

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

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

пример

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

Немного подкрепимся: количество рукопожатий точно равно n-выбирать-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 = 1 миллион, отношение # рукопожатия /x²: 0,499999...


Интуиция здания

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

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

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

    N → (2N) = 2 (N)

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

    → (2N) ² = 4 ()

  • ... Я удваиваю кубы (в восьмерике) времени, которое занимает алгоритм 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) ("постоянное время"). "(Самый дешевый!)

    с * 1с * 1

  • ... Я "(в основном) удваиваю" время, которое занимает алгоритм 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/...)

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

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

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

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


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

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

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

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

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

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


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

Math Addenda

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

372
ответ дан 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
источник поделиться

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

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

Обозначение 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
источник поделиться

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

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

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

Вы хотите знать все, что нужно знать о большом 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. Это устраняет все постоянные факторы, так что время работы можно оценить по отношению к 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"?

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

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

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

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

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

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

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

Другие вопросы по меткам или Задайте вопрос