Escolar Documentos
Profissional Documentos
Cultura Documentos
TOMÁS DE MOÇAMBIQUE
CURSO DE TECNOLOGIAS DE SISTEMAS DE
INFORMAÇÃO
Estrutura de Dados e Algoritmos
Ano 2007
Conteúdo
1
1. Conceito de Complexidade Algorítmica
Exemplo: Suponhamos que nos seja colocado um problema para encontrar o maior e o
menor elemento dum array com n elementos, e, que tenhamos como dado de entrada o
referido array.
Resolução:
Primeiro é necessário determinar as instâncias de execução do algoritmo. Neste caso
teremos:
2
Instâncias de Execução de Algoritmos:
Uma instância de um problema consiste de um conjunto de dados de entrada e saída
utilizados durante uma única execução.
Por exemplo, abaixo temos duas diferentes instâncias de execução do mesmo algoritmo,
cujo problema foi especificado acima.
3
Deste modo, passaremos a ilustrar a avaliação do algoritmo.
Algoritmos podem ser avaliados utilizando-se vários critérios. Geralmente o que interessa
é a taxa de crescimento ou espaço necessário para resolver instâncias cada vez maiores de
um problema. Podemos associar um problema a um valor chamado de ‘tamanho’ do
problema, que mede a quantidade de dados de entrada.
O tempo que um algoritmo necessita, expresso como uma função do tamanho do
problema, é chamado de complexidade temporal do algoritmo. O comportamento
assimptótico dos algoritmos (ou funções) representa o limite do comportamento de custo
quando o tamanho cresce. O comportamento assimptótico pode ser definido como o
comportamento de um algoritmo para grandes volumes de dados de entrada.
4
execução. Com base na observação, pode-se calcular o pior caso (a instância de
execução que levou mais tempo), o melhor caso (a instância de execução que
gastou menos tempo) e o caso médio (a média do tempo gasto em todas as
instâncias de execução).
O problema com esse tipo de avaliação é que o tempo gasto vai depender do
computador utilizado, do compilador, da linguagem de programação, etc.
5
Propriedades:
f(n) = O(f(n))
c. f(n) = O(f(n)), c=constante
O(O(f(n))) = O(f(n))
O(f(n)) + O(g(n)) = O(max(f(n),g(n))
O(f(n)) * O(g(n)) = O (f(n) * g(n))
6
Dentre as notações existentes, a notação O (O-grande) é a mais utilizada porque
geralmente o mais importante é descobrir o pior caso para saber se existe alguma
possibilidade de o algoritmo falhar, isto é, não conseguir executar caso entre com um
volume de dados demasiado grande.
Fazendo uma analogia entre as notações para melhor compreensão das notações, temos o
seguinte, considerando que f e g são funções que representam o comportamento de dois
algoritmos diferentes.
Algoritmo Complexidade
A1 n
A2 n log n
A3 n2
A4 n3
A5 2n
7
millisegundos. Enquanto o A1 consegue processar 1000 entradas em um segundo, o A5
só consegue processar 9 entradas em 1 segundo.
Função Nome
1 Constante
log n Logarítmica
log2 n Log quadrático
n Linear
n log n n log n
n2 Quadrática
n3 Cúbica
2n exponencial
Já vimos que o mais importante é tentar avaliar o tempo de execução para grandes
volumes de dados de entrada. Para isso, vamos tentar achar uma fórmula que expresse o
tempo de execução em função do volume de dados de entrada. Dependendo da fórmula
encontrada, podemos classificar e comparar a complexidade de diferentes algoritmos.
8
3.3 Regras para Análise de Algoritmos
Para diferentes tipos de instruções, existem regras específicas. Nos exemplos abaixo
ilustram-se diferentes casos:
1 int x=1;
2 x= (1+y);
3 System.Out.print(“Digite um número”);
Podemos facilmente notar que qualquer desses comandos, a não ser que esteja dentro de
um ciclo, será executado uma única vez. Ou seja, são comandos que não dependem do
volume de dados de entrada. Portanto, dizemos que esses comandos têm ordem de
complexidade constante ou O(1).
9
Agora multiplicamos esse valor pelo número de iterações do ciclo. Como o ciclo vai de 1
até 10, o número de iterações é 10. Portanto, temos: O(10) *O(2) = O(10*2) = O(20). Ou
seja, esse trecho de código vai demorar 20 unidades de tempo para ser executado. Como
o tempo é constante, isto é, independente do volume de dados de entrada, poderíamos
simplificar ainda mais e dizer que a complexidade é simplesmente O(1).
3. Ciclos aninhados
Devem ser analisados de dentro para fora. O tempo total de execução de uma instrução
dentro de um grupo de ciclos aninhados é o tempo de execução da instrução multiplicado
pelo produto do tamanho de todos os ciclos.
Exemplo:
1 for(i=1;i<n;i++)
2 {
3 for(j=1;j<n,j++)
4 {
5 k= k+1; O(1)
6 }
7 }
Neste caso, só temos um comando propriamente dito, que é constante e, portanto, tem
ordem O(1). Esse valor corresponde ao tempo total de execução dentro do grupo de
ciclos. Agora é necessário, portanto, multiplicá-lo pelo produto do tamanho de todos os
ciclos.
O primeiro ciclo será executado n vezes e, portanto, tem tamanho n. O mesmo vale para o
segundo ciclo, que também será executado n vezes. Portanto, temos:
O(n) * O(n) = O(n * n) = O(n2)
O(1) * O(n2) = O(1 *n2) = O(n2)
4. Instruções consecutivas
Simplesmente soma-se a complexidade de cada instrução, sendo que os termos de menor
ordem são ignorados.
10
Exemplo:
1 for(i=1;i<n; i++){
2 a[i] = 0;
3 }
4 for (i=1;i<n; i++) {
5 for(j = 1;j<n; j++) {
6 a[i] = a[j] + 1;
7 }
8 }
5. Ifs
Considere o seguinte exemplo:
1 if condição
2 {expressão1;}
3 else
4 {expressão2;}
11
4. Exercícios
1. Faça a análise algorítmica das instruções que se seguem e determine a eficiência de
cada código:
int i, j, k, count = 0;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
for (k = 0; k < n; k++)
count++;
________________________________________________________________________
Docentes regentes:
Rossana Abdul Carimo
Hamilton Isaias Mutaquiha
12