SlideShare uma empresa Scribd logo
1 de 57
INE5408
Estruturas de Dados
Complexidade de Algoritmos
- Complexidade de tempo
- Interpretação da complexidade de tempo
- Cálculo da complexidade de tempo
Introdução
O estudo de Estruturas de Dados é
basicamente o estudo de métodos de
organização de grandes quantidades
de dados. Estes métodos incluem:
–formas de organizá-los (estruturas);
–técnicas para manipulá-los
(algoritmos básicos).
Introdução
• Pilhas, Listas e Filas são estruturas de dados típicas;
• para manipular estas estruturas - inserir elementos,
retirar elementos e ordenar elementos - necessitamos
de algoritmos;
• estes algoritmos podem ser implementados de muitas
maneiras. Algumas simples e diretas, outras não tão
simples - porém engenhosas - e outras ainda
complicadas e envolvendo muitas operações;
• quando trabalhamos com quantidades muito grandes
de dados, um projeto ruim de algoritmo para
manipular uma estrutura de dados pode resultar em
um programa que apresenta um tempo de execução
inviável.
Introdução
Quando estudamos algoritmos, um
aspecto que devemos considerar, além
de sua correção, é a análise da sua
eficiência.
A análise de algoritmos pode ser
definida como o estudo da estimativa
do tempo de execução dos
algoritmos (M. A. Weiss).
Idéias básicas
• Um algoritmo é um conjunto finito de
passos que devem ser seguidos com
um conjunto de dados de entrada para
se chegar à solução de um problema;
• um problema, geralmente, pode ser
resolvido por muitos algoritmos
diferentes;
• exemplo: cálculo da potência xn de um
número inteiro x.
Idéias básicas
//Versão iterativa.
inteiro potência(x, n)
inteiro y, i;
início
i <- n;
y <- 1;
enquanto (i > 0) faça
y <- y * x;
i <- i - 1;
fim enquanto
retorne y;
fim
Idéias básicas
//Versão recursiva.
inteiro potência_recursiva(x, n)
inteiro y;
início
se (n = 1) então
retorne x;
y <- potência_recursiva (x, (n / 2));
se (ímpar(n)) então
retorne x*y*y;
senão
retorne y*y;
fim se
fim
Idéias básicas
O fato de existir um algoritmo para
resolver um problema não implica
necessariamente que este problema
possa realmente ser resolvido na
prática. Há restrições de tempo e de
espaço de memória.
Exemplo: calcular todas as possíveis
partidas de xadrez.
Idéias básicas
Um algoritmo é uma idéia abstrata de como
resolver um determinado problema. Ele é, a
princípio, independente da máquina que o
executará e de suas características.
Um programa é uma implementação de um
algoritmo em uma linguagem particular que
será executado em um computador particular.
Um programa está sujeito às limitações físicas
da máquina onde será executado, como
capacidade de memória, velocidade do
processador e dos periféricos, entre outras.
Idéias básicas
O tempo que a execução de um programa toma
é uma grandeza física que depende:
– do tempo que a máquina leva para executar
uma instrução ou um passo de programa;
– da natureza do algoritmo, isto é, de quantos
passos são necessários para se resolver o
problema para um dado;
– do tamanho do conjunto de dados que
constitui o problema.
Problema básico na Análise de Algoritmos
Necessitamos definir uma forma de criar
uma medida de comparação entre
diferentes algoritmos que resolvem um
mesmo problema, para:
– podermos saber se são viáveis;
– podermos saber qual é o melhor algoritmo
para a solução do problema.
Problema básico na Análise de Algoritmos
Para fazermos isso, abstraímos de um
computador em particular e
assumimos que a execução de todo e
qualquer passo de um algoritmo leva
um tempo fixo e igual a uma unidade de
tempo:
– o tempo de execução em um computador
particular não é interessante;
– muito mais interessante é uma comparação
relativa entre algoritmos.
Problema básico na Análise de Algoritmos
Modelo de computação
– as operações são todas executadas
seqüencialmente;
– a execução de toda e qualquer operação
toma uma unidade de tempo;
– a memória do computador é infinita.
Assim nos sobram duas grandezas:
• tempo = número de operações executadas;
• quantidade de dados de entrada.
Complexidade de Tempo
Podemos expressar de forma abstrata a
eficiência de um algoritmo descrevendo
o seu tempo de execução como uma
função do tamanho do problema
(quantidade de dados). Isto é chamado
de complexidade de tempo.
Exemplo: ordenação de um Vetor.
Primeiro caso: Bubblesort
Bubblesort é o mais primitivo dos métodos
de ordenação de um vetor. A idéia é
percorrer um vetor de n posições n
vezes, a cada vez comparando dois
elementos e trocando-os caso o
primeiro seja maior que o segundo.
Primeiro caso: Bubblesort
//Método da bolha.
Bubblesort(a[], n)
inteiro i, j, x;
início
para i de 1 até n faça
para j de 2 até n faça
se (a[j-1] > a[j]) então
x <- a[j-1];
a[j-1] <- a[j];
a[j] <- x;
fim se
fim para
fim para
fim
Primeiro caso: Bubblesort
A comparação (a[j-1] > a[j]) vai ser
executada n*(n-1) vezes. No caso de
um vetor na ordem inversa, as
operações da atribuição triangular
poderão ser executadas até 3*n*(n-1)
vezes, já que uma troca de elementos
não significa que um dos elementos
trocados tenha encontrado o seu lugar
definitivo.
Segundo caso: StraightSelection
O método da seleção direta é uma forma
intuitiva de ordenarmos um vetor:
escolhemos o menor elemento do vetor
e o trocamos de posição com o primeiro
elemento.
Depois começamos do segundo e
escolhemos novamente o menor dentre
os restantes e o trocamos de posição
com o segundo e assim por diante.
Segundo caso: StraightSelection
StraightSelection(a[], n)
inteiro i, j, k, x;
início
para i de 1 até n-1 faça
k <- i;
x <- a[i];
para j de i+1 até n faça
se (a[j] < x) então
k <- j;
x <- a[k];
fim se
fim para
a[k] <- a[i];
a[i] <- x;
fim para
fim
Segundo caso: StraightSelection
Neste algoritmo o número de vezes que a
comparação (a[j] < x) é executada é
expresso por (n-1)+(n-2)+...+2+1 =
(n/2)*(n-1).
O número de trocas a[k] <- a[i]; a[i] <- x é
realizado no pior caso, onde o vetor
está ordenado em ordem inversa,
somente n-1 vezes, num total de 2*(n-
1).
Interpretação
Como já foi dito, a única forma de se
poder comparar dois algoritmos é
descrevendo o seu comportamento
temporal em função do tamanho do
conjunto de dados de entrada. Assim:
Talgoritmo = f(n), onde n é o tamanho
do conjunto de dados.
Interpretação
Se tomarmos as operações de troca de
valores como critério-base, podemos
dizer que:
TBubblesort = 3*n*(n-1) sempre
TStraightSelection = 2*(n-1) para o pior caso
Interpretação
O que nos interessa é o comportamento
assintótico de f(n), ou seja, como f(n)
varia com a variação de n.
Razão: para mim é interessante saber
como o algoritmo se comporta com uma
quantidade de dados realística para o
meu problema e o que acontece quando
eu vario esses dados.
Interpretação
Exemplo: eu tenho dois algoritmos (a e b)
para a solução de um problema. Se a
complexidade de um é expressa por
fa(n) = n2 e a do outro por fb(n) = 100*n,
significa que o algoritmo a cresce
quadraticamente (uma parábola) e que
o algoritmo b cresce linearmente
(embora seja uma reta bem inclinada).
Interpretação
Se eu uso estes algoritmos para um
conjunto de 30 dados, o segundo com
Tb=3.000 é pior do que o primeiro com
Ta=900.
Se eu os uso para um conjunto de 30.000
dados, porém, terei Ta=900.000.000 e
Tb=3.000.000.
Isto ocorre porque o comportamento
assintótico dos dois é bem diferente.
Interpretação
Interpretação da Complexidade de Tempo
Aspecto essencial que deve ser expresso
pelo cálculo de complexidade:
Qual é o comportamento assintótico
predominante de um algoritmo
em função do tamanho do
conjunto de dados a ser processado.
Exemplo: se é linear, polinomial
(quadrático, cúbico, etc.), logarítmico ou
exponencial.
Análise Assintótica
Para a análise do comportamento de
algoritmos existe toda uma terminologia
própria.
Para o cálculo do comportamento de
algoritmos foram desenvolvidas
diferentes medidas de complexidade.
A mais importante delas e que é usada na
prática é chamada de Ordem de
Complexidade ou Notação-O ou Big-
Oh.
Análise Assintótica
Definição (Big-Oh): T(n) = O(f(n)) se existem
constantes c e n0 tais que T(n)c.f(n) quando nn0.
A definição indica que existe uma constante c que
faz com que c.f(n) seja sempre pelo menos tão
grande quanto T(n), desde que n seja maior que
um n0.
Em outras palavras: a Notação-O me fornece a
Ordem de Complexidade ou a Taxa de
Crescimento de uma função.
Para isso, não consideramos os termos de ordem
inferior da complexidade de um algoritmo,
apenas o termo predominante.
Análise Assintótica
Exemplo: algoritmo com complexidade T(n) =
3n2 + 100n.
Nesta função, o segundo termo tem um peso
relativamente grande, mas a partir de n0 = 11,
é o termo n2 que "dá o tom" do crescimento
da função: uma parábola. A constante 3
também tem uma influência irrelevante sobre
a taxa de crescimento da função após um
certo tempo.
Por isso dizemos que este algoritmo é da ordem
de n2 ou que tem complexidade O(n2).
Análise Assintótica
Função Nome
1 Constante
log n Logarítmica
log2 n Log-quadrática
n Linear
n log n n log n
n2 Quadrática
n3 Cúbica
2n Exponencial
Diferentes Tempos de Execução
Problema da Subseqüência de Soma Máxima: dada
uma seqüência de números a1, a2, ... , an, positivos ou
negativos, encontre uma subseqüência aj, ..., ak
dentro desta seqüência cuja soma seja máxima. A
soma de uma seqüência contendo só números
negativos é por definição 0.
Exemplo: para a seqüência -2, 11, -4, 13, -5, -2, a
resposta é 20 (a2,a3,a4).
Este problema oferece um bom exemplo para o estudo
de como diferentes algoritmos que resolvem o
mesmo problema possuem diferentes
comportamentos, pois para ele existem muitas
soluções diferentes.
Diferentes Tempos de Execução
Algoritmo 1 2 3 4
Tempo O(n3) O(n2) O(n log n) O(n)
n = 10 0,00103 0,00045 0,00066 0,00034
n = 100 0,47015 0,01112 0,00486 0,00063
n = 1.000 448,77 1,1233 0,05843 0,00333
n = 10.000 NA 111,13 0,68631 0,03042
n = 100.000 NA NA 8,0113 0,29832
Cálculo da Complexidade de Tempo
Um exemplo intuitivo
inteiro somaCubos(inteiro n)
inteiro i, somaParcial;
início
1 somaParcial <- 0;
2 para i de 1 até n faça
3 somaParcial <- somaParcial + i * i * i;
4 fim para
5 retorne somaParcial;
fim
Cálculo da Complexidade de Tempo
Análise:
• as declarações não tomam tempo nenhum;
• a linha 4 também não toma tempo nenhum;
• as linhas 1 e 5 contam uma unidade de tempo cada;
• a linha 3 conta 4 unidades de tempo (2 multiplicações,
uma adição e uma atribuição) e é executada n vezes,
contando com um total de 4n unidades de tempo;
• a linha 2 possui custos implícitos de inicializar i, testar
se é menor que n e incrementá-lo. Contamos 1 unidade
para sua inicialização, n + 1 para todos os testes e n
para todos os incrementos, o que perfaz 2n + 2
unidades de tempo;
• o total perfaz 6n + 4 unidades de tempo, o que indica
que o algoritmo é O(n), da Ordem de Complexidade n,
ou seja, linear.
Regras para o cálculo
Laços: o tempo de execução de um laço é, no máximo, a
soma dos tempos de execução de todas as instruções
dentro do laço (incluindo todos os testes) multiplicado pelo
número de iterações.
Laços aninhados: analise-os de dentro para fora. O tempo
total de execução de uma instrução dentro de um grupo
de laços aninhados é o tempo de execução da instrução
multiplicado pelo produto dos tamanhos de todos os laços.
Exemplo: O(n2)
para i de 1 até n faça
para j de 1 até n faça
k <- k + 1;
fim para
fim para
Regras para o cálculo
Instruções Consecutivas: estes simplesmente somam, sendo os
termos de ordem menor da soma ignorados.
Exemplo: O(n) + O(n2) = O(n2)
para i de 1 até n faça
a[i] <- 0;
fim para
para i de 1 até n faça
para j de 1 até n faça
a[i] <- a[j] + k + 1;
fim para
fim para
Regras para o cálculo
Se / Então / Senão: considere o fragmento de código abaixo.
se cond então
expresssão1
senão
expressão2
fim se
O tempo de execução de um comando Se / Então / Senão
nunca é maior do que o tempo de execução do teste cond
em si mais o tempo de execução da maior dentre as
expressões expressão1 e expressão2. Ou seja: se
expressão1 é O(n3) e expressão2 é O(n), então o teste é
O(n3) + 1 = O(n3).
Regras para o cálculo
Chamada a Funções: segue a mesma
regra dos laços aninhados - analise tudo
de dentro para fora. Ou seja: para
calcular a complexidade de um
programa com várias funções, calcula-
se primeiro a complexidade de cada
uma das funções e depois considera-se
cada uma das funções como uma
instrução com a complexidade de
função.
Regras para o cálculo
Recursão: é a parte mais difícil da análise de
complexidade. Na verdade existem dois casos:
muitos algoritmos recursivos mais simples podem
ser "linearizados", substituindo-se a chamada
recursiva por alguns laços aninhados ou por uma
outra subrotina extra e eventualmente uma pilha
para controlá-la. Nestes casos, o cálculo é simples
e pode ser feito depois da "linearização". Em
muitos algoritmos recursivos, porém, isto não é
possível. Nestes casos obtemos uma relação de
recorrência que tem de ser resolvida e é uma tarefa
matemática menos trivial.
Regras para o cálculo
Exemplo de cálculo de complexidade
em recursão: Fatorial
inteiro Fatorial(inteiro n)
início
se n <= 1 então
retorne 1
senão
retorne (n * Fatorial (n - 1));
fim se
fim
Regras para o cálculo
O exemplo anterior é realmente um exemplo pobre de recursão e
pode ser "iterativisado" de forma extremamente simples com
apenas um laço para-faça:
inteiro FatorialIterativo(inteiro n)
inteiro i, fatorial;
início
fatorial <- 1;
para i de 2 até n faça
fatorial <- fatorial * i;
fim para
retorne fatorial;
fim
A complexidade de FatorialIterativo pode então ser facilmente
calculada e é evidente que é O(n).
Regras para o cálculo
O caso dos números de Fibonacci abaixo não é tão simples e
requer a resolução de uma relação de recorrência:
inteiro Fibonacci(inteiro n)
início
se n <= 1 então
retorne 1
senão
retorne (Fibonacci(n-1) + Fibonacci(n-2));
fim se
fim
Observando o algoritmo, vemos que para n >= 2 temos um tempo
de execução T(n) = T(n-1)+T(n-2)+2. A resolução desta relação
nos mostra que Fibonacci é O(()n).
Logaritmos e outros Tempos de Execução
O aspecto mais complexo na análise de
complexidade centra-se em torno do
logaritmo. Para analisar-se um algoritmo de
complexidade logarítmica e chegar-se a um
resultado correto sobre a sua ordem exata de
complexidade é necessária uma certa
experiência e algum "jeito" matemático.
Algumas regras de aproximação podem ser
dadas: algoritmos seguindo a técnica Dividir-
Para-Conquistar são muitas vezes n log n.
Logaritmos e outros Tempos de Execução
• Quando um algoritmo, em uma passada de
uma iteração toma o conjunto de dados e o
divide em duas ou mais partes, sendo cada
uma dessas partes processada separada e
recursivamente, este algoritmo utiliza a
técnica dividir-para-conquistar e será
possivelmente n log n;
• um algoritmo é log n se ele toma um tempo
constante O(1) para dividir o tamanho do
problema, usualmente pela metade;
• a pesquisa binária é um exemplo de log n.
Logaritmos e outros Tempos de Execução
Se o algoritmo toma tempo constante
para reduzir o tamanho do problema em
um tamanho constante, ele será O(n).
Algoritmos combinatórios são
exponenciais: se um algoritmo testa
todas as combinações de alguma coisa,
ele será exponencial.
Exemplo: Problema do Caixeiro Viajante
Checando a sua análise
Uma vez que a análise de complexidade tenha
sido executada, é interessante verificar-se se
a resposta está correta e é tão boa quanto
possível.
Uma forma de se fazer isto é o procedimento
pouco matemático de se codificar o trecho de
algoritmo cuja complexidade se tentou
descrever e verificar se o tempo de execução
coincide com o tempo previsto pela análise.
Quando n dobra, o tempo de execução se eleva
de um fator 2 para algoritmos lineares, fator 4
para quadráticos e fator 8 para cúbicos.
Checando a sua análise
Programas logarítmicos só aumentam o seu
tempo de execução de uma constante a cada
vez que n dobra. Algoritmos de O(n log n)
tomam um pouco mais do que o dobro do
tempo sob as mesmas circunstâncias.
Estes aumentos podem ser difíceis de se
detectar se os termos de ordem inferior têm
coeficientes relativamente grandes e n não é
grande o suficiente. Um exemplo é o pulo no
tempo de execução de n=10 para n=100 em
algumas implementações do problema da
subseqüência de soma máxima.
Checando a sua análise
Distinguir programas n log n de
programas lineares só em evidência de
tempo de execução pode também ser
muito difícil.
Uma boa praxe é, caso o programa não
seja exponencial (o que você vai
descobrir muito rápido), fazer alguns
experimentos com conjuntos maiores de
dados de entrada.
Exercício: cálculo de complexidade
1. Floyd
Considere o grafo e sua matriz de custos
D, abaixo:
Exercício: cálculo de complexidade
Pelo algoritmo de Floyd pode-se obter a Matriz de
Custos A – com os comprimentos dos menores
caminhos – e a Matriz de Roteamento R.
Exemplo: custo e rota v4 -> v3
3
v4
v3
v4->v3
v1->v3
v2->v3
v4->v3 = v4->v1->v3->v2
Exercício: cálculo de complexidade
Agora, por exemplo, para determinar a
rota de v2 para v1, toma-se R[2,1] = v5,
R[5,1] = v4, R[4,1] = v1.
Logo, a rota de v2 para v1 é:
v2 → v5 → v4 → v1
Exercício: cálculo de complexidade
Algoritmo de Floyd – modificado
• Considere a matriz de custos D[n,n]
definida como no algoritmo de
Floyd. O algoritmo produzirá uma
matriz A[n,n], com comprimentos
dos menores caminhos e ainda
uma matriz R[n,n] que fornece o
vértice k, que é o primeiro vértice a
ser visitado no menor caminho de vi
até vj.
Exercício: cálculo de complexidade
FloydModificado()
início
para i = 1 até n faça
para j = 1 até n faça
A[i,j] <- D[i,j];
R[i,j] <- 0;
fim para
fim para
para i = 1 até n faça
A[i,i] <- 0;
fim para
para k = 1 até n faça
para i = 1 até n faça
para j = 1 até n faça
se A[i,k] + A[k,j] < A[i,j] então
A[i,j] <- A[i,k] + A[k,j];
R[i,j] <- k;
fim para
fim para
fim para
fim
Exercício: cálculo de complexidade
2. Torres de Hanoi
2.1. Considere o Problema das Torres de Hanoi com 3
Pinos na sua versão iterativa, como visto em aula:
– É um programa que itera sempre sobre duas jogadas: a
menor peça e outra possível de ser movida
– Você viu que para 2 discos, houve 3 movimentações de
disco e para 3 discos o algoritmo executou 7
movimentações.
– Qual é essa tendência ? Será que esse comportamento
continuará seguindo essa tendência?
– Calcule a complexidade do algoritmo das torres de Hanoi
Exercício: cálculo de complexidade
2. Torres de Hanoi
2.2. Considere o Problema das Torres de Hanoi
implementado por você em aula:
– Mantendo o número de pilhas (pinos) constante, você pode
escolher um número arbitrário de discos.
– Modifique o programa para poder mensurar o tempo de
execução.
• Use as funções ftime(), ctime(), difftime().
• Observe que difftime() só tem sentido se o programa rodar mais
de um segundo, o que não acontecerá para valores baixos.
– Rode o programa para 2, 3, 6, 7,10, 30, 50, 100, 200, 300,
500, 1000, 2000, 10.000, 50.000,100.000 e 500.000 discos.
Exercício: cálculo de complexidade
2. Torres de Hanoi
– Ao final o programa
deverá chamar o
programa unix gnuplot
com o arquivo de
tempos como
parâmetro para que
um gráfico dos tempos
seja mostrado na tela.
Para tanto esse
arquivo deverá ser
gerado na sintaxe do
gnuplot, detalhada no
manual do gnuplot.

