• Nenhum resultado encontrado

Análise de melhor caso, pior caso e caso médio

Tempo de execução

4.1 Análise de melhor caso, pior caso e caso médio

Perceba que, na análise de tempo que fizemos para os algoritmos de busca linear e binária, mesmo considerando instâncias de um mesmo tamanho n, o tempo de execução dependia de qual instância era dada (x está ou não em A, está em qual posição de A).

Definição 4.2: Tempo de melhor caso

O tempo de execução de melhor caso de um algoritmo é o tempo de execução de uma instância que executa de forma mais rápida, dentre todas as instâncias possíveis de um dado tamanho n.

No caso da BuscaLinear, é necessário executar a inicialização da variável i, o teste do laço enquanto e o teste do comando condicional obrigatoriamente. Agora, se esse teste (A[i] == x) for verdadeiro, a busca termina sua execução com o comando de retorno. Esse certamente é o menor tempo de execução desse algoritmo. E note que isso acontece quando x = A[1]. Dizemos então que “o melhor caso de BuscaLinear ocorre quando x está na primeira posição do vetor”. Como o tempo de execução de BuscaLinear quando x está em Aé TBLE(n) = 5tpx (veja (4.1)), onde px é a posição de x em A, temos que, no melhor caso, o tempo de execução TBL(n)da busca linear é TBL(n) = 5t.

No caso da BuscaLinearEmOrdem, também é necessário executar a inicialização da variável i e os testes do laço enquanto obrigatoriamente. Acontece que esse teste pode falhar se x < A[1], fazendo o algoritmo retornar −1. Esse certamente é o menor tempo de execução desse algoritmo, que ocorre quando x não está em A e é menor do que todos os elementos já armazenados. Como o tempo de execução de BuscaLinearEmOrdem quando x não está em A é dado por TBON(n) = 8tax+ 6t, onde ax é a posição do maior elemento que é menor do que x, então no melhor caso o tempo de execução TBO(n) da busca linear em ordem é TBO(n) = 6t (ax = 0).

Já no caso da BuscaBinaria, a inicialização das variáveis, o primeiro teste do laço enquanto, o cálculo do valor meio e o teste se A[meio] == x devem ser obrigatoriamente realizados. Caso esse último teste dê verdadeiro, o algoritmo para, e esse é o mínimo de operações que ele irá realizar. Isso só ocorre se x estiver exatamente na posição da metade do vetor A, i.e., Ab(n − 1)/2c= x. Assim, como o tempo de execução de BuscaBinaria quando x está em A é dado por TBBE(n) = 11trx− t, onde rx é o número de vezes que o teste do laço é executado, temos que no melhor caso da busca binária o tempo de execução TBB(n)é TBB(n) = 10t.

que seja a instância de entrada recebida, pelo menos tal tempo será necessário. Assim, como o tempo de execução TBL(n)de BuscaLinear no melhor caso é TBL(n) = 5t, podemos dizer que o tempo de execução de BuscaLinear é TBL(n)≥ 5t. Perceba aqui a diferença no uso da igualdade e da desigualdade de acordo com as palavras que as precedem.

Geralmente, no entanto, estamos interessados no tempo de execução de pior caso. Definição 4.3: Tempo de pior caso

O tempo de execução de pior caso de um algoritmo é o maior tempo de execução do algoritmo dentre todas as instâncias de entrada possíveis de um dado tamanho n.

Perceba que o pior caso de BuscaLinear e de BuscaLinearEmOrdem ocorre quando o laço enquanto executa o máximo de vezes que puder executar. Para isso acontecer na BuscaLinear, o teste do condicional precisa falhar sempre, isto é, A[i] 6= x sempre, de forma que o laço termina apenas porque i ficou maior do que n (a condição do laço falhou). Isso acontece quando o elemento x a ser buscado não se encontra no vetor A. No caso da BuscaLinearEmOrdem, o teste do condicional também precisa falhar sempre, bem como o segundo teste de condição de parada do próprio laço precisa ser verdadeiro sempre. Assim, o laço termina apenas quando i > n, nos garantindo ainda que x é maior do que qualquer elemento armazenado em A, de forma que ax = n. Assim, o tempo de execução do pior caso da BuscaLinear é TBL(n) = 5tn + 3te o tempo de execução do pior caso da BuscaLinearEmOrdem é TBO(n) = 8tn + 6t.

No caso de BuscaBinaria, o pior caso também ocorre quando o laço enquanto executa o máximo de vezes que puder e o maior número de linhas internas ao corpo do laço são executadas a cada iteração. Isso significa que os dois primeiros testes do corpo do laço falham sempre, o que significa que x < A[meio] sempre e a variável dir vai ser atualizada em toda iteração. Com isso, o vetor vai ser subdividido na metade a cada iteração, até que esq > dir e o laço termine. Isso significa que o elemento x também não está no vetor A e que o laço executou log n + 1 vezes. Com isso, o tempo de pior caso da busca binária é TBB(n) = 11t log n + 4t.

