slides

Propaganda
Algumas Noções de Complexidade
Luís Lopes
DCC-FCUP
Estruturas de Dados
Algoritmo
I
um método para resolver um problema
I
especifica, passo a passo, as operações a realizar
I
as funções que manipulam as estruturas de dados que
definimos são também algoritmos
I
e.g., inserir elemento no fim de uma lista, encontrar o índice
de um elemento na lista, remover um elemento no início da
lista
I
outros algoritmos operam sobre estruturas de dados completas
I
e.g., ordenar um array, multiplicar matrizes, inverter uma lista
Eficiência de um Algoritmo
I
normalmente mede-se com base em 2 critérios em função do
tamanho do input n
I
taxa de crescimento do tempo de execução (complexidade
temporal)
I
taxa de crescimento do espaço usado na memória
(complexidade espacial)
I
vamos centrar a nossa atenção na primeira métrica
Análise da Eficiência
I
descrição matemática do algoritmo em vez de implementação
I
caracteriza o tempo de execução como uma função do
tamanho do input
I
tem em conta todos os possíveis inputs
I
permite avaliar eficiência de forma independente do ambiente
de hardware/software
Exemplo
Estimativa do tempo de execução em função de n
i n t findMax ( i n t a [] , i n t n ) {
i n t max = a [0];
// 2 operações
int i
= 1;
// 1 operação
w h i l e ( i <= n - 1) { // n operações
// n-1 vezes ciclo
i f ( a [ i ] > max )
// 2 operações
max = a [ i ];
// 2 operações
i = i + 1;
// 2 operações
}
r e t u r n max ;
// 1 operação
}
Pressupostos
I
contabilizar número de instruções primitivas
I
memória de acesso aleatório e ilimitada
e.g., max = a[0] é composta por 2 operações primitivas
I
I
1 leitura de a[0] + 1 atribuição a max
Exemplo (cont.)
I
caso mais favorável (a[0] é o maior elemento):
T (n) = 2 + 1 + n + 4(n − 1) + 1 = 5n operações primitivas
I
pior caso:
T (n) = 2 + 1 + n + 6(n − 1) + 1 = 7n − 2
I
caso médio depende da distribuição do input
I
o pior caso dá-nos um limite superior do tempo de execução
I
neste caso T (n) ∝ n
Taxa de Crescimento de T (n)
I
o tempo de execução, T (n), pode ser afectado pela alteração
do ambiente hardware/software
I
tal não acontece se considerarmos a taxa de crescimento de
T (n): a variação de T (n) quando se aumenta o valor de n.
I
para a função findMax(), T (n) é limitado por 2 funções
lineares em n
I
diz-se que o crescimento de T (n) é linear
Taxas de crescimento
logarítmica
linear
n-logarítmica
quadrática
cúbica
exponencial
log n
n
n log n
n2
n3
an (a > 1)
Crescimento de
n log2 n
2
1
8
3
16
4
...
...
1024
10
algumas funções:
√
n
n n log2 n
1.4
2
2
2.8
8
24
4.0
16
64
...
...
...
32 1024
10240
n2
4
64
256
...
> 106
n3
8
512
4096
...
> 109
2n
4
256
65536
...
308
> 10
Taxas de crescimento
Se assumirmos que cada operação pode ser executada em 1µs
(micro-sec), qual será o maior problema (função de n) para um
programa que execute em 1 seg., 1 min., 1 hora?
T (n)
400n
20ndlog ne
2n2
n4
2n
Tamanho máximo problema (n)
1 seg.
1 min.
1 hora
2500 150,000
9,000,000
4096 166,666
7,826,087
707
5,477
42,426
31
88
244
19
25
31
No caso de crescimento exponencial, apenas conseguimos tratar
problemas para dimensões muito pequenas de n.
Ordem de Complexidade Assimptótica: O()
Sejam f (n) e g(n) funções de N0 7−→ R.
f (n) é O(g(n)) (ou seja, f (n) é da ordem de g(n))
se ∃c > 0, ∃n0 ≥ 1 : f (n) ≤ cg(n), ∀n ≥ n0
Interpretação gráfica:
I
O(g(n)) corresponde ao limite
superior da taxa de crescimento de
f (n).
I
dizer que f (n) é O(g(n)), significa
dizer que f (n) não cresce mais do que
g(n).
Exemplo
I
2n + 10 é O(n) porque:
2n+10 ≤ cn
⇔
(c−2)n ≥ 10
⇔
n≥
10
(c − 2)
para c = 3 e n0 = 10 verifica-se sempre que 2n + 10 ≤ cn
I
outras funções:
20n3 + 10n log n + 5
ak nk + ak−1 nk−1 + . . . + a0
3 log n + log log n
2100
5
n
—
—
—
—
—
O(n3 )
O(nk )
O(log n)
O(1)
O( n1 )
Algumas Propriedades Úteis
I
Se h(n) ∼ O(f (n)), então αh(n) (com α > 0) ∼ O(f (n))
I
Se h1 (n) ∼ O(f (n)) e h2 (n) ∼ O(g(n)), então h1 (n) + h2 (n) ∼
O(f (n) + g(n))
I
Se h1 (n) ∼ O(f (n)) e h2 (n) ∼ O(g(n)), então h1 (n)h2 (n) ∼
O(f (n)g(n))
I
Se h(n) ∼ O(f (n)) e f (n) ∼ O(g(n)), então h(n) ∼ O(g(n))
Exemplo: Método da Bolha (Bubble-Sort)
v o i d bubbleSort ( i n t a [] , i n t n ) {
i n t i , j , tmp ;
f o r ( i n t i = 0; i < n ; i ++)
// n iterações
f o r ( i n t j = 0; j < n -1 - i ; j ++) // n-i iterações
i f ( a [ j ] > a [ j +1]) {
tmp
= a [ j +1];
a [ j +1] = a [ j ];
a[j]
= tmp ;
}
}
Efectivamente, o número de iterações total é a soma das iterações
que o ciclo j faz para cada valor de i, i.e.
T (n) ∼
Pn−1
i=1 (n
− i)
= (n − 1) + (n − 2) + . . . + 2 + 1 =
2
= n(n−1)
= n2 − n2
2
⇒ T (n) é O(n2 )
Exemplo: Factorial
i n t factorial ( i n t n ) {
i f ( n == 0 || n == 1)
r e t u r n 1;
else
r e t u r n n * factorial (n -1);
}
∴ T (n) =
// O(1)
// O(1)
// O(1) + T(n-1)