Mais conteúdo relacionado

Semelhante a Aula_07_Complexidade_de_Algoritmos.ppt

Análise de desempenho de algoritmos de ordenação
Análise de desempenho de algoritmos de ordenaçãoAnálise de desempenho de algoritmos de ordenação
Análise de desempenho de algoritmos de ordenaçãoGustavo Carvalho
 
Comparação Experimental de Algoritmos de Ordenação
Comparação Experimental de Algoritmos de OrdenaçãoComparação Experimental de Algoritmos de Ordenação
Comparação Experimental de Algoritmos de OrdenaçãoLenon Fachiano
 
Tutorial aed iii 005 - algoritmo de ordenação quicksort
Tutorial aed iii   005 - algoritmo de ordenação quicksortTutorial aed iii   005 - algoritmo de ordenação quicksort
Tutorial aed iii 005 - algoritmo de ordenação quicksortFlávio Freitas
 
Análise assintótica
Análise assintóticaAnálise assintótica
Análise assintóticaPablo Silva
 
Análise da complexidade de algoritmos
Análise da complexidade de algoritmosAnálise da complexidade de algoritmos
Análise da complexidade de algoritmosPablo Silva
 
Artigo sobre complexibilidade complexity.pdf
Artigo sobre complexibilidade complexity.pdfArtigo sobre complexibilidade complexity.pdf
Artigo sobre complexibilidade complexity.pdfItaloRainier1
 
