Esta é realmente uma questão profunda que tem algumas respostas metódicas e pragmáticas. Suponho que você queira saber algo sobre o (s) algoritmo (s) em questão. Se você quiser saber qual algoritmo funciona melhor em uma determinada máquina em determinadas entradas, vá em frente e meça os tempos de execução. Se você deseja comparar a qualidade de um compilador para um determinado algoritmo, vá em frente e meça os tempos de execução. Para aprender algo sobre o algoritmo, não faça isso.
Deixe-me primeiro dar algumas razões pelas quais o uso de tempos de execução não é uma boa ideia.
- Generalidade Os
tempos de execução medidos usando um idioma e um compilador em uma máquina têm pouco significado se você alterar qualquer componente. Mesmo implementações ligeiramente diferentes do mesmo algoritmo podem ter um desempenho diferente, porque você aciona alguma otimização do compilador no caso, mas não no outro.
- Previsão
Então você tem alguns tempos de execução para algumas entradas. O que isso diz sobre o tempo de execução de alguma outra entrada? Em geral, nada.
- Significado
Normalmente, você não fará o benchmark de todas as entradas (de algum tamanho), de modo que restrinja imediatamente sua capacidade de comparar algoritmos: talvez seu conjunto de testes tenha acionado o pior caso em um e o melhor caso no outro algoritmo? Ou talvez suas entradas sejam muito pequenas para exibir o comportamento do tempo de execução .
- Medição
Medir bem os tempos de execução não é trivial. Existe um JIT? Houve contenda, ou seja, você está contando o tempo em que o algoritmo nem sequer foi executado? Você pode reproduzir exatamente o mesmo estado de máquina para outra execução (do outro algoritmo), em particular processos e caches simultâneos? Como é tratada a latência da memória?
Espero que eles o tenham convencido de que os tempos de execução são uma medida horrível para comparar algoritmos e que é necessário algum método abstrato e geral para investigar o tempo de execução do algoritmo.
Para a segunda parte da pergunta. Por que usamos comparações ou operações elementares semelhantes?
Rastreabilidade analítica
Supondo que você queira fazer uma análise formal, você deve ser capaz de fazê-lo. Contar declarações individuais é muito técnico, às vezes até difícil; algumas pessoas fazem isso mesmo assim (por exemplo, Knuth). Contar apenas algumas instruções - aquelas que dominam o tempo de execução - é mais fácil. Pelo mesmo motivo, geralmente "apenas" investigamos (limites superiores no) pior tempo de execução.
Dominância
A operação selecionada domina o tempo de execução. Isso não significa que ele contribui com mais tempo de execução - as comparações claramente não, por exemplo, no Quicksort ao classificar números inteiros do tamanho de palavras. Mas eles são executados com mais frequência ; portanto, contando-os, você conta com que frequência as partes mais executadas do algoritmo são executadas. Consequentemente, seu tempo de execução assintótico é proporcional ao número de operações elementares dominantes. É por isso que nos sentimos à vontade usando a notação Landau e a palavra "tempo de execução", mesmo contando apenas comparações.
Observe que pode ser útil contar mais de uma operação. Por exemplo, algumas variantes do Quicksort fazem mais comparações, mas menos trocas do que outras (em média).
Pelo que vale a pena, depois de ter feito toda a teoria, convém revisitar os tempos de execução para verificar se as previsões que sua teoria faz são sólidas. Caso contrário, sua teoria não é útil (na prática) e precisa ser ampliada. A hierarquia de memória é uma das primeiras coisas que você percebe ser importante, mas está ausente nas análises básicas.