Algoritmos Avançados

Propaganda
CCAE
Centro de
Ciências
Aplicadas e Educação
UFPB - Campus IV - Litoral Norte
Algoritmos Avançados
Análise de Complexidade
COMPLEXIDADE DE ALGORITMOS
  Definição:
A Complexidade de um Algoritmo
consiste na quantidade de “trabalho” necessária
para a sua execução, expressa em função das
operações fundamentais, as quais variam de
acordo com o algoritmo, e em função do volume
de dados
COMPLEXIDADE DE ALGORITMOS
  Um
algoritmo serve para resolver um
determinado problema, e todos os problemas têm
sempre uma entrada de dados
  O tamanho dessa entrada (N) tem geralmente
efeito direto no tempo de resposta de um
algoritmo
  Dependendo do problema a ser resolvido, já
existem algoritmos prontos ou que podem ser
adaptados
  O
problema é: qual algoritmo escolher?
COMO COMPARAR DUAS SOLUÇÕES
PARA UM MESMO PROBLEMA ?
  Tomemos
duas soluções para localizar um
elemento em um vetor: LIN e BIN
  Temos dois computadores diferentes, A e B,
sendo A um Core 2 Duo e B um Celeron.
Tamanho (n)
Tempo de execução de
LIN em A
Tempo de execução de
BIN em B
16
8 ns
100.000 ns
64
32 ns
150.000 ns
256
128 ns
200.000 ns
1024
512 ns
250.000 ns
Qual é a melhor solução? LIN ou BIN ?
4
COMO COMPARAR DUAS SOLUÇÕES
PARA UM MESMO PROBLEMA ?
Tamanho (n)
Tempo de execução de
LIN em A
Tempo de execução de
BIN em B
16
8 ns
100.000 ns
64
32 ns
150.000 ns
256
128 ns
200.000 ns
1024
512 ns
250.000 ns
...
...
...
1.048.576
524.288 ns
500.000 ns
4.194.304
2.097.152 ns
550.000 ns
16.777.216
8.388.608 ns
600.000 ns
...
63,072 x 1012
...
31,536 x 1012 ns ou 1 ano
...
1.375.000 ns ou cerca de
1,4 segundos5
COMPLEXIDADE DE ALGORITMOS
  Pode-se
falar de dois tipos de complexidade de
algoritmos :
 
 
Complexidade Espacial: Quantidade de recursos
utilizados para resolver o problema;
Complexidade Temporal: Quantidade de tempo
utilizado.
 
  Em
Pode ser vista também como o número de instruções
necessárias para resolver um determinado problema;
ambos os casos, a complexidade é medida de
acordo com o tamanho dos dados de entrada (n)
  Estamos mais interessados em calcular a
Complexidade Temporal de um algoritmo!
COMPLEXIDADE DE ALGORITMOS
  Existem
três perspectivas para análise de
complexidade:
 
 
 
Melhor Caso
Caso Médio
Pior Caso
  Nas
três perspectivas, a função f(n) retorna a
complexidade de um algoritmo com entrada de
tamanho n
ANÁLISE DO MELHOR CASO
  Definido
pela letra grega Ω(Ômega)
  Exprime o menor tempo de execução de um
algoritmo para uma entrada de tamanho n
  É pouco usado, por ter aplicação em poucos
casos.
  Ex.:
 
 
O algoritmo de pesquisa sequêncial em um vetor tem
complexidade f(n) = Ω(1)
A análise assume que o número procurado seria o
primeiro selecionado na lista.
 
Abordagem otimista!
ANÁLISE DO CASO MÉDIO
  Definido
pela letra grega θ (Theta)
  Deve-se obter a média dos tempos de execução de
todas as entradas de tamanho n, ou baseado em
probabilidade de determinada condição ocorrer
  Ex.:
 
 
O algoritmo de pesquisa sequêncial em um vetor tem
complexidade f(n) = θ(n/2)
Em média será necessário visitar n/2 elementos do
vetor até encontrar o elemento procurado
Melhor aproximação
  Muito difícil de determinar na maioria dos casos
 
ANÁLISE DO PIOR CASO
  Representado
 
pela letra grega O
O maiúsculo. Trata-se da letra grega ômicron
maiúscula
  Baseia-se
no maior tempo de execução sobre
todas as entradas de tamanho n
  É o método mais fácil de se obter.
  Ex.:
 
 
O algoritmo de pesquisa sequêncial em um vetor tem
complexidade f(n) = O(n)
No pior caso será necessário visitar todos os n
elementos do vetor até encontrar o elemento
procurado
 
Abordagem pessimista!
A NOTAÇÃO O
  Tempo
(ou espaço) é contabilizado em número de
passos do algoritmo (unidade de
armazenamento)
  Análise do algoritmo determina uma função que
depende do tamanho da entrada n.
  10n3 + 4n -10
 
 
