Cover do episódio 37: Algoritmos e por que complexidade não era abstrata para mim
#0379 de outubro, 20173 min leituraCódigo na PráticaS2 · 2017

Algoritmos e por que complexidade não era abstrata para mim

A matéria de Algoritmos ensina O(n), O(n²), O(log n). Para a maioria dos alunos é teoria. Para mim, que tinha visto script lento travar servidor, era vocabulário para algo que eu já tinha sentido.

AlgoritmosPerformanceFundamentosUniversidade

Na Oi, tinha um script de inventário que eu rodava mensalmente.

Ele percorria uma lista de máquinas, se conectava a cada uma via SSH, coletava informações e gravava em arquivo. Simples. O problema é que a lista tinha crescido para 400 máquinas e o script levava 6 horas para terminar.

Não sabia o nome do problema. Mas percebia o padrão: mais máquinas, mais tempo de forma linear. E com algumas adições — quando comecei a checar dependências entre máquinas — o tempo explodia de forma que não parecia mais linear.

Quando aprendi notação Big O na faculdade, a primeira coisa que pensei foi: aquele script era O(n²).


A maioria dos meus colegas tratava complexidade de algoritmos como abstração matemática.

E faz sentido — se você nunca teve que esperar 6 horas por um script que rodava em segundos quando a entrada era pequena, "tempo de execução cresce quadraticamente com o tamanho da entrada" é uma frase sem referência concreta.

Para mim, tinha referência. O problema que eu tinha resolvido no passado por instinto — quebrar em batches, paralelizar, reduzir operações dentro do loop — tinha nome. E o nome me dava ferramenta para pensar sobre o problema antes de escrever código.


O que a matéria de Algoritmos deu:

Vocabulário para raciocinar antes de implementar. Antes de escrever qualquer código, perguntar: qual é a entrada? Como o tempo cresce com a entrada? Existe um algoritmo melhor para esse tipo de problema?

Categorias de problema. Busca em lista não ordenada: O(n). Busca em lista ordenada com busca binária: O(log n). Ordenação com bubble sort: O(n²). Ordenação com merge sort: O(n log n). Essas categorias permitem comparar abordagens antes de implementar qualquer uma.

O valor da estrutura de dados certa. Por que lista ligada existe se array existe? Porque inserção no meio é O(1) em lista ligada e O(n) em array. Por que hash table existe? Porque busca é O(1) em média contra O(n) em lista. A estrutura de dados é a decisão que determina a complexidade das operações.


O exercício que mais me marcou: implementar busca binária do zero.

Parece simples: divida o array ao meio, compare com o elemento do meio, descarte metade, repita. A lógica é clara. A implementação tem armadilhas — off-by-one no índice, overflow de inteiro se você calcular meio como (low + high) / 2 com inteiros grandes, condição de parada errada que resulta em loop infinito.

Jon Bentley — o pesquisador que popularizou o algoritmo — escreveu em 1986 que a maioria das implementações de busca binária tinha bugs. Pesquisa de 2006 encontrou que o bug de overflow estava no Java até a versão 6.

Algoritmo correto é diferente de implementação correta. A matéria ensinou os dois.


O que ficou foi o hábito de pensar em escala antes de implementar.

Não como análise formal toda vez — mas como pergunta de fundo: se a entrada crescer dez vezes, o que acontece com o tempo? Se crescer cem vezes? Essa pergunta, feita antes de escrever código, evita refatoração cara depois.

Anos depois, em plataforma de alto volume, essa pergunta virou instinto. Quando alguém propunha uma query de banco que era O(n) por usuário em contexto de query que rodava por usuário ativo — eu via o problema antes de rodar o benchmark.

O vocabulário que a matéria deu transformou instinto em análise.

Essa semana: qual parte do seu código você nunca parou para pensar no que acontece quando a entrada cresce dez vezes?