Os Fundamentos da Notação Big-O

Como utilizar a notação Big-O para medir o desempenho e a escalabilidade do seu algoritmo

Foto de Nadine Shaabana no Unsplash

Maneira correta de medir o desempenho de um algoritmo

  • O computador que estamos usando terá um enorme impacto na rapidez com que o algoritmo é executado. Se você estiver usando um hardware antigo, é esperado que o código seja executado mais lentamente do que se estivesse usando um hardware mais novo.
  • Se você executar o algoritmo enquanto outros programas se encontram abertos e ativos em seu computador, o tempo necessário para que seu algoritmo resolva a solução será mais lento do que se seu computador dedicasse todos os seus recursos para executar o algoritmo.
  • O compilador e as bibliotecas que usamos em nosso algoritmo também afetam sua duração de tempo de execução.

A notação Big-O

  • O(1)
  • O(n)
  • O(n²)
  • O(log n)

O(1) — Complexidade de tempo de execução constante

def array_elements(array):
print(array[0])
def array_elements(array):
third_index = array[2]
return third_index

O(n) — Complexidade de tempo de execução linear

def array_element(array):
for i in range (len(array)):
print(array[i])

O(n²) — Complexidade de tempo de execução quadrática

def array_element(array):
sum_number = 0
for i in range (len(array)):
for j in range (len(array)):
sum_number += array[i]+array[j]
return sum_number

O(log n) — Complexidade logarítmica de tempo de execução

def binary_search(array, value):    first_val = 0
last_val = len(array)-1
bool_stat = False
while (first_val <= last_val and bool_stat == False): mid_val = (first_val + last_val) // 2 if array[mid_val] == value:
bool_stat = True
return bool_stat
elif value > array[mid_val]:
first_val = mid_val + 1
else:
last_val = mid_val - 1
return bool_statarray = [1,2,3,4,5,6,7,8,9]
value = 7
is_found = binary_search(array, value)

Comparações de complexidade entre diferentes notações Big-O

**O(1) < O(log n) < O(n) < O(n^2)**

Determinando a notação Big-O de um código

Melhor cenário e pior cenário

def linear_search(array, value):    for i in range (len(array)):        if array[i] == value:           return True    return Falsedef binary_search(array, value):    first_val = 0
last_val = len(array)-1
bool_stat = False
while (first_val <= last_val and bool_stat == False): mid_val = (first_val + last_val) // 2 if array[mid_val] == value:
bool_stat = True
return bool_stat
elif value > array[mid_val]:
first_val = mid_val + 1
else:
last_val = mid_val - 1
return bool_statarray = [1,2,3,4,5,6,7,8,9]
value = 7
  • O melhor caso para pesquisa linear seria se quisermos pesquisar o valor de 1, que é o primeiro elemento da matriz. Neste caso, temos complexidade O(1) .
  • O pior caso para pesquisa linear seria se quisermos pesquisar o valor de 9, que é o último elemento do array, ou se quisermos pesquisar um valor que não esteja incluído no array. Isso ocorre porque o algoritmo precisa percorrer cada elemento da matriz. Neste caso, temos complexidade O(n) .
  • O melhor caso para pesquisa binária seria se quiséssemos pesquisar o valor de 5, que é o valor no elemento do meio da matriz. Neste caso, temos complexidade O(1) .
  • O pior caso para a busca binária seria se quiséssemos buscar o valor de 1 ou 10, que são o primeiro e o último elemento do array, ou o valor que não está incluído no array. Isso ocorre porque com a busca binária, o algoritmo precisa dividir os dados ao meio recursivamente até atingir o primeiro e o último elemento. Neste caso, temos a complexidade de O(log n).

Estimando a notação Big-O de um código

def max_value(array):    value = 0  # O(1) complexity    for i in range (len(array)): # O(n) complexity        for j in range (len(array)-10): # O(10) complexity            for k in range (len(array)//2): # O(n/2) complexity                value += array[i] + array[j] + array[k] # O(1) complexity    return value # O(1) complexity
  1. A partir da operação no loop mais interno, que é value += array[i] + array[j] + array[k]. Esta operação tem uma complexidade O(1) constante.
  2. Em seguida, olhamos para o loop mais interno, que é for k in range (len(array)/2). Esse loop sempre iterará mais da metade do tamanho do nosso array, portanto, tem complexidade O(n/2) .
  3. Em seguida, subimos um nível, que é o loop for j in range (len(array)-10). Embora este seja um loop for , mas tem uma complexidade de tempo de execução constante. Isso ocorre porque não importa quão grande seja o tamanho da matriz de entrada, esse loop sempre iterará apenas nos últimos 10 elementos. Portanto, tem uma complexidade O(10) constante.
  4. Em seguida, passamos para o loop externo, que é for i in range (len(array)). Este loop for sempre iterará sobre o tamanho da matriz de entrada. Portanto, esse loop tem complexidade O(n) .
  5. Finalmente, passamos para as operações fora do loop, que são value = 0e return value. Essas duas operações sempre serão executadas em um tempo de execução constante, portanto, ambas têm complexidade O(1) .
  1. O loop mais interno tem complexidade O(n/2) e a operação neste loop tem complexidade O(1) . Isso significa que esse loop mais interno tem complexidade.(n/2)*(1) = **O(n/2)**
  2. Em seguida, o segundo loop interno tem complexidade O(10) . O loop interno deste loop, como computamos no ponto nº 1, tem O(n/2) . Isso significa que o segundo loop interno tem complexidade.10*(n/2) = **O(5n)**
  3. Finalmente, o loop externo tem complexidade O(n) . O loop interno deste loop externo tem uma complexidade total de O(5n) , conforme computamos no ponto no.2. Então, no total, eles têm complexidade.n*5n = **O(5n^2)**
  4. Se combinarmos a complexidade do loop e as duas operações fora do loop, obtemos . Como você pode ver, o código acima no total tem complexidade O(2+5n²) .1+1+5n^2 = **O(2+5n^2)**

Créditos

--

--

☕🇳🇿 - https://eduardorabelo.me

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store