Curso Básico de Java - Aula 10
Curso Básico de Java - Aula 10Curso Básico de Java - Aula 10
Curso Básico de Java - Aula 10PeslPinguim
 
Desenvolvimento análise de sistemas lineares
Desenvolvimento análise de sistemas linearesDesenvolvimento análise de sistemas lineares
Desenvolvimento análise de sistemas linearesMaique Mateus
 
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...Eduardo de Lucena Falcão
 
Algoritmos de ordenação
Algoritmos de ordenaçãoAlgoritmos de ordenação
Algoritmos de ordenaçãoJonas Mendonça
 
Introdução à analise e complexidade de algoritmos
Introdução à analise e complexidade de algoritmosIntrodução à analise e complexidade de algoritmos
Introdução à analise e complexidade de algoritmosNécio de Lima Veras
 
Ordenação de pares e ímpares
Ordenação de pares e ímparesOrdenação de pares e ímpares
Ordenação de pares e ímparesDiego Lusa
 
Análise de Complexidade em Algoritmos.pdf
Análise de Complexidade em Algoritmos.pdfAnálise de Complexidade em Algoritmos.pdf
Análise de Complexidade em Algoritmos.pdfLukasBernardo
 
Algoritmo e estruturas de dados operações com matrizes
Algoritmo e estruturas de dados operações com matrizesAlgoritmo e estruturas de dados operações com matrizes
Algoritmo e estruturas de dados operações com matrizesRADILSON RIPARDO DE FRETIAS
 
