Как найти временную сложность алгоритма

Вопрос

Как найти временную сложность алгоритма?

Что я сделал до публикации вопроса о SO?

Я прошел этот, этот и многие другие ссылки

Но нет, где я смог найти ясное и прямое объяснение того, как рассчитать сложность времени.

Что я знаю?

Скажите, что код такой же простой, как ниже:

char h = 'y'; // This will be executed 1 time
int abc = 0; // This will be executed 1 time

Произнесите цикл, подобный приведенному ниже:

for (int i = 0; i < N; i++) {        
    Console.Write('Hello World !');
}

int я = 0; Это будет выполняться только один раз. Время фактически вычисляется до i=0, а не декларации.

я < N; Это будет выполнено N + 1 раз

я ++; Это будет выполнено N раз

Таким образом, количество операций, требуемых этим циклом, равно

{1+ (N + 1) + N} = 2N + 2

Примечание. Это может быть неправильно, поскольку я не уверен в своем понимании при вычислении временной сложности.

Что я хочу знать?

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

O (N), O (n2), O (log n), O (n!).... и многие другие,

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

Ответ 1

Как найти временную сложность алгоритма

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

Например, давайте посмотрим, как упростить машинные инструкции 2N + 2, чтобы описать это как просто O(N).

Почему мы удаляем два 2?

Мы заинтересованы в производительности алгоритма по мере того, как N становится большим.

Рассмотрим два члена 2N и 2.

Каково относительное влияние этих двух членов, когда N становится большим? Предположим, что N - миллион.

Тогда первый член равен 2 миллионам, а второй член - только 2.

По этой причине мы бросаем все, кроме наибольших членов для больших N.

Итак, теперь мы прошли от 2N + 2 до 2N.

Традиционно нас интересует только производительность до постоянных факторов.

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

Итак, 2N становится просто N.

Ответ 2

Это отличная статья: http://www.daniweb.com/software-development/computer-science/threads/13488/time-complexity-of-algorithm

Нижеприведенный ответ скопирован сверху (в случае, если прекрасная ссылка обанкротится)

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

Ответ 3

Взято отсюда - Введение во временную сложность алгоритма

1. Введение

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

2. Большая О запись

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

Например, если время, требуемое алгоритмом на всех входах размера n, составляет не более 5n 3 + 3n, асимптотическая сложность времени составляет O (n 3). Подробнее об этом позже.

Еще несколько примеров:

  • 1 = O (n)
  • n = O (n 2)
  • log (n) = O (n)
  • 2 n + 1 = O (n)

3. O (1) постоянное время:

Говорят, что алгоритм работает в постоянном времени, если ему требуется одинаковое количество времени независимо от размера ввода.

Примеры:

  • массив: доступ к любому элементу
  • стек фиксированного размера: методы push и pop
  • очередь фиксированного размера: методы enqueue и dequeue

4. O (n) линейное время

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

Рассмотрим следующие примеры: ниже я линейно ищу элемент, который имеет временную сложность O (n).

int find = 66;
var numbers = new int[] { 33, 435, 36, 37, 43, 45, 66, 656, 2232 };
for (int i = 0; i < numbers.Length - 1; i++)
{
    if(find == numbers[i])
    {
        return;
    }
}

Больше примеров:

  • Массив: линейный поиск, обход, поиск минимума и т.д.
  • ArrayList: содержит метод
  • Очередь: содержит метод

5. O (log n) Логарифмическое время:

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

Пример: бинарный поиск

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

6. O (n2) квадратичное время

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

Примеры:

7. Некоторые полезные ссылки

Ответ 4

