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

Вопрос

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

что я сделал, прежде чем размещать вопрос на SO ?

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

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

что я знаю ?

скажите для a код так же прост, как и приведенный ниже:

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 i=0; это будет выполняться только после. Время фактически рассчитано на i=0 и не декларация.

i это будет выполнено N+1 времени

i++; это будет выполнено N времени

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

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

Примечание: это все еще может быть неправильно, так как я не уверен в своем понимании сложности расчета времени

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

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

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

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

9 ответов


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

наиболее распространенной метрикой для вычисления сложности времени является Big O notation. Это удаляет все постоянные факторы, так что время выполнения может быть оценено относительно 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 );
}

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

В общем, делать что-то с каждым элементом в одном измерении линейно, делать что-то с каждым элементом в двух измерениях квадратично, а деление рабочей области пополам логарифмично. Есть и другие большие меры O, такие как кубический, экспоненциальный и квадратный корень, но они не так распространены. Обозначение Big O описывается как O (), где находится мера. Алгоритм quicksort будет описан как O ( N * log (N ) ).

обратите внимание, что ничто из этого не учитывало лучшие, средние и худшие меры. У каждого будет своя большая буква "О". Также обратите внимание, что это очень упрощенный объяснение. Big O является наиболее распространенным, но это также более сложно, что я показал. Есть и другие обозначения, такие как большая омега, маленькая О и большая тета. Вы, вероятно,не столкнетесь с ними вне курса анализа алгоритмов. ;)


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

1. Введение

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

2. Big O notation

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

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

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

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

3. O (1) Постоянное Время:

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

примеры:

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

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

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

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


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

  • O (n): временная сложность цикла рассматривается как 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) если переменные цикла разделены / умножены на a постоянное количество.

    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


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

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

пример :

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

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

пример:

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. Таким образом, его большой ох все еще постоянная T(n) = O (1).

3-Looping (for, 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;

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

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

  1. O (1) – Постоянное Время Постоянное время означает, что время работы постоянно, это не зависит от размера входных данных.

  2. O (n) – линейное время Когда алгоритм принимает N входных размеров, он также будет выполнять n операций.

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

  4. O (N log n) - Линеарифмическое время Это время работы часто встречается в "алгоритмах divide & conquer", которые рекурсивно разделяют проблему на подзадачи, а затем объединяют их за n раз. Пример: Сортировка Слиянием алгоритм.

  5. O (n2) – Квадратичное Время Посмотрите алгоритм сортировки пузырьков!

  6. O (n3) – Кубический Время Он имеет тот же принцип с O(N2).

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

  8. O (n!) – Факторный Время МЕДЛЕННЕЕ !!! Пример: проблема коммивояжера (TSP)

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


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

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

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

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

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

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

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

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


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

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

O (N)

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

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

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

O (N^2)

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

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

Пространство И Коммуникации

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

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

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

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

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

для всех k.


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


O (n) - большая o-нотация, используемая для записи временной сложности алгоритма. При сложении количества выполнений в алгоритме вы получите выражение в результате 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)