Программно получить эффективность кода Big-O - PullRequest
28 голосов
/ 26 января 2009

Интересно, существует ли какой-либо автоматический способ определения (хотя бы приблизительно) временной сложности Big-O для данной функции?

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

Есть идеи?

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

Ответы [ 18 ]

57 голосов
/ 26 января 2009

Похоже, то, что вы просите, является продолжением проблемы остановки. Я не верю, что такое возможно даже в теории.

Просто отвечаю на вопрос "Будет ли когда-нибудь выполняться эта строка кода?" было бы очень трудно, если не невозможно сделать в общем случае.

Отредактировано, чтобы добавить: Хотя общий случай неразрешим, смотрите здесь для частичного решения: http://research.microsoft.com/apps/pubs/default.aspx?id=104919

Кроме того, некоторые утверждают, что выполнение анализа вручную - это единственный вариант, но я не верю, что это действительно правильный взгляд на это. Неразрешимая проблема все еще неразрешима, даже когда человек добавлен в систему / машину. После дальнейших размышлений, я полагаю, что 99% -ный раствор может быть выполнимым и даже может работать так же хорошо, как человек.

32 голосов
/ 26 января 2009

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

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

Что касается проверки кода методов, то их не существует. Но заставить ваш код работать с различной длиной и вывести простой файл (было бы достаточно RunSize RunLength). Генерирование правильных тестовых данных может быть более сложным (некоторые алгоритмы работают лучше / хуже с частично упорядоченными данными, поэтому вы захотите сгенерировать данные, которые представляют ваш обычный вариант использования ).

Из-за проблем с определением «что является большим» и того факта, что производительность зависит от данных, я считаю, что статический анализ часто вводит в заблуждение. При оптимизации производительности и выборе между двумя алгоритмами реальный тест «резиновые удары» является единственным последним арбитром, которому я доверяю.

9 голосов
/ 26 января 2009

Короткий ответ: невозможно, потому что постоянные имеют значение .

Например, я мог бы написать функцию, которая запускается в O((n^3/k) + n^2). Это упрощается до O (n ^ 3), поскольку при приближении n к бесконечности член n^3 будет доминировать в функции независимо от константы k.

Однако, если k очень велико в приведенном выше примере функции, функция будет работать почти точно n^2 до некоторой точки пересечения, в которой член n^3 начнет доминировать. Поскольку константа k будет неизвестна любому инструменту профилирования, будет невозможно узнать, насколько велик набор данных для проверки целевой функции. Если k может быть сколь угодно большим, вы не можете обработать тестовые данные, чтобы определить время выполнения большого числа.

8 голосов
/ 26 января 2009

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

  1. Сложность алгоритма не является вопросом "программирования"; это вопрос "информатики". Ответ на вопрос требует анализа кода с точки зрения математика, так что вычисление сложности Big-O является практически формой математического доказательства. Это требует очень глубокого понимания основных компьютерных операций, алгебры, возможно, исчисления (пределов) и логики. Никакое количество «тестирования» не может быть заменено этим процессом.

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

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

  4. Для тех, кто серьезно задумывается о написании такого инструмента, я предлагаю следующее упражнение. Выберите достаточно простой алгоритм, такой как ваш любимый вид, в качестве вашего предметного алгоритма. Получить надежный справочник (книга, веб-учебник), чтобы провести вас через процесс расчета сложности алгоритма и, в конечном итоге, "Big-O". Документируйте свои шаги и результаты по мере того, как вы проходите процесс с помощью вашего предметного алгоритма. Выполните шаги и задокументируйте свой прогресс для нескольких сценариев, таких как лучший случай, наихудший случай и средний случай. Как только вы закончите, просмотрите свою документацию и спросите себя, что потребуется, чтобы написать программу (инструмент), которая сделает это за вас. Это можно сделать? Сколько на самом деле будет автоматизировано, а сколько еще будет ручным?