Хотя есть несколько хороших ответов на этот вопрос. Я хотел бы дать еще один ответ здесь с несколькими примерами loop.

  • O (n): Time Сложность цикла рассматривается как O (n), если переменные цикла увеличиваются/уменьшаются на постоянную величину. Например, следующие функции имеют O (n) временную сложность.

    // Here c is a positive integer constant   
    for (int i = 1; i <= n; i += c) {  
        // some O(1) expressions
    }
    
    for (int i = n; i > 0; i -= c) {
        // some O(1) expressions
    }
    
  • O (n ^ c): временная сложность вложенных циклов равна количеству раз, когда выполняется самый внутренний оператор. Например, следующие контуры образцов имеют сложность времени O (n ^ 2)

    for (int i = 1; i <=n; i += c) {
       for (int j = 1; j <=n; j += c) {
          // some O(1) expressions
       }
    }
    
    for (int i = n; i > 0; i += c) {
       for (int j = i+1; j <=n; j += c) {
          // some O(1) expressions
    }
    

    Например, сортировка сортировки и сортировка вставки имеют сложность времени O (n ^ 2).

  • O (Logn) Время Сложность цикла рассматривается как O (Logn), если переменные цикла делятся/умножаются на постоянную величину.

    for (int i = 1; i <=n; i *= c) {
       // some O(1) expressions
    }
    for (int i = n; i > 0; i /= c) {
       // some O(1) expressions
    }
    

    Например, двоичный поиск имеет сложность времени O (Logn).

  • O (LogLogn) Время Сложность цикла рассматривается как O (LogLogn), если переменные цикла уменьшены/увеличены экспоненциально на постоянную величину.

    // Here c is a constant greater than 1   
    for (int i = 2; i <=n; i = pow(i, c)) { 
       // some O(1) expressions
    }
    //Here fun is sqrt or cuberoot or any other constant root
    for (int i = n; i > 0; i = fun(i)) { 
       // some O(1) expressions
    }
    

Один пример анализа временной сложности

int fun(int n)
{    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < n; j += i)
        {
            // Some O(1) task
        }
    }    
}

Анализ:

For i = 1, the inner loop is executed n times. For i = 2, the inner loop is executed approximately n/2 times. For i = 3, the inner loop is executed approximately n/3 times. For i = 4, the inner loop is executed approximately n/4 times. ……………………………………………………. For i = n, the inner loop is executed approximately n/n times.

Таким образом, общая временная сложность алгоритма выше (n + n/2 + n/3 + … + n/n), который становится n * (1/1 + 1/2 + 1/3 + … + 1/n)

Важная вещь о серии (1/1 + 1/2 + 1/3 + … + 1/n) равна O (Logn). Таким образом, временная сложность вышеуказанного кода - O (nLogn).


Ref: 1 2 3

Ответ 5

Сложность времени с примерами

1 - Основные операции (арифметика, сравнение, доступ к элементам массивов, присвоение): время работы всегда постоянное O (1)

Пример:

read(x)                               // O(1)
a = 10;                               // O(1)
a = 1.000.000.000.000.000.000         // O(1)

2 - Если then else statement: Выполняет только максимальное время работы из двух или более возможных операторов.

Пример:

age = read(x)                               // (1+1) = 2
if age < 17 then begin                      // 1
      status = "Not allowed!";              // 1
end else begin
      status = "Welcome! Please come in";   // 1
      visitors = visitors + 1;              // 1+1 = 2
end;

Таким образом, сложность вышеуказанного псевдокода T (n) = 2 + 1 + max (1, 1 + 2) = 6. Таким образом, его большое oh по-прежнему остается постоянным T (n) = O (1).

3 - Looping (для, while, repeat): Время выполнения для этого оператора - это число циклов, умноженное на количество операций внутри этого цикла.

Пример:

total = 0;                                  // 1
for i = 1 to n do begin                     // (1+1)*n = 2n
      total = total + i;                    // (1+1)*n = 2n
end;
writeln(total);                             // 1

Таким образом, его сложность T (n) = 1 + 4n + 1 = 4n + 2. Таким образом, T (n) = O (n).

4 - Вложенная петля (циклический цикл): поскольку в основном цикле имеется по крайней мере один цикл, время выполнения этого оператора используется O (n ^ 2) или O (n ^ 3).

Пример:

for i = 1 to n do begin                     // (1+1)*n  = 2n
   for j = 1 to n do begin                  // (1+1)n*n = 2n^2
       x = x + 1;                           // (1+1)n*n = 2n^2
       print(x);                            // (n*n)    = n^2
   end;
end;

Общее время выполнения

При анализе алгоритма существует некоторое общее время работы:

  • O (1) - Постоянное время Постоянное время означает, что время работы постоянное, на него не влияет размер ввода.

  • O (n) - Линейное время Когда алгоритм принимает n входного размера, он также выполняет n операций.

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

  • O (n log n) - Линеаритическое время Это время работы часто встречается в "алгоритмах разделения и покорения", которые рекурсивно делят проблему на субаремы и затем объединяют их в n раз. Пример: алгоритм Merge Sort.

  • O (n 2) - Квадратичное время Посмотрите алгоритм сортировки Bubble!

  • O (n 3) - Кубическое время Он имеет тот же принцип с O (n 2).

  • O (2 n) - Экспоненциальное время Это очень медленно, так как вход становится больше, если n = 1000.000, T (n) будет 21000.000. Алгоритм Brute Force имеет это время работы.

  • O (n!) - Факториальное время САМЫЙ МЕДЛЕННЫЙ!!! Пример: проблема с продавцом (TSP)

