O primeiro algoritmo que analisaremos será o INSERTION SORT (), cujo código está descrito abaixo em C#. Eu recomendo fortemente que você o implemente para se familiarizar com seu funcionamento e caso tenha dúvidas recorra aos vídeos sugeridos que são altamente didáticos e estão disponíveis na seção de links deste post.
Tempo de Execução
O tempo de execução deste algoritmo depende de alguns fatores, dentre eles:
- A entrada em sí: Se a entrada já se encontra ordenada o insertion sort tem muito pouco trabalho a fazer, já que não precisa executar o looping interno nenhuma vez. Perceba que o pior caso para este algoritmo é quando o vetor de entrada se encontra reversamente ordenado, onde para todas as iterações do looping externo ele executa outras
iterações.
- Tamanho da entrada: Leva-se mais tempo para ordenar 1000 elementos do que apenas 6. Portanto fala-se to tempo de execução como uma função do número de elementos na entrada.
Quando estamos analisando o tempo de execução nós geralmente queremos o tempo máximo, ou o limite superior deste tempo, pois desta maneira temos uma garantia de que o mesmo não excederá um dado limite. Se é dito que o limite inferior de um algoritmo é de 3 segundos não podemos deduzir que seu tempo máximo será 3 minutos ou 3 anos, não fornecendo uma informação relevante ao usuário. Desta forma temos alguns tipos de análise:
- Pior caso (geralmente): Tempo máximo para qualquer entrada de tamanho
.
- Caso médio (algumas vezes): Tempo esperado para todas as entradas de tamanho
. Para esta análise é necessária uma premissa a respeito da distribuição estatística das entradas. Uma premissa comum é de que todas as entradas são igualmente prováveis (ou distribuição uniforme).
- Melhor caso (nunca): Algum algoritmo pior pode trabalhar mais rápido para uma entrada específica, e portanto este tipo de análise não provê garantia alguma a respeito de seu tempo de execução.
Então qual o tempo de execução de pior caso do insertion sort? Poderíamos inicialmente responder de que depende da máquina, mas na verdade estamos interessados em saber um tempo de execução relativo, independente de hardware, pois é claro que se estivermos executando o mesmo algoritmo em uma máquina mais eficiente teremos um menor tempo. Utilizamos a ideia da análise assintótica para ignorar constantes relativas a máquina e olhar então apenas para o crescimento do tempo de execução a medida que o número de entradas se aproxima do infinito ().
Análise Assintótica
Notação-Θ: Descarta-se termos de menor ordem e ignora-se constantes. Veja um exemplo abaixo.
Podemos dizer que um algoritmo sempre ultrapassa, ou sempre é mais performático que um
, mesmo executando o último em um computador mais poderoso. A notação assintótica portanto resolve nosso problema em sermos capazes de comparar velocidades relativas e absolutas, independentemente de computador ou plataforma específicos. Embora hajam diferenças nas constantes específicas de cada máquina podemos afirmar que sempre haverá um ponto onde a função
passa a executar mais rapidamente que
. Porém algumas vezes este ponto pode ser tão grande que mesmo uma função assintoticamente pior pode ser mais vantajosa quando lidamos com um universo entradas menor que este ponto. O gráfico abaixo ilustra esta idéia.

Análise do Insertion Sort
Após termos introduzido o conceito de análise assintótica podemos finalmente obter o tempo de execução do insertion sort. Para isso, nós consideramos que cada operação elemental custa uma quantia constante de tempo, onde não precisamos nos importar com seu valor já que analisaremos assintoticamente. Na listagem abaixo apresentamos novamente o insertion sort porém em cada linha são apresentados mais dois valores, um que representa seu custo computacional e outro que representa o número de execuções desta linha. Para este exemplo e
o número de vezes que o loop interno
é executado para cada valor de
.
O tempo de execução de um algoritmo é a soma dos tempos de execução de cada linha. Se uma linha custa para ser executada e é executada
vezes, então ela contribuirá com
para o tempo total. Para o insertion sort temos:
Para concluirmos nossa análise precisamos decidir a respeito de . Imagine que fossemos analisar o melhor caso, então fica claro que o loop interno nunca seria executado, pois sua condição nunca seria verdadeira (os números já estão ordenados,
nunca seria maior que
), nesta ocasião poderíamos substituir o primeiro somatório acima por
, que seria apenas o custo da sua verificação para cada iteracao de zero a
, o segundo e terceiro somatórios seriam excluídos da nossa fórmula já que seriam instruções que nunca seriam executadas. No entanto estamos interessados no pior caso, onde para cada chave no índice
o algoritmo percorre todo o array até o primeiro índice, ou seja, o número de iterações do loop interno é o próprio valor de
, portanto para o pior caso
.
Desta maneira nossos somatórios acima se transformam respectivamente em (haverá um post apenas sobre o background matemático dessas conclusões):
Substituindo temos
Aplicando a distributiva e colocando em evidência os fatores e
temos:
Que é uma função quadratica da forma
Analisando assintoticamente (descartando os termos de menor ordem e desconsiderando constantes) chegamos a conclusão que o tempo de execução, ou a ordem de crescimento do insertion sort é .
Consideramos que um algoritmo é mais eficiente quando seu tempo de pior caso tem uma ordem de crescimento menor do que outro. Devido a fatores relacionados as constantes e termos de menor ordem esta conclusão pode estar errada para pequenas entradas, mas não para entradas grandes o suficiente.