à medida que n aumenta, o termo cúbico começa a
dominar
A constante do termo cúbico tem relativamente a
mesma importância que a velocidade da CPU
11
A NOTAÇÃO O
  Desprezar
 
Número de passos 3n será aproximado para n
  Interesse
 
constantes aditivas ou multiplicativas
assintótico
termos de menor grau podem ser desprezados
n2 + n será aproximado para n2
  6n3 + 4n - 9 será aproximado para n3
 
12
CÁLCULO DA COMPLEXIDADE
  Foi
visto que, para calcular a complexidade de
um algoritmo, deve-se analisar o pior caso
  A análise deve ser feita de acordo com a tabela a
seguir
Número de Operações
Complexidade
f(n)
O(f(n))
c x f(n)
O(f(n))
f(n) + f(n)
O(f(n))
f(n) + g(n)
O(max(f(n),g(n))
f(n) x g(n)
O(f(n) x g(n))
CÁLCULO DA COMPLEXIDADE:
ALGORITMO ITERATIVO
void FloidWarshall(int dist[][]) {
int i;
int j;
int k;
for ( k = 0; k < n; k++ ) {
for ( i = 0; i < n; i++ ) {
for ( j = 0; j < n; j++ ) {
int a = dist[i][j];
int b = dist[j][k];
int c = dist[k][j];
dist[i][j] = min(a, b + c );
3
}
}
3
}
}
1+1+1+n*(n*(n*(1+1+1+3)))
3+n*n* n*6
3 + 6n
O(n )
int min(int a, int b) {
if(a < b)
return a;
return b;
}
1+
1+
1+
n*(
n*(
n*(
1+
1+
1+
3
)
)
)
1+
1+
1
CÁLCULO DA COMPLEXIDADE:
ALGORITMO RECURSIVO
  A
questão se complica um pouco quando se trata
de algoritmos recursivos
  Embora não haja um método único para esta
avaliação, a complexidade de um algoritmo
recursivo é definida em função de componentes
como:
 
 
 
Complexidade da base
Complexidade do núcleo
Profundidade da recursão
Número de vezes que o procedimento recursivo é invocado
  Depende do tamanho do problema e da taxa de redução do
tamanho do problema
  É justamente em sua determinação que reside o
problema!
 
15
CÁLCULO DA COMPLEXIDADE:
ALGORITMO RECURSIVO
int fatorial( int n
if( n == 0 )
return
} else {
return
}
}
) {
{
1; //Base
n * fatorial( n – 1 );
// Núcleo
  A
redução se dá de uma em uma unidade, de n até
chegar a 0
 
Logo, a profundidade da recursão é n
  Tanto
o núcleo quando a base executam apenas uma
operação
 
A base é executada uma única vez e o núcleo n - 1 vezes
  Logo,
o número de operações executadas é ((n – 1) *
1) + 1, resultando em uma complexidade O(n)
16
ORDENS DE ALGORITMOS
  Complexidade
Constante
  Complexidade Linear
  Complexidade Logarítmica
  Complexidade Log Linear
  Complexidade Quadrática
  Complexidade Cúbica
  Complexidade Exponencial
  Complexidade Fatorial
COMPLEXIDADE CONSTANTE - O(1)
  São
os algoritmos onde a complexidade
independe do tamanho n de entradas
  É o único em que as instruções dos algoritmos
são executadas um número fixo de vezes
if (condição == true) then {
realiza alguma operação em tempo constante
}
else
{
realiza alguma operação em tempo constante
}
COMPLEXIDADE LINEAR – O(N)
  Uma
operação é realizada em cada elemento de
entrada, ex.: pesquisa de elementos em uma lista
for (i = 0; i < N; i = i + 1 ) {
if (condição == true) then {
realiza alguma
em tempo constante
}
else
{
realiza alguma operação em tempo constante
}
}
COMPLEXIDADE LOGARÍTMICA - O
(LOGN)
  Ocorre
tipicamente em algoritmos que dividem o
problema em problemas menores
int PesquisaBinaria ( int array[], int chave , int N){
int inf = 0;
int sup = N - 1;
int meio;
while (inf <= sup) {
meio = (inf+sup)/2;
if (chave == array[meio]) return meio;
else
if (chave < array[meio]) sup = meio-1;
else inf = meio+1;
}
return -1;
// não encontrado
}
COMPLEXIDADE LOG LINEAR – O
(NLOGN)
  Ocorre
tipicamente em algoritmos que dividem o
problema em problemas menores, porém
juntando posteriormente a solução dos
problemas menores
void merge(int inicio, int fim) {
if (inicio < fim) {
int meio = (inicio + fim) / 2;
merge(inicio, meio);
merge(meio + 1, fim);
mesclar(inicio, meio, fim);
}
}
COMPLEXIDADE QUADRÁTICA – O
(N²)
  Itens
são processados aos pares, geralmente com
um loop dentro do outro
void bubbleSort(int[] a) {
for (int i = 0; i < a.length-1; i++) {
for (int j = 0; j < a.length-1; j++) {
if (a[j] > a[j+1]) {
swap(a, j, j+1);
}
}
}
}
COMPLEXIDADE CÚBICA – O(N³)
  Itens
são processados três a três, geralmente
com um loop dentro do outros dois
int dist[N][N];
int i, j, k;
for ( k = 0; k < N; k++ )
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
dist[i][j] = min( dist[i][j], dist[i][k] +
dist[k][j] );
COMPLEXIDADE EXPONENCIAL – O
(2N)
  Utilização
de “Força Bruta” para encontrar a
solução de um problema.
  A solução geralmente é baseada diretamente no
enunciado do problema e nas definições dos
conceitos envolvidos
  Ex.:
 
 
Utilizando apenas números é possível criar 10n senhas
de n dígitos
Um algoritmo de força bruta para quebrar uma dessas
senhas tem complexidade O(2n)
COMPLEXIDADE FATORIAL – O(N!)
  Também
é baseada na utilização de força bruta
para encontrar a solução de um problema
  Consiste em testar todas as possíveis
permutações existentes na solução à procura da
solução ótima para o problema
  Ex.: Problema do Caixeiro Viajante
 
 
 
 
Encontrar a rota mínima para visitar várias cidades
sem repetir nenhuma
É um problema base para o projeto de microchips,
sequênciamento de genôma e muitas outras aplicações
Não possui solução exata eficiente (Problema NP)
Utilização de heurísticas para aproximar a solução
ótima
25
ORDENS DE COMPLEXIDADE
  Imagine
um computador que leva 1ms para
executar uma operação.
  A tabela abaixo indica o tempo aproximado de
execução de um algoritmo com diferentes ordens
de complexidades para 3 tamanhos de entrada
n
O(n)
Log(n)
nLog(n)
O(n2)
O(n3)
O(2n)
16
0.016s
0.004s
0.064s
0.256s
4s
32
0.032s
0.005s
0.16s
1s
33s
512
0.512s
0.009s
4.608s
4m22s
37h
O(n!)
1m5s 663 anos
49 dias
10143 sec
1023 sec
...
26
LIMITES SUPERIOR E INFERIOR
  Todas
as ordens de complexidade vistas definem
o Limite Superior (Upper Bound) dos Algoritmos
 
 
Qualquer que seja o tamanho da entrada, o tempo de
execução crescerá com velocidade igual ou inferior a
apontada pela análise de complexidade.
Algumas otimizações podem ser feitas para melhorar o
limite superior;
  Existem,
porém, os Limites Inferiores (Lower
Bound) para certos problemas, que são pontos a
partir dos quais não é mais possível otimizar
uma solução algorítmica
LIMITES SUPERIOR E INFERIOR
  Dado
um problema de Multiplicação de 2
matrizes N X N.
  A solução trivial tem complexidade O(n3);
 
Sabemos assim que a complexidade deste problema
não deve superar O(n3), uma vez que existe um
algoritmo com está ordem complexidade que o resolve;
  Este
limite superior de um algoritmo pode
mudar se alguém descobrir um algoritmo
melhor.
LIMITES SUPERIOR E INFERIOR
  Strassen
resolveu o problema com uma
complexidade de O(nlog 7)
 
 
Outros pesquisadores melhoraram ainda mais este
resultado.
Atualmente o melhor resultado é o de Coppersmith e
Winograd de O(n2.376).
  O
limite superior de um algoritmo é parecido
com o recorde mundial de uma modalidade de
atletismo.
  Ele é estabelecida pelo melhor atleta (algoritmo)
do momento. Assim como o recorde mundial, o
limite superior pode ser melhorado por um
algoritmo (atleta) mais veloz.
29
LIMITES SUPERIOR E INFERIOR
  Às
vezes é necessário mostrar que, para um dado
problema, qualquer que seja o algoritmo a ser
usado, requer um certo número mínimo de
operações: o Limite Inferior
  Para o problema de multiplicação de matrizes de
ordem n, apenas para ler os elementos das duas
matrizes de entrada leva O(n2). Assim uma cota
inferior trivial é Ω(n2).
LIMITES SUPERIOR E INFERIOR
  Na
analogia anterior, o limite inferior não
dependeria mais do atleta.
 
 
Seria algum tempo mínimo que a modalidade exige,
qualquer que seja o atleta.
Um limite inferior trivial para os 100 metros seria o
tempo que a velocidade da luz leva para percorrer 100
metros no vácuo.
  Se
um algoritmo tem uma complexidade que é
igual ao limite inferior do problema então o
algoritmo é ótimo.
  O algoritmo de CopperSmith e Winograd é de O
(n2.376) mas o limite inferior é de Ω(n²).
 
Portanto não é ótimo. Este limite superior ainda pode
ser melhorado
31
Download