Взято из этой статьи. Очень хорошо объясненный должен дать прочитать.

Ответ 6

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

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

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

  • Постоянное время имеет порядок роста 1, например: a = b + c.

  • Логарифмическое время имеет порядок роста LogN, это обычно происходит когда вы делите что-то наполовину (двоичный поиск, деревья, даже петли) или умножаете что-то таким же образом.

  • Линейный, порядок роста N, например

    int p = 0;
    for (int i = 1; i < N; i++)
      p = p + 2;
    
  • Линеаритический, порядок роста n*logN, обычно встречается в алгоритмах разделения и покорения.

  • Кубический, порядок роста N^3, классический пример - тройной цикл, где вы проверяете все триплеты:

    int x = 0;
    for (int i = 0; i < N; i++)
       for (int j = 0; j < N; j++)
          for (int k = 0; k < N; k++)
              x = x + 2
    
  • Экспоненциальный, порядок роста 2^N, обычно возникает, когда вы выполняете исчерпывающий поиск, например, проверяете подмножества некоторого набора.

Ответ 7

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

Как и большинство вещей, коктейль может помочь нам понять.

O (N)

Когда вы приходите на вечеринку, вам нужно пожать всем руку (выполнить операцию над каждым предметом). По мере того, как количество участников N увеличивается, время/работа, которую вам потребуется, чтобы дрожать, каждая рука увеличивается как O(N).

Почему O(N), а не cN?

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

O (N ^ 2)

Ведущий коктейль хочет, чтобы вы играли в глупую игру, где все встречали всех. Поэтому вы должны встретить других людей и, потому что следующий человек уже встретил вас, они должны встретить людей N-2 и так далее. Сумма этого ряда x^2/2+x/2. По мере роста количества участников, термин x^2 становится очень быстрым, поэтому мы просто бросаем все остальное.

O (N ^ 3)

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

O (1)

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

O (log N)

Ведущий выложил всех за стол в алфавитном порядке. Где Дэн? Вы полагаете, что он должен быть где-то между Адамом и Мэнди (конечно, не между Мэнди и Заком!). Учитывая это, он между Джорджем и Мэнди? Нет. Он должен быть между Адамом и Фредом, а также между Синди и Фредом. И так далее... мы можем эффективно найти Дэна, посмотрев на половину набора, а затем на половину этого набора. В конечном счете мы смотрим на людей O (log_2 N).

O (N log N)

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

Лучший/худший случай

Вы приходите на вечеринку и должны найти Иниго - сколько времени это займет? Это зависит от того, когда вы приедете. Если все мелькают вокруг, вы попали в худший случай: это займет время O(N). Однако, если все сидят за столом, это займет всего O(log N). Или, может быть, вы можете использовать мощность мотогонщика для хозяев, и это займет всего O(1).

Предполагая, что хост недоступен, можно сказать, что алгоритм поиска Иниго имеет нижнюю границу O(log N) и верхнюю границу O(N), в зависимости от состояния стороны, когда вы приходите.

Пространство и связь

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

Кнут написал хорошую статью о первом названии "Сложность песен" .

Теорема 2: Существуют сколь угодно длинные песни сложности O (1).

Доказательство: (из-за Кейси и полосы Солнца). Рассмотрим песни Sk, определенные по (15), но с

V_k = 'That the way,' U 'I like it, ' U
U   = 'uh huh,' 'uh huh'

для всех k.

Ответ 8

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

Ответ 9

O (n) - большая нотация, используемая для записи временной сложности алгоритма. Когда вы добавляете количество выполнений в алгоритме, вы получите выражение в виде как 2N + 2, в этом выражении N является доминирующим термином (термин имеет наибольший эффект на выражение, если его значение увеличивается или уменьшается). Теперь O (N) - временная сложность, а N - доминирующий член. Пример

For i= 1 to n;
  j= 0;
while(j<=n);
  j=j+1;

здесь общее число исполнений для внутреннего цикла равно n + 1, а общее количество исполнений для внешнего цикла - n (n + 1)/2, поэтому общее число исполнений для всего алгоритма n + 1 + n (n + 1/2) = (n ^ 2 + 3n)/2. здесь n ^ 2 является доминирующим термином, поэтому временная сложность этого алгоритма равна O (n ^ 2)

Ответ 10

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

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

Вы также можете быть проиндексированы в http://en.wikipedia.org/wiki/Big_O_notation, несмотря на то, что он довольно математичен.

Я также нашел http://en.wikipedia.org/wiki/Analysis_of_algorithms