Você está na página 1de 13

Análise de Algoritmos:

Melhor caso, pior caso, caso médio

Fernando Lobo

Algoritmos e Estrutura de Dados II

1 / 25

Sumário

I Rever um problema e um algoritmo que já conhecem.

I Descrevê-lo em pseudo-código (como no livro).

I Começar a usar a notação assimptótica para descrever o


tempo de execução de algoritmos.

2 / 25
O problema de ordenação

I Input: Uma sequência de n números ha1 , a2 , . . . , an i

I Output: Uma permutação ha10 , a20 , . . . , an0 i da sequência de


input tal que:
a10 ≤ a20 ≤ . . . ≤ an0

I Sequência é tipicamente guardada num array.

I Exemplo:
I Input: h8, 2, 4, 9, 3, 6i
I Output: h2, 3, 4, 6, 8, 9i

3 / 25

Insertion Sort

I Bom para ordenar um número pequeno de elementos.

I Funciona de modo análogo ao modo como ordenamos uma


“mão”de cartas:
I Metemos as cartas na mão sempre ordenadas.
I Ao recolhermos mais uma carta, inserimo-la na posição
correcta (para que as cartas na mão continuem ordenadas).

4 / 25
Pseudocódigo
(convenção: arrays começam na posição 1)

Insertion-Sort(A)
1 for j = 2 to length[A]
2 key = A[j]
3 // Insere A[j] na sequência ordenada A[1 . . j − 1]
4 i = j −1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i −1
8 A[i + 1] = key

5 / 25

Exemplo

8 2 4 9 3 6 // input
2 8 4 9 3 6 // fim da 1a iteraç~
ao
2 4 8 9 3 6 // fim da 2a iteraç~
ao
2 4 8 9 3 6 // fim da 3a iteraç~
ao
2 3 4 8 9 6 // fim da 4a iteraç~
ao
2 3 4 6 8 9 // fim da 5a iteraç~
ao

6 / 25
Correcção do algoritmo

I Invariante: No começo de cada iteração do ciclo for, o


subarray A[1 . . j − 1] está ordenado.

I Usamos o invariante para provar que o algoritmo funciona de


modo correcto.

7 / 25

Correcção do algoritmo (cont.)

Temos de provar 3 coisas:

1. Inicialização: Invariante é verdadeiro antes da 1a iteração.

2. Manutenção: Se é verdadeiro no inı́cio de uma iteração,


mantém-se verdadeiro no inı́cio da iteração seguinte.

3. Término: Quando o ciclo termina, o array A[1 . . n] está


ordenado.

8 / 25
Invariantes

A utilização de invariantes em ciclos é análogo ao método de


indução matemática:

I Inicialização → base de indução.

I Manutenção → passo indutivo.

I Término: → não existe no método de indução matemática (é


infinito). Aqui a “indução”pára quando o ciclo termina.

9 / 25

Como analisar o tempo de execução do algoritmo?

O tempo depende do input:

I Ordenar 10000 números demora mais do que ordenar 30.

I Pode demorar tempos diferentes em inputs do mesmo


tamanho (ex: Insertion-Sort é mais rápido se o input já
estiver ordenado.)

10 / 25
Como analisar o tempo de execução do algoritmo?

O tamanho do input depende do problema:

I Normalmente é o no de elementos do input (n no caso de


ordenação).

I Mas pode ser outras coisas:


I Em algoritmos de grafos, o tamanho é geralmente expresso em
termos de 2 quantidades: no de nós e no de arcos (veremos
isso mais tarde).

11 / 25

Tempo de execução

O tempo de execução num determinado input, é o número de


operações primitivas executadas.

I Operações primitivas: afectações, comparações, etc.

I Cada linha do pseudocódigo requer tempo constante


(independente do tamanho do input).

I Linhas diferentes podem requerer tempos diferentes.

I Chamadas de funções também têm tempo constante, mas a


execução da função poderá não ter.

12 / 25
Análise de Insertion-Sort

I Para j = 2, 3, . . , n, seja tj o no de vezes que o ciclo while é


executado para esse valor de j.

I (NOTA: os ciclos for e while são testados mais uma vez que
o corpo do ciclo.)

I Tempo de execução depende dos tj 0 s . Isto é, variam


consoante o input.

13 / 25

Pseudocódigo (outra vez)

Insertion-Sort(A)
1 for j = 2 to length[A]
2 key = A[j]
3 // Insere A[j] na sequência ordenada A[1 . . j − 1]
4 i = j −1
5 while i > 0 and A[i] > key
6 A[i + 1] = A[i]
7 i = i −1
8 A[i + 1] = key

14 / 25
Número de vezes que cada linha é executada

I linha 1: n
I linha 2: n − 1
I linha 3: n − 1
I linha 4: n − 1
I linha 5: t2 + t3 + . . . + tn
I linha 6: (t2 − 1) + (t3 − 1) + . . . + (tn − 1)
I linha 7: (t2 − 1) + (t3 − 1) + . . . + (tn − 1)
I linha 8: n − 1

15 / 25

Tempo de execução do algoritmo

(custo da linha i) × (no de vezes que a linha i é exec.)


P
I
i

I Seja ci o custo da linha i.

I Seja T (n) o tempo de execução de Insertion-Sort.

T (n) = c1 n + c2 (n − 1) + c3 (n − 1) + c4 (n − 1)
Xn n
X X n
+ c5 tj + c6 (tj − 1) + c7 (tj − 1) + c8 (n − 1)
j=2 j=2 j=2

16 / 25
Melhor caso

I O array já está ordenado. Isto é,

I A[i] ≤ key no teste do ciclo while =⇒ todos os tj 0 s = 1

T (n) = c1 n + c2 (n − 1) + c4 (n − 1) + c5 (n − 1) + c8 (n − 1)
= (c1 + c2 + c4 + c5 + c8 )n − (c2 + c4 + c5 + c8 )

I T (n) = an + b, em que a e b são constantes.

I =⇒ T (n) é uma função linear de n.

17 / 25

Pior caso
I O array está ordenado por ordem inversa.

I A[i] > key em todas as iterações do ciclo while.

I Temos de comparar key com todos os elementos à esquerda


da posição j =⇒ j − 1 comparações.

I Como o ciclo while termina quando i = 0, há um teste


adicional depois das j − 1 comparações =⇒ tj = j
n
X n
X
tj = j
j=2 j=2
n
X n
X
(tj − 1) = (j − 1)
j=2 j=2
(estes somatórios são somas de termos de progressões
aritméticas)
18 / 25
Pior caso (cont.)

(2 + n)(n − 1)
T (n) = c1 n + c2 (n − 1) + c4 (n − 1) + c5
2
n(n − 1) n(n − 1)
+ c6 + c7 + c8 (n − 1)
c 2 2
5 c6 c7  2
= + + n
2 2 2
 c5 c6 c7 
+ c1 + c2 + c4 + − − + c8 n
 2  2 2
− c2 + c4 + c5 + c8

I T (n) = an2 + bn + c, em que a, b e c são constantes.

I =⇒ T (n) é uma função quadrática de n.

19 / 25

Caso médio

I No geral é difı́cil (ou mesmo impossı́vel) de calcular.

I Temos de assumir certas coisas. Por exemplo, todas as


sequências de input são igualmente prováveis (o que nem
sempre é verdade).

20 / 25
Caso médio (cont.)

I Imaginemos que o input para Insertion-Sort é um array de


n números aleatórios gerados a partir de uma distribuição
uniforme.

I Em média, key em A[j] é menor que metade dos elementos


em A[1 . . j − 1] e é maior que a outra metade.

I =⇒ em média o ciclo while tem de olhar até metade do


subarray A[1 . . j − 1] =⇒ tj = j/2

I O tempo de execução é aprox metade do tempo do pior caso.


Mas continua a ser uma função quadrática de n.

21 / 25

Casos: Melhor, pior e médio

I Análise do melhor caso normalmente não nos interessa.

I Porquê? Porque é fácil fazer batota.

I =⇒ Podemos ter um algoritmo de ordenação muito mau que


é linear no melhor caso e exponencial no pior caso (e também
no caso médio).

I Que algoritmo é esse?

22 / 25
Casos: Melhor, pior e médio

I Normalmente estamos interessados na análise do pior caso: o


tempo de execução mais longo de qualquer input de tamanho
n.

I Porquê?
I Dá-nos um limite superior do tempo de execução, qualquer
que seja o input.
I O caso médio costuma ser quase tão mau como o pior caso e é
normalmente mais difı́cil de analisar.
I Nalguns algoritmos, o pior caso ocorre com muita frequência
(ex: pesquisa de um elemento que não está presente na
colecção.)

23 / 25

Ordem de crescimento
I Simplificamos para nos concentrar no essencial.
I eliminamos termos de ordem inferior.
I ignoramos o coeficiente do termo de ordem superior.

I Exemplo: O tempo de execução de Insertion-Sort no pior


caso é dado por an2 + bn + c
I eliminando termos de ordem inferior =⇒ an2
I ignorando o coeficiente =⇒ n2

I IMPORTANTE: Mas não podemos dizer que T (n) = n2

I =⇒ o que podemos dizer é que T (n) “cresce”de modo


proporcional a n2 .

24 / 25
Ordem de crescimento (cont.)

I Dizemos que T (n) = Θ(n2 ) para capturar a noção de que a


ordem de crescimento é proporcional a n2 .

I Chama-se a isto análise assimptótica.

I Normalmente dizemos que um algoritmo é mais eficiente que


outro, se o seu tempo de execução no pior caso tiver uma
menor ordem de crescimento (i.e. menor complexidade
assimptótica).

25 / 25

Você também pode gostar