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

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

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

Есть идеи?

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

Ответы [ 18 ]

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

Вы также должны соблюдать осторожность при выполнении таких тестов. Некоторые алгоритмы будут сильно зависеть от типа ввода.

Взять, например, Quicksort. Это наихудший случай O (n²), но обычно O (nlogn). Для двух входов одинакового размера.

Торговый продавец (я думаю, не уверен) O (n²) ( РЕДАКТИРОВАТЬ: правильное значение 0 (n!) Для алгоритма грубой силы ), но большинство алгоритмов получаются довольно хорошо приближенными Решения гораздо быстрее.

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

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

Ну, так как вы не можете доказать, останавливается ли функция или нет, я думаю, вы спрашиваете немного.

В противном случае у @Godeke это есть.

0 голосов
/ 04 августа 2018

Я использую библиотеку big_O (ссылка здесь ), которая соответствует изменению времени выполнения для независимой переменной n, чтобы вывести порядок класса роста O().

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

Проверьте код в этот ответ .

Пример вывода,

Measuring .columns[::-1] complexity against rapid increase in # rows
--------------------------------------------------------------------------------
Big O() fits: Cubic: time = -0.017 + 0.00067*n^3
--------------------------------------------------------------------------------
Constant: time = 0.032                                        (res: 0.021)
Linear: time = -0.051 + 0.024*n                               (res: 0.011)
Quadratic: time = -0.026 + 0.0038*n^2                         (res: 0.0077)
Cubic: time = -0.017 + 0.00067*n^3                            (res: 0.0052)
Polynomial: time = -6.3 * x^1.5                               (res: 6)
Logarithmic: time = -0.026 + 0.053*log(n)                     (res: 0.015)
Linearithmic: time = -0.024 + 0.012*n*log(n)                  (res: 0.0094)
Exponential: time = -7 * 0.66^n                               (res: 3.6)
--------------------------------------------------------------------------------
0 голосов
/ 09 декабря 2009

Как уже говорили другие, это теоретически невозможно. Но на практике вы можете сделать обоснованное предположение о том, является ли функция O ( n ) или O ( n ^ 2), если вы не против иногда ошибаться.

Первый раз алгоритм, запускающий его на ввод различных n . Нанесите точки на график log-log . Нарисуйте наиболее подходящую линию через точки. Если линия хорошо соответствует всем точкам, то данные свидетельствуют о том, что алгоритм имеет вид O ( n ^ k ), где k - наклон линии .

Я не статистика. Вы должны взять все это с зерном соли. Но на самом деле я сделал это в контексте автоматизированного тестирования для снижения производительности. Патч здесь содержит некоторый JS-код для него.

0 голосов
/ 29 августа 2009

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

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

Удивительно, но результаты не выявили студентов, которые обманули. Это может быть потому, что наши студенты честны и хотят учиться (или просто знали, что мы проверим это ;-)). Можно пропустить мошенничество студентов, если входные данные небольшие, или если сам вход упорядочен или что-то в этом роде. Также возможно ошибаться в отношении учеников, которые не обманывают, но имеют большие постоянные значения.

Но, несмотря на возможные ошибки, оно того стоит, так как экономит много времени на проверку.

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

Легко получить указание (например, «является ли функция линейной? Сублинейной? Полиномиальной? Экспоненциальной")

Трудно найти точную сложность.

Например, вот решение Python: вы предоставляете функцию и функцию, которая создает для нее параметры размера N. Вы получаете список (n, времени) значений для построения графика или для выполнения регрессионного анализа . Он умножается один раз на скорость, чтобы получить действительно хорошее указание, он должен был бы рассчитать его много раз, чтобы минимизировать влияние факторов окружающей среды (например, с помощью модуля timeit ).

import time
def measure_run_time(func, args):
  start = time.time()
  func(*args)
  return time.time() - start

def plot_times(func, generate_args, plot_sequence):
  return [
    (n, measure_run_time(func, generate_args(n+1)))
    for n in plot_sequence
  ]

И чтобы использовать его для сортировки по времени:

def bubble_sort(l):
  for i in xrange(len(l)-1):
    for j in xrange(len(l)-1-i):
      if l[i+1] < l[i]:
        l[i],l[i+1] = l[i+1],l[i]

import random
def gen_args_for_sort(list_length):
  result = range(list_length) # list of 0..N-1
  random.shuffle(result) # randomize order
  # should return a tuple of arguments
  return (result,)

# timing for N = 1000, 2000, ..., 5000
times = plot_times(bubble_sort, gen_args_for_sort, xrange(1000,6000,1000))

import pprint
pprint.pprint(times)

Это напечатано на моей машине:

[(1000, 0.078000068664550781),
 (2000, 0.34400010108947754),
 (3000, 0.7649998664855957),
 (4000, 1.3440001010894775),
 (5000, 2.1410000324249268)]
0 голосов
/ 26 января 2009

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

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

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

...