Curso Básico de Java - Aula 5
Curso Básico de Java - Aula 5Curso Básico de Java - Aula 5
Curso Básico de Java - Aula 5PeslPinguim
 
Aula 1 - Estudando o problema a ser resolvido
Aula 1 - Estudando o problema a ser resolvidoAula 1 - Estudando o problema a ser resolvido
Aula 1 - Estudando o problema a ser resolvidoEduardo de Lucena Falcão
 

Semelhante a Aula_07_Complexidade_de_Algoritmos.ppt (20)

Análise de desempenho de algoritmos de ordenação
Análise de desempenho de algoritmos de ordenaçãoAnálise de desempenho de algoritmos de ordenação
Análise de desempenho de algoritmos de ordenação
 
Comparação Experimental de Algoritmos de Ordenação
Comparação Experimental de Algoritmos de OrdenaçãoComparação Experimental de Algoritmos de Ordenação
Comparação Experimental de Algoritmos de Ordenação
 
Tutorial aed iii 005 - algoritmo de ordenação quicksort
Tutorial aed iii   005 - algoritmo de ordenação quicksortTutorial aed iii   005 - algoritmo de ordenação quicksort
Tutorial aed iii 005 - algoritmo de ordenação quicksort
 
Algoritmos
AlgoritmosAlgoritmos
Algoritmos
 
Análise assintótica
Análise assintóticaAnálise assintótica
Análise assintótica
 
Análise da complexidade de algoritmos
Análise da complexidade de algoritmosAnálise da complexidade de algoritmos
Análise da complexidade de algoritmos
 
Artigo sobre complexibilidade complexity.pdf
Artigo sobre complexibilidade complexity.pdfArtigo sobre complexibilidade complexity.pdf
Artigo sobre complexibilidade complexity.pdf
 
Curso Básico de Java - Aula 10
Curso Básico de Java - Aula 10Curso Básico de Java - Aula 10
Curso Básico de Java - Aula 10
 
Desenvolvimento análise de sistemas lineares
Desenvolvimento análise de sistemas linearesDesenvolvimento análise de sistemas lineares
Desenvolvimento análise de sistemas lineares
 
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...
Estudo e Avaliação do Problema de Otimização da Multiplicação de Cadeias de M...
 
Cap1
Cap1Cap1
Cap1
 
Algoritmos de ordenação
Algoritmos de ordenaçãoAlgoritmos de ordenação
Algoritmos de ordenação
 
Introdução à analise e complexidade de algoritmos
Introdução à analise e complexidade de algoritmosIntrodução à analise e complexidade de algoritmos
Introdução à analise e complexidade de algoritmos
 
Math
MathMath
Math
 
Apostila rpira
Apostila rpiraApostila rpira
Apostila rpira
 