A análise de pior caso é muito importante pois limita superiormente o tempo de execução para qualquer instância, garantindo que o algoritmo nunca vai demorar mais do que esse limite. Por exemplo, com as análises feitas acima, podemos dizer que o tempo de execução TBL(n)de BuscaLinear no pior caso é TBL(n) = 5tn + 3t, o que nos permite dizer que o tempo de execução de BuscaLinear é TBL(n)≤ 5tn + 3t. Outra razão para a análise de pior caso ser considerada é que, para alguns algoritmos, o pior caso (ou algum caso próximo do pior) ocorre com muita frequência.

O tempo de execução no melhor e no pior caso são limitantes para o tempo de execução do algoritmo sobre qualquer instância. Em geral, eles são calculados pensando-se em instâncias específicas, que forçam o algoritmo a executar o mínimo e o máximo de comandos possíveis. No caso da BuscaLinear, sabemos das análises anteriores que qualquer que seja o vetor A e o elemento x dados ao algoritmo, seu tempo de execução TBL(n)será tal que 5t ≤ TBL(n)≤ 5tn + 3t. Veja que a diferença entre esses dois valores é da ordem de n, isto é, quando n = 2 o tempo de execução pode ser algo entre 5t e 13t, mas quando n = 50 o tempo pode ser qualquer coisa entre 5t e 253t. Assim, quanto maior o valor de n, mais distantes serão esses limitantes. Por isso, em alguns casos pode ser interessante ter uma previsão um pouco mais justa sobre o tempo de execução, podendo-se considerar o tempo no caso médio.

Definição 4.4: Tempo do caso médio

O tempo de execução do caso médio de um algoritmo é a média do tempo de execução dentre todas as instâncias de entrada possíveis de um dado tamanho n.

Por exemplo, para os algoritmos de busca, assuma por simplicidade que x está em A. Agora considere que quaisquer uma das n! permutações dos n elementos de A têm a mesma chance de ser passada como o vetor de entrada. Note que, nesse caso, cada número tem a mesma probabilidade de estar em quaisquer das n posições do vetor. Assim, em média, a posição px de x em A é dada por (1 + 2 + · · · + n)/n = (n + 1)/2. Logo, o tempo médio de execução da busca linear é dado por TBL(n) = 5tpx= (5tn + 5t)/2.

O tempo de execução de caso médio da busca binária envolve calcular a média de rx

dentre todas as ordenações possíveis do vetor, onde, lembre-se, rx é a quantidade de vezes que o teste do laço principal é executado. Calcular precisamente essa média não é difícil, mas vamos evitar essa tecnicalidade nesse momento, apenas mencionando que, no caso médio, o tempo de execução da busca binária é dado por d log n, para alguma constante d (um número que não é uma função de n).

Muitas vezes o tempo de execução no caso médio é quase tão ruim quanto no pior caso. No caso das buscas, vimos que a busca linear tem tempo de execução 5tn + 3t no pior caso, e (5tn+5t)/2no caso médio, sendo ambos uma expressão da forma an+b, para constantes a e b, isto é, funções lineares em n. Assim, ambos possuem tempo de execução linear no tamanho da entrada. Mas é necessário deixar claro que esse nem sempre é o caso. Por exemplo, seja n o tamanho de um vetor que desejamos ordenar. Existe um algoritmo de ordenação chamado Quicksort que tem tempo de execução de pior caso quadrático em n (i.e., da forma an2+ bn + c, para constantes a, b e c), mas em média o tempo gasto é da ordem de n log n, que é muito menor que uma função quadrática em n, conforme n cresce. Embora o tempo de

execução de pior caso do Quicksort seja pior do que de outros algoritmos de ordenação (e.g., Mergesort, Heapsort), ele é comumente utilizado, dado que seu pior caso raramente ocorre. Por fim, vale mencionar que nem sempre é simples descrever o que seria uma “entrada média” para um algoritmo, e análises de caso médio são geralmente mais complicadas do que análises de pior caso.

4.2 Bons algoritmos

Ao projetar algoritmos, estamos sempre em busca daqueles que são eficientes. Definição 4.5: Algoritmo eficiente

Um algoritmo é eficiente se seu tempo de execução no pior caso puder ser descrito por uma função polinomial no tamanho da entrada.

Mas e quando temos vários algoritmos eficientes que resolvem um mesmo problema, qual deles escolher? A resposta para essas perguntas é depende. Depende se temos informação sobre a entrada, sobre os dados que estão sendo passados, ou mesmo da aplicação em que usaremos esses algoritmos. Quando não se sabe nada, respondemos a pergunta escolhendo pelo algoritmo mais eficiente, que é o que tem melhor tempo de execução no pior caso.

Assim, por exemplo, dos três algoritmos que temos para resolver o problema da busca em vetor ordenado, o mais eficiente é a BuscaBinaria, que tem uma função logarítmica de tempo no pior caso, ao contrário da BuscaLinear e da BuscaLinearEmOrdem, cujo pior caso tem tempo linear no tamanho da entrada.

É importante ter em mente que outras informações devem ser levadas em consideração sempre que possível. Quando o tamanho da entrada é muito pequeno, por exemplo, um algoritmo cuja ordem de crescimento do tempo de pior caso é menor do que a de outro não necessariamente é a melhor escolha (quando n < 6, por exemplo, a BuscaLinear executa menos operações do que a BuscaBinaria).

5

Capítulo

Documentos relacionados