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

Хороший код имеет два качества:

  • Читаемость
  • Масштабируемость

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

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

Обозначение Big O используется для оценки матрицы производительности кода с точки зрения пространственной и временной сложности. Он сообщает, насколько хорошо код будет работать для больших входных данных, оценивая время выполнения и пространство независимо от оборудования, на котором он выполняется.

Сложность времени:оценочное количество времени, необходимое для выполнения

Следующие пункты помогут вам рассчитать большую временную сложность кода:

  • Большой O =, +, -, *, /, % равен 0 (1)
  • Большой O цикла - это O (N), где N - количество выполнений цикла.
  • Big O всегда оценивается для наихудшего сценария
  • Как правило, O(1) + O(1) +… = O(1) и O(N) + O(N) +…= O(N)
  • Кроме того, мы пренебрегаем постоянными терминами и присваиваниями и вычисляем только Big O циклов и дорогостоящих операций.
  • Большой O вложенного цикла равен O(N) * O(N) = O(N²)
  • Для разных входов вложенных циклов O(N) * O(M) = O(N*M) = O(P)

Рассмотрим этот код:

def TwoSum(numbers, target):
    mapValues = {}                                     O(1)
    for i in range(len(numbers)):                      O(N):N=length
        currentVal = mapValues.get(numbers[i], None)   O(1)
        if(currentVal is not None):                    O(1)
            return [currentVal, i]                     O(1)
        else:
            numberToFind = target - numbers[i]         O(1)
            mapValues[numberToFind] = i                0(1)
    return None                                        O(1)

Note: Statements inside loop will execute N=length of numbers times, so their complexity will be multiplied with O(N)
Big O = O(1) + O(N)*(O(1) + O(1) + O(1) + O(1) + O(1)) + O(1)
As O(1) + O(1) +O(1) + ... = O(1), so
      = O(1) + O(N)*(O(1) + O(1)
Also O(1) + O(N) = O(N) and O(1)*O(N) = O(N)
      = O(1) + O(N) + O(1)
      = O(N) 

O(N) — это общий термин, где N означает большое число. Мы можем оценить, что N/2, N/3 равны N, когда N слишком велико.

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

Пространственная сложность:оценочный объем памяти, необходимый программе.

Space Complexity рассчитывается с точки зрения инициализации переменных внутри программы. Сложность пространства зависит от:

  • Переменные
  • Структуры данных
  • Вызовы функций
  • Распределения

Рассмотрим пример ниже для лучшего понимания:

newArray = []                O(1)
for i in range(n):
    newArray.append(i);      O(N)
Big O = O(1) + O(N) = O(N)

Здесь сложность пространства равна O(N), потому что новая память выделяется для массива N раз.

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