Ordenação de pares e ímpares
Ordenação de pares e ímparesOrdenação de pares e ímpares
Ordenação de pares e ímpares
 
Análise de Complexidade em Algoritmos.pdf
Análise de Complexidade em Algoritmos.pdfAnálise de Complexidade em Algoritmos.pdf
Análise de Complexidade em Algoritmos.pdf
 
Algoritmo e estruturas de dados operações com matrizes
Algoritmo e estruturas de dados operações com matrizesAlgoritmo e estruturas de dados operações com matrizes
Algoritmo e estruturas de dados operações com matrizes
 
Curso Básico de Java - Aula 5
Curso Básico de Java - Aula 5Curso Básico de Java - Aula 5
Curso Básico de Java - Aula 5
 
Aula 1 - Estudando o problema a ser resolvido
Aula 1 - Estudando o problema a ser resolvidoAula 1 - Estudando o problema a ser resolvido
Aula 1 - Estudando o problema a ser resolvido
 

Mais de ssuserd654cb1

Aula sobre Brainstorming 07.09.2022.pptx
Aula sobre Brainstorming 07.09.2022.pptxAula sobre Brainstorming 07.09.2022.pptx
Aula sobre Brainstorming 07.09.2022.pptxssuserd654cb1
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).pptssuserd654cb1
 
Aula_05_-_Listas_Duplamente_Encadeadas_opp.ppt
Aula_05_-_Listas_Duplamente_Encadeadas_opp.pptAula_05_-_Listas_Duplamente_Encadeadas_opp.ppt
Aula_05_-_Listas_Duplamente_Encadeadas_opp.pptssuserd654cb1
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.pptssuserd654cb1
 
Aula_6b_-_Listas_Circulares.ppt
Aula_6b_-_Listas_Circulares.pptAula_6b_-_Listas_Circulares.ppt
Aula_6b_-_Listas_Circulares.pptssuserd654cb1
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).pptssuserd654cb1
 
Aula_02_-_Listas_com_Vetores-OOP.ppt
Aula_02_-_Listas_com_Vetores-OOP.pptAula_02_-_Listas_com_Vetores-OOP.ppt
Aula_02_-_Listas_com_Vetores-OOP.pptssuserd654cb1
 
Aula_05_-_Listas_Duplamente_Encadeadas.ppt
Aula_05_-_Listas_Duplamente_Encadeadas.pptAula_05_-_Listas_Duplamente_Encadeadas.ppt
Aula_05_-_Listas_Duplamente_Encadeadas.pptssuserd654cb1
 
Aula_07_Complexidade_de_Algoritmos (1).ppt
Aula_07_Complexidade_de_Algoritmos (1).pptAula_07_Complexidade_de_Algoritmos (1).ppt
Aula_07_Complexidade_de_Algoritmos (1).pptssuserd654cb1
 
Aula_02_-_Listas_com_Vetores-OOP_2011_2.ppt
Aula_02_-_Listas_com_Vetores-OOP_2011_2.pptAula_02_-_Listas_com_Vetores-OOP_2011_2.ppt
Aula_02_-_Listas_com_Vetores-OOP_2011_2.pptssuserd654cb1
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.pptssuserd654cb1
 
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.ppt
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.pptAula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.ppt
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.pptssuserd654cb1
 
Aula_01_-_Pilhas_e_Filas_com_Vetores.ppt
Aula_01_-_Pilhas_e_Filas_com_Vetores.pptAula_01_-_Pilhas_e_Filas_com_Vetores.ppt
Aula_01_-_Pilhas_e_Filas_com_Vetores.pptssuserd654cb1
 

Mais de ssuserd654cb1 (13)

Aula sobre Brainstorming 07.09.2022.pptx
Aula sobre Brainstorming 07.09.2022.pptxAula sobre Brainstorming 07.09.2022.pptx
Aula sobre Brainstorming 07.09.2022.pptx
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete (1).ppt
 
Aula_05_-_Listas_Duplamente_Encadeadas_opp.ppt
Aula_05_-_Listas_Duplamente_Encadeadas_opp.pptAula_05_-_Listas_Duplamente_Encadeadas_opp.ppt
Aula_05_-_Listas_Duplamente_Encadeadas_opp.ppt
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria_new_delete.ppt
 
Aula_6b_-_Listas_Circulares.ppt
Aula_6b_-_Listas_Circulares.pptAula_6b_-_Listas_Circulares.ppt
Aula_6b_-_Listas_Circulares.ppt
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria (1).ppt
 
Aula_02_-_Listas_com_Vetores-OOP.ppt
Aula_02_-_Listas_com_Vetores-OOP.pptAula_02_-_Listas_com_Vetores-OOP.ppt
Aula_02_-_Listas_com_Vetores-OOP.ppt
 
Aula_05_-_Listas_Duplamente_Encadeadas.ppt
Aula_05_-_Listas_Duplamente_Encadeadas.pptAula_05_-_Listas_Duplamente_Encadeadas.ppt
Aula_05_-_Listas_Duplamente_Encadeadas.ppt
 
Aula_07_Complexidade_de_Algoritmos (1).ppt
Aula_07_Complexidade_de_Algoritmos (1).pptAula_07_Complexidade_de_Algoritmos (1).ppt
Aula_07_Complexidade_de_Algoritmos (1).ppt
 
Aula_02_-_Listas_com_Vetores-OOP_2011_2.ppt
Aula_02_-_Listas_com_Vetores-OOP_2011_2.pptAula_02_-_Listas_com_Vetores-OOP_2011_2.ppt
Aula_02_-_Listas_com_Vetores-OOP_2011_2.ppt
 
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.pptAula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.ppt
Aula_03_-_2_-_Aloca_o_Din_mica_de_Mem_ria.ppt
 
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.ppt
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.pptAula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.ppt
Aula_01_-_Apresenta_o_e_Programa_da_Disciplina_2012-1.ppt
 
Aula_01_-_Pilhas_e_Filas_com_Vetores.ppt
Aula_01_-_Pilhas_e_Filas_com_Vetores.pptAula_01_-_Pilhas_e_Filas_com_Vetores.ppt
Aula_01_-_Pilhas_e_Filas_com_Vetores.ppt
 