С наилучшими пожеланиями.

8 голосов
/ 26 января 2009

Мне любопытно, почему вы хотите это сделать? По моему опыту, когда кто-то говорит: «Я хочу выяснить сложность этого алгоритма во время выполнения», они не спрашивают, что, по его мнению, они спрашивают. Скорее всего, вы спрашиваете, какова реальная производительность такого алгоритма для вероятных данных. Вычисление Big-O функции имеет разумную пользу, но есть так много аспектов, которые могут изменить «реальную производительность во время выполнения» алгоритма в реальном использовании, что ничто не сравнится с инструментами и тестированием.

Например, следующие алгоритмы имеют одинаковый точный Big-O (дурацкий псевдокод):

пример а:

huge_two_dimensional_array foo
for i = 0, i < foo[i].length, i++
  for j = 0; j < foo[j].length, j++
    do_something_with foo[i][j]

пример б:

huge_two_dimensional_array foo
for j = 0, j < foo[j].length, j++
  for i = 0; i < foo[i].length, i++
    do_something_with foo[i][j]

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

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

Ура!

4 голосов
/ 26 января 2009

Это может работать для простых алгоритмов, но как насчет O (n ^ 2 lg n) или O (n lg ^ 2 n)?

Вы можете легко обмануть визуально.

И если это действительно плохой алгоритм, возможно, он не вернется даже при n = 10.

3 голосов
/ 16 апреля 2009

Доказательство того, что это неразрешимо:

Предположим, что у нас был некоторый алгоритм HALTS_IN_FN (Программа, функция), который определял, остановилась ли программа в O (f (n)) для всех n, для некоторой функции f.

Пусть P будет следующей программой:

if(HALTS_IN_FN(P,f(n)))
{
    while(1);
}
halt;

Поскольку функция и программа фиксированы, HALTS_IN_FN на этом входе имеет постоянное время. Если HALTS_IN_FN возвращает true, программа работает вечно и, конечно, не останавливается в O (f (n)) для любого f (n). Если HALTS_IN_FN возвращает false, программа останавливается за O (1) раз.

Таким образом, у нас есть парадокс, противоречие, и поэтому программа неразрешима.

2 голосов
/ 26 января 2009

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

Допустим, у вас есть программа, которая имеет набор вложенных циклов, каждый из которых основан на количестве элементов в массиве. O (N ^ 2). Но что, если внутренний цикл запускается только в очень специфических обстоятельствах? Скажем, в среднем он запускается в случаях aprox log (n). Внезапно наш «очевидно» алгоритм O (n ^ 2) действительно становится O (n log n). Написание программы, которая могла бы определить, будет ли выполняться внутренний цикл и как часто, потенциально сложнее, чем исходная проблема.

Помните, O (N) не бог; высокие константы могут и будут менять игровое поле. Алгоритмы быстрой сортировки, конечно, O (n log n), но когда рекурсия становится достаточно маленькой, скажем, до 20 элементов или около того, многие реализации быстрой сортировки изменят тактику на отдельный алгоритм, поскольку на самом деле быстрее выполнить другой тип сортировки скажем, вставка сортируется с худшим O (N), но намного меньшей константой

Итак, поймите свои данные, дайте обоснованные догадки и проверьте.

2 голосов
/ 26 января 2009

Я думаю, что это сделать практически невозможно. Помните, что O (g (n)) - верхняя граница наихудшего случая, и многие функции работают лучше, чем для многих наборов данных. Вы должны были бы найти набор данных наихудшего случая для каждого из них, чтобы сравнить их. Это сложная задача для многих алгоритмов.

2 голосов
/ 26 января 2009

Джеффри Л. Уитледж прав. Простое сокращение от проблемы остановки доказывает, что это неразрешимо ...

ТАКЖЕ, если бы я мог написать эту программу, я бы использовал ее для решения P против NP и получил бы 1 миллион ... B -)

...