α + T (n − 1),
se n > 1
β,
se n ≤ 1
assumindo n ≥ 2:
T (n − 1) = α + T (n − 2) ⇒ T (n) = 2α + T (n − 2)
em geral: T (n) = iα + T (n − i), n > i
se i = n − 1, então T (n) = α(n − 1) + T (1) = α(n − 1) + β
donde podemos concluir que T (n) é O(n)
Exemplo: Pesquisa Binária
i n t binarySearch ( i n t [] values , i n t val , i n t low , i n t high ) {
i f ( high < low )
r e t u r n -1;
else {
i n t half = low + ( high - low ) / 2;
i f ( val == values [ half ] ) )
r e t u r n half ;
e l s e i f ( val < values [ half ] ) )
r e t u r n binarySearch ( values , val , low , half - 1);
else
r e t u r n binarySearch ( values , val , half + 1 , high );
}
}
I
em cada passo do ciclo, o intervalo reduz-se a metade.
I
para um n dado, o algoritmo pára quando atingir um intervalo
de tamanho ≤ 1
Exemplo: Pesquisa Binária
I
qual dos valores n/2, n/4, n/8, . . . , n/2k . . . será o primeiro a
ser ≤ 1?
I
temos de resolver n/2k ≤ 1, o que dá um número de passos
k ≥ log2 n
I
tomando o mais pequeno inteiro k que satisfaz k ≥ log2 n
(k = dlog2 ne) sabemos que ao fim de um máximo de k
iterações encontramos o valor no array ou podemos concluir
que o valor que procuramos não existe
I
deduzimos assim que T (n) = dlog2 ne ou seja T (n) é O(log n)
Download