Aula_07_Complexidade_de_Algoritmos.ppt

  • 1. INE5408 Estruturas de Dados Complexidade de Algoritmos - Complexidade de tempo - Interpretação da complexidade de tempo - Cálculo da complexidade de tempo
  • 2. Introdução O estudo de Estruturas de Dados é basicamente o estudo de métodos de organização de grandes quantidades de dados. Estes métodos incluem: –formas de organizá-los (estruturas); –técnicas para manipulá-los (algoritmos básicos).
  • 3. Introdução • Pilhas, Listas e Filas são estruturas de dados típicas; • para manipular estas estruturas - inserir elementos, retirar elementos e ordenar elementos - necessitamos de algoritmos; • estes algoritmos podem ser implementados de muitas maneiras. Algumas simples e diretas, outras não tão simples - porém engenhosas - e outras ainda complicadas e envolvendo muitas operações; • quando trabalhamos com quantidades muito grandes de dados, um projeto ruim de algoritmo para manipular uma estrutura de dados pode resultar em um programa que apresenta um tempo de execução inviável.
  • 4. Introdução Quando estudamos algoritmos, um aspecto que devemos considerar, além de sua correção, é a análise da sua eficiência. A análise de algoritmos pode ser definida como o estudo da estimativa do tempo de execução dos algoritmos (M. A. Weiss).
  • 5. Idéias básicas • Um algoritmo é um conjunto finito de passos que devem ser seguidos com um conjunto de dados de entrada para se chegar à solução de um problema; • um problema, geralmente, pode ser resolvido por muitos algoritmos diferentes; • exemplo: cálculo da potência xn de um número inteiro x.
  • 6. Idéias básicas //Versão iterativa. inteiro potência(x, n) inteiro y, i; início i <- n; y <- 1; enquanto (i > 0) faça y <- y * x; i <- i - 1; fim enquanto retorne y; fim
  • 7. Idéias básicas //Versão recursiva. inteiro potência_recursiva(x, n) inteiro y; início se (n = 1) então retorne x; y <- potência_recursiva (x, (n / 2)); se (ímpar(n)) então retorne x*y*y; senão retorne y*y; fim se fim
  • 8. Idéias básicas O fato de existir um algoritmo para resolver um problema não implica necessariamente que este problema possa realmente ser resolvido na prática. Há restrições de tempo e de espaço de memória. Exemplo: calcular todas as possíveis partidas de xadrez.
  • 9. Idéias básicas Um algoritmo é uma idéia abstrata de como resolver um determinado problema. Ele é, a princípio, independente da máquina que o executará e de suas características. Um programa é uma implementação de um algoritmo em uma linguagem particular que será executado em um computador particular. Um programa está sujeito às limitações físicas da máquina onde será executado, como capacidade de memória, velocidade do processador e dos periféricos, entre outras.
  • 10. Idéias básicas O tempo que a execução de um programa toma é uma grandeza física que depende: – do tempo que a máquina leva para executar uma instrução ou um passo de programa; – da natureza do algoritmo, isto é, de quantos passos são necessários para se resolver o problema para um dado; – do tamanho do conjunto de dados que constitui o problema.
  • 11. Problema básico na Análise de Algoritmos Necessitamos definir uma forma de criar uma medida de comparação entre diferentes algoritmos que resolvem um mesmo problema, para: – podermos saber se são viáveis; – podermos saber qual é o melhor algoritmo para a solução do problema.
  • 12. Problema básico na Análise de Algoritmos Para fazermos isso, abstraímos de um computador em particular e assumimos que a execução de todo e qualquer passo de um algoritmo leva um tempo fixo e igual a uma unidade de tempo: – o tempo de execução em um computador particular não é interessante; – muito mais interessante é uma comparação relativa entre algoritmos.
  • 13. Problema básico na Análise de Algoritmos Modelo de computação – as operações são todas executadas seqüencialmente; – a execução de toda e qualquer operação toma uma unidade de tempo; – a memória do computador é infinita. Assim nos sobram duas grandezas: • tempo = número de operações executadas; • quantidade de dados de entrada.
  • 14. Complexidade de Tempo Podemos expressar de forma abstrata a eficiência de um algoritmo descrevendo o seu tempo de execução como uma função do tamanho do problema (quantidade de dados). Isto é chamado de complexidade de tempo. Exemplo: ordenação de um Vetor.
  • 15. Primeiro caso: Bubblesort Bubblesort é o mais primitivo dos métodos de ordenação de um vetor. A idéia é percorrer um vetor de n posições n vezes, a cada vez comparando dois elementos e trocando-os caso o primeiro seja maior que o segundo.
  • 16. Primeiro caso: Bubblesort //Método da bolha. Bubblesort(a[], n) inteiro i, j, x; início para i de 1 até n faça para j de 2 até n faça se (a[j-1] > a[j]) então x <- a[j-1]; a[j-1] <- a[j]; a[j] <- x; fim se fim para fim para fim
  • 17. Primeiro caso: Bubblesort A comparação (a[j-1] > a[j]) vai ser executada n*(n-1) vezes. No caso de um vetor na ordem inversa, as operações da atribuição triangular poderão ser executadas até 3*n*(n-1) vezes, já que uma troca de elementos não significa que um dos elementos trocados tenha encontrado o seu lugar definitivo.
  • 18. Segundo caso: StraightSelection O método da seleção direta é uma forma intuitiva de ordenarmos um vetor: escolhemos o menor elemento do vetor e o trocamos de posição com o primeiro elemento. Depois começamos do segundo e escolhemos novamente o menor dentre os restantes e o trocamos de posição com o segundo e assim por diante.
  • 19. Segundo caso: StraightSelection StraightSelection(a[], n) inteiro i, j, k, x; início para i de 1 até n-1 faça k <- i; x <- a[i]; para j de i+1 até n faça se (a[j] < x) então k <- j; x <- a[k]; fim se fim para a[k] <- a[i]; a[i] <- x; fim para fim
  • 20. Segundo caso: StraightSelection Neste algoritmo o número de vezes que a comparação (a[j] < x) é executada é expresso por (n-1)+(n-2)+...+2+1 = (n/2)*(n-1). O número de trocas a[k] <- a[i]; a[i] <- x é realizado no pior caso, onde o vetor está ordenado em ordem inversa, somente n-1 vezes, num total de 2*(n- 1).
  • 21. Interpretação Como já foi dito, a única forma de se poder comparar dois algoritmos é descrevendo o seu comportamento temporal em função do tamanho do conjunto de dados de entrada. Assim: Talgoritmo = f(n), onde n é o tamanho do conjunto de dados.
  • 22. Interpretação Se tomarmos as operações de troca de valores como critério-base, podemos dizer que: TBubblesort = 3*n*(n-1) sempre TStraightSelection = 2*(n-1) para o pior caso
  • 23. Interpretação O que nos interessa é o comportamento assintótico de f(n), ou seja, como f(n) varia com a variação de n. Razão: para mim é interessante saber como o algoritmo se comporta com uma quantidade de dados realística para o meu problema e o que acontece quando eu vario esses dados.
  • 24. Interpretação Exemplo: eu tenho dois algoritmos (a e b) para a solução de um problema. Se a complexidade de um é expressa por fa(n) = n2 e a do outro por fb(n) = 100*n, significa que o algoritmo a cresce quadraticamente (uma parábola) e que o algoritmo b cresce linearmente (embora seja uma reta bem inclinada).
  • 25. Interpretação Se eu uso estes algoritmos para um conjunto de 30 dados, o segundo com Tb=3.000 é pior do que o primeiro com Ta=900. Se eu os uso para um conjunto de 30.000 dados, porém, terei Ta=900.000.000 e Tb=3.000.000. Isto ocorre porque o comportamento assintótico dos dois é bem diferente.
  • 27. Interpretação da Complexidade de Tempo Aspecto essencial que deve ser expresso pelo cálculo de complexidade: Qual é o comportamento assintótico predominante de um algoritmo em função do tamanho do conjunto de dados a ser processado. Exemplo: se é linear, polinomial (quadrático, cúbico, etc.), logarítmico ou exponencial.
  • 28. Análise Assintótica Para a análise do comportamento de algoritmos existe toda uma terminologia própria. Para o cálculo do comportamento de algoritmos foram desenvolvidas diferentes medidas de complexidade. A mais importante delas e que é usada na prática é chamada de Ordem de Complexidade ou Notação-O ou Big- Oh.
  • 29. Análise Assintótica Definição (Big-Oh): T(n) = O(f(n)) se existem constantes c e n0 tais que T(n)c.f(n) quando nn0. A definição indica que existe uma constante c que faz com que c.f(n) seja sempre pelo menos tão grande quanto T(n), desde que n seja maior que um n0. Em outras palavras: a Notação-O me fornece a Ordem de Complexidade ou a Taxa de Crescimento de uma função. Para isso, não consideramos os termos de ordem inferior da complexidade de um algoritmo, apenas o termo predominante.
  • 30. Análise Assintótica Exemplo: algoritmo com complexidade T(n) = 3n2 + 100n. Nesta função, o segundo termo tem um peso relativamente grande, mas a partir de n0 = 11, é o termo n2 que "dá o tom" do crescimento da função: uma parábola. A constante 3 também tem uma influência irrelevante sobre a taxa de crescimento da função após um certo tempo. Por isso dizemos que este algoritmo é da ordem de n2 ou que tem complexidade O(n2).
  • 31. Análise Assintótica Função Nome 1 Constante log n Logarítmica log2 n Log-quadrática n Linear n log n n log n n2 Quadrática n3 Cúbica 2n Exponencial
  • 32. Diferentes Tempos de Execução Problema da Subseqüência de Soma Máxima: dada uma seqüência de números a1, a2, ... , an, positivos ou negativos, encontre uma subseqüência aj, ..., ak dentro desta seqüência cuja soma seja máxima. A soma de uma seqüência contendo só números negativos é por definição 0. Exemplo: para a seqüência -2, 11, -4, 13, -5, -2, a resposta é 20 (a2,a3,a4). Este problema oferece um bom exemplo para o estudo de como diferentes algoritmos que resolvem o mesmo problema possuem diferentes comportamentos, pois para ele existem muitas soluções diferentes.
  • 33. Diferentes Tempos de Execução Algoritmo 1 2 3 4 Tempo O(n3) O(n2) O(n log n) O(n) n = 10 0,00103 0,00045 0,00066 0,00034 n = 100 0,47015 0,01112 0,00486 0,00063 n = 1.000 448,77 1,1233 0,05843 0,00333 n = 10.000 NA 111,13 0,68631 0,03042 n = 100.000 NA NA 8,0113 0,29832
  • 34. Cálculo da Complexidade de Tempo Um exemplo intuitivo inteiro somaCubos(inteiro n) inteiro i, somaParcial; início 1 somaParcial <- 0; 2 para i de 1 até n faça 3 somaParcial <- somaParcial + i * i * i; 4 fim para 5 retorne somaParcial; fim
  • 35. Cálculo da Complexidade de Tempo Análise: • as declarações não tomam tempo nenhum; • a linha 4 também não toma tempo nenhum; • as linhas 1 e 5 contam uma unidade de tempo cada; • a linha 3 conta 4 unidades de tempo (2 multiplicações, uma adição e uma atribuição) e é executada n vezes, contando com um total de 4n unidades de tempo; • a linha 2 possui custos implícitos de inicializar i, testar se é menor que n e incrementá-lo. Contamos 1 unidade para sua inicialização, n + 1 para todos os testes e n para todos os incrementos, o que perfaz 2n + 2 unidades de tempo; • o total perfaz 6n + 4 unidades de tempo, o que indica que o algoritmo é O(n), da Ordem de Complexidade n, ou seja, linear.
  • 36. Regras para o cálculo Laços: o tempo de execução de um laço é, no máximo, a soma dos tempos de execução de todas as instruções dentro do laço (incluindo todos os testes) multiplicado pelo número de iterações. Laços aninhados: analise-os de dentro para fora. O tempo total de execução de uma instrução dentro de um grupo de laços aninhados é o tempo de execução da instrução multiplicado pelo produto dos tamanhos de todos os laços. Exemplo: O(n2) para i de 1 até n faça para j de 1 até n faça k <- k + 1; fim para fim para
  • 37. Regras para o cálculo Instruções Consecutivas: estes simplesmente somam, sendo os termos de ordem menor da soma ignorados. Exemplo: O(n) + O(n2) = O(n2) para i de 1 até n faça a[i] <- 0; fim para para i de 1 até n faça para j de 1 até n faça a[i] <- a[j] + k + 1; fim para fim para
  • 38. Regras para o cálculo Se / Então / Senão: considere o fragmento de código abaixo. se cond então expresssão1 senão expressão2 fim se O tempo de execução de um comando Se / Então / Senão nunca é maior do que o tempo de execução do teste cond em si mais o tempo de execução da maior dentre as expressões expressão1 e expressão2. Ou seja: se expressão1 é O(n3) e expressão2 é O(n), então o teste é O(n3) + 1 = O(n3).
  • 39. Regras para o cálculo Chamada a Funções: segue a mesma regra dos laços aninhados - analise tudo de dentro para fora. Ou seja: para calcular a complexidade de um programa com várias funções, calcula- se primeiro a complexidade de cada uma das funções e depois considera-se cada uma das funções como uma instrução com a complexidade de função.
  • 40. Regras para o cálculo Recursão: é a parte mais difícil da análise de complexidade. Na verdade existem dois casos: muitos algoritmos recursivos mais simples podem ser "linearizados", substituindo-se a chamada recursiva por alguns laços aninhados ou por uma outra subrotina extra e eventualmente uma pilha para controlá-la. Nestes casos, o cálculo é simples e pode ser feito depois da "linearização". Em muitos algoritmos recursivos, porém, isto não é possível. Nestes casos obtemos uma relação de recorrência que tem de ser resolvida e é uma tarefa matemática menos trivial.
  • 41. Regras para o cálculo Exemplo de cálculo de complexidade em recursão: Fatorial inteiro Fatorial(inteiro n) início se n <= 1 então retorne 1 senão retorne (n * Fatorial (n - 1)); fim se fim
  • 42. Regras para o cálculo O exemplo anterior é realmente um exemplo pobre de recursão e pode ser "iterativisado" de forma extremamente simples com apenas um laço para-faça: inteiro FatorialIterativo(inteiro n) inteiro i, fatorial; início fatorial <- 1; para i de 2 até n faça fatorial <- fatorial * i; fim para retorne fatorial; fim A complexidade de FatorialIterativo pode então ser facilmente calculada e é evidente que é O(n).
  • 43. Regras para o cálculo O caso dos números de Fibonacci abaixo não é tão simples e requer a resolução de uma relação de recorrência: inteiro Fibonacci(inteiro n) início se n <= 1 então retorne 1 senão retorne (Fibonacci(n-1) + Fibonacci(n-2)); fim se fim Observando o algoritmo, vemos que para n >= 2 temos um tempo de execução T(n) = T(n-1)+T(n-2)+2. A resolução desta relação nos mostra que Fibonacci é O(()n).
  • 44. Logaritmos e outros Tempos de Execução O aspecto mais complexo na análise de complexidade centra-se em torno do logaritmo. Para analisar-se um algoritmo de complexidade logarítmica e chegar-se a um resultado correto sobre a sua ordem exata de complexidade é necessária uma certa experiência e algum "jeito" matemático. Algumas regras de aproximação podem ser dadas: algoritmos seguindo a técnica Dividir- Para-Conquistar são muitas vezes n log n.
  • 45. Logaritmos e outros Tempos de Execução • Quando um algoritmo, em uma passada de uma iteração toma o conjunto de dados e o divide em duas ou mais partes, sendo cada uma dessas partes processada separada e recursivamente, este algoritmo utiliza a técnica dividir-para-conquistar e será possivelmente n log n; • um algoritmo é log n se ele toma um tempo constante O(1) para dividir o tamanho do problema, usualmente pela metade; • a pesquisa binária é um exemplo de log n.
  • 46. Logaritmos e outros Tempos de Execução Se o algoritmo toma tempo constante para reduzir o tamanho do problema em um tamanho constante, ele será O(n). Algoritmos combinatórios são exponenciais: se um algoritmo testa todas as combinações de alguma coisa, ele será exponencial. Exemplo: Problema do Caixeiro Viajante
  • 47. Checando a sua análise Uma vez que a análise de complexidade tenha sido executada, é interessante verificar-se se a resposta está correta e é tão boa quanto possível. Uma forma de se fazer isto é o procedimento pouco matemático de se codificar o trecho de algoritmo cuja complexidade se tentou descrever e verificar se o tempo de execução coincide com o tempo previsto pela análise. Quando n dobra, o tempo de execução se eleva de um fator 2 para algoritmos lineares, fator 4 para quadráticos e fator 8 para cúbicos.
  • 48. Checando a sua análise Programas logarítmicos só aumentam o seu tempo de execução de uma constante a cada vez que n dobra. Algoritmos de O(n log n) tomam um pouco mais do que o dobro do tempo sob as mesmas circunstâncias. Estes aumentos podem ser difíceis de se detectar se os termos de ordem inferior têm coeficientes relativamente grandes e n não é grande o suficiente. Um exemplo é o pulo no tempo de execução de n=10 para n=100 em algumas implementações do problema da subseqüência de soma máxima.
  • 49. Checando a sua análise Distinguir programas n log n de programas lineares só em evidência de tempo de execução pode também ser muito difícil. Uma boa praxe é, caso o programa não seja exponencial (o que você vai descobrir muito rápido), fazer alguns experimentos com conjuntos maiores de dados de entrada.
  • 50. Exercício: cálculo de complexidade 1. Floyd Considere o grafo e sua matriz de custos D, abaixo:
  • 51. Exercício: cálculo de complexidade Pelo algoritmo de Floyd pode-se obter a Matriz de Custos A – com os comprimentos dos menores caminhos – e a Matriz de Roteamento R. Exemplo: custo e rota v4 -> v3 3 v4 v3 v4->v3 v1->v3 v2->v3 v4->v3 = v4->v1->v3->v2
  • 52. Exercício: cálculo de complexidade Agora, por exemplo, para determinar a rota de v2 para v1, toma-se R[2,1] = v5, R[5,1] = v4, R[4,1] = v1. Logo, a rota de v2 para v1 é: v2 → v5 → v4 → v1
  • 53. Exercício: cálculo de complexidade Algoritmo de Floyd – modificado • Considere a matriz de custos D[n,n] definida como no algoritmo de Floyd. O algoritmo produzirá uma matriz A[n,n], com comprimentos dos menores caminhos e ainda uma matriz R[n,n] que fornece o vértice k, que é o primeiro vértice a ser visitado no menor caminho de vi até vj.
  • 54. Exercício: cálculo de complexidade FloydModificado() início para i = 1 até n faça para j = 1 até n faça A[i,j] <- D[i,j]; R[i,j] <- 0; fim para fim para para i = 1 até n faça A[i,i] <- 0; fim para para k = 1 até n faça para i = 1 até n faça para j = 1 até n faça se A[i,k] + A[k,j] < A[i,j] então A[i,j] <- A[i,k] + A[k,j]; R[i,j] <- k; fim para fim para fim para fim
  • 55. Exercício: cálculo de complexidade 2. Torres de Hanoi 2.1. Considere o Problema das Torres de Hanoi com 3 Pinos na sua versão iterativa, como visto em aula: – É um programa que itera sempre sobre duas jogadas: a menor peça e outra possível de ser movida – Você viu que para 2 discos, houve 3 movimentações de disco e para 3 discos o algoritmo executou 7 movimentações. – Qual é essa tendência ? Será que esse comportamento continuará seguindo essa tendência? – Calcule a complexidade do algoritmo das torres de Hanoi
  • 56. Exercício: cálculo de complexidade 2. Torres de Hanoi 2.2. Considere o Problema das Torres de Hanoi implementado por você em aula: – Mantendo o número de pilhas (pinos) constante, você pode escolher um número arbitrário de discos. – Modifique o programa para poder mensurar o tempo de execução. • Use as funções ftime(), ctime(), difftime(). • Observe que difftime() só tem sentido se o programa rodar mais de um segundo, o que não acontecerá para valores baixos. – Rode o programa para 2, 3, 6, 7,10, 30, 50, 100, 200, 300, 500, 1000, 2000, 10.000, 50.000,100.000 e 500.000 discos.
  • 57. Exercício: cálculo de complexidade 2. Torres de Hanoi – Ao final o programa deverá chamar o programa unix gnuplot com o arquivo de tempos como parâmetro para que um gráfico dos tempos seja mostrado na tela. Para tanto esse arquivo deverá ser gerado na sintaxe do gnuplot, detalhada no manual do gnuplot.