O documento introduz conceitos básicos de algoritmos, estruturas de dados e programas. Discute que algoritmos descrevem padrões de comportamento para solução de problemas e que estruturas de dados e algoritmos estão ligados na escolha de representações de dados. Também aborda tipos de dados, tipos abstratos de dados, análise da complexidade de algoritmos e medidas de tempo de execução.
Introdução aos algoritmos, estruturas de dados e programas
1. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.1 1
Algoritmos, Estruturas de Dados e
Programas
• Os algoritmos fazem parte do dia-a-dia das
pessoas. Exemplos de algoritmos:
– instruções para o uso de medicamentos,
– indicações de como montar um aparelho,
Introdução∗ – uma receita de culinária.
• Seqüência de ações executáveis para a
obtenção de uma solução para um
determinado tipo de problema.
• Segundo Dijkstra, um algoritmo corresponde
a uma descrição de um padrão de
comportamento, expresso em termos de um
conjunto finito de ações.
Última alteração: 1 de Abril de 2004 – Executando a operação a + b percebemos
um padrão de comportamento, mesmo
que a operação seja realizada para
valores diferentes de a e b.
∗ Transparências elaboradas por Charles Ornelas Almeida e Nivio Ziviani
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.1 2 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.1 3
Estruturas de dados Escolha da Representação dos Dados
• Estruturas de dados e algoritmos estão • A escolha da representação dos dados é
intimamente ligados: determinada, entre outras, pelas operações a
– não se pode estudar estruturas de dados serem realizadas sobre os dados.
sem considerar os algoritmos associados • Considere a operação de adição:
a elas,
– Para pequenos números, uma boa
– assim como a escolha dos algoritmos em representação é por meio de barras
geral depende da representação e da verticais (caso em que a operação de
estrutura dos dados. adição é bastante simples).
• Para resolver um problema é necessário – Já a representação por dígitos decimais
escolher uma abstração da realidade, em requer regras relativamente complicadas,
geral mediante a definição de um conjunto de as quais devem ser memorizadas.
dados que representa a situação real. – Entretanto, quando consideramos a adição
• A seguir, deve ser escolhida a forma de de grandes números é mais fácil a
representar esses dados. representação por dígitos decimais
(devido ao princípio baseado no peso
relativo da posição de cada dígito).
2. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.1 4 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.2 5
Programas Tipos de Dados
• Programar é basicamente estruturar dados e • Caracteriza o conjunto de valores a que uma
construir algoritmos. constante pertence, ou que podem ser
• Programas são formulações concretas de assumidos por uma variável ou expressão, ou
algoritmos abstratos, baseados em que podem ser gerados por uma função.
representações e estruturas específicas de
• Tipos simples de dados são grupos de
dados.
valores indivisíveis (como os tipos básicos
• Programas representam uma classe especial integer, boolean, char e real do Pascal).
de algoritmos capazes de serem seguidos por
– Exemplo: uma variável do tipo boolean
computadores.
pode assumir o valor verdadeiro ou o valor
• Um computador só é capaz de seguir falso, e nenhum outro valor.
programas em linguagem de máquina
• Os tipos estruturados em geral definem uma
(seqüência de instruções obscuras e
desconfortáveis). coleção de valores simples, ou um agregado
de valores de tipos diferentes.
• É necessário construir linguagens mais
adequadas, que facilitem a tarefa de
programar um computador.
• Uma linguagem de programação é uma
técnica de notação para programar, com a
intenção de servir de veículo tanto para a
expressão do raciocínio algorítmico quanto
para a execução automática de um algoritmo
por um computador.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.2 6 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.2 7
Tipos Abstratos de Dados (TAD’s) Implementação de TAD’s
• Modelo matemático, acompanhado das • Considere uma aplicação que utilize uma lista
operações definidas sobre o modelo. de inteiros. Poderíamos definir TAD Lista,
com as seguintes operações:
– Exemplo: o conjunto dos inteiros
acompanhado das operações de adição, 1. faça a lista vazia;
subtração e multiplicação. 2. obtenha o primeiro elemento da lista; se a lista
estiver vazia, então retorne nulo;
ˇ
• TAD’s sCo utilizados extensivamente como
3. insira um elemento na lista.
base para o projeto de algoritmos.
• Há várias opções de estruturas de dados que
• A implementação do algoritmo em uma
permitem uma implementação eficiente para
linguagem de programação específica exige a
˘ ˇ listas (por ex., o tipo estruturado arranjo).
representaGCo do TAD em termos dos tipos
de dados e dos operadores suportados. • Cada operação do tipo abstrato de dados é
implementada como um procedimento na
• A representação do modelo matemático por
linguagem de programação escolhida.
trás do tipo abstrato de dados é realizada
mediante uma estrutura de dados. • Qualquer alteração na implementação do
TAD fica restrita à parte encapsulada, sem
• Podemos considerar TAD’s como
causar impactos em outras partes do código.
generalizações de tipos primitivos e
procedimentos como generalizações de • Cada conjunto diferente de operações define
operações primitivas. um TAD diferente, mesmo atuem sob um
mesmo modelo matemático.
• O TAD encapsula tipos de dados. A definição
do tipo e todas as operações ficam • A escolha adequada de uma implementação
localizadas numa seção do programa. depende fortemente das operações a serem
realizadas sobre o modelo.
3. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 8 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 9
Medida do Tempo de Execução de um Tipos de Problemas na Análise de
Programa Algoritmos
• O projeto de algoritmos é fortemente • Análise de um algoritmo particular.
influenciado pelo estudo de seus – Qual é o custo de usar um dado algoritmo
comportamentos. para resolver um problema específico?
– Características que devem ser
• Depois que um problema é analisado e
investigadas:
decisões de projeto são finalizadas, é
∗ análise do número de vezes que cada
necessário estudar as várias opções de
parte do algoritmo deve ser executada,
algoritmos a serem utilizados, considerando
∗ estudo da quantidade de memória
os aspectos de tempo de execução e espaço
necessária.
ocupado.
• Análise de uma classe de algoritmos.
• Muitos desses algoritmos são encontrados
– Qual é o algoritmo de menor custo
em áreas como pesquisa operacional,
possível para resolver um problema
otimização, teoria dos grafos, estatística,
particular?
probabilidades, entre outras.
– Toda uma família de algoritmos é
investigada.
– Procura-se identificar um que seja o
melhor possível.
– Coloca-se limites para a complexidade
computacional dos algoritmos
pertencentes à classe.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 10 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 11
Custo de um Algoritmo Medida do Custo pela Execução do
Programa
• Determinando o menor custo possível para
resolver problemas de uma dada classe, • Tais medidas são bastante inadequadas e os
temos a medida da dificuldade inerente para resultados jamais devem ser generalizados:
resolver o problema. – os resultados são dependentes do
• Quando o custo de um algoritmo é igual ao compilador que pode favorecer algumas
menor custo possível, o algoritmo é ótimo construções em detrimento de outras;
para a medida de custo considerada. – os resultados dependem do hardware;
– quando grandes quantidades de memória
• Podem existir vários algoritmos para resolver
são utilizadas, as medidas de tempo
o mesmo problema.
podem depender deste aspecto.
• Se a mesma medida de custo é aplicada a
• Apesar disso, há argumentos a favor de se
diferentes algoritmos, então é possível
obterem medidas reais de tempo.
compará-los e escolher o mais adequado.
– Ex.: quando há vários algoritmos distintos
para resolver um mesmo tipo de problema,
todos com um custo de execução dentro
de uma mesma ordem de grandeza.
– Assim, são considerados tanto os custos
reais das operações como os custos não
aparentes, tais como alocação de
memória, indexação, carga, dentre outros.
4. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 12 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 13
Medida do Custo por meio de um Função de Complexidade
Modelo Matemático
• Para medir o custo de execução de um
• Usa um modelo matemático baseado em um algoritmo é comum definir uma função de
computador idealizado. custo ou função de complexidade f .
• Deve ser especificado o conjunto de • f (n) é a medida do tempo necessário para
operações e seus custos de execuções. executar um algoritmo para um problema de
tamanho n.
• É mais usual ignorar o custo de algumas das
operações e considerar apenas as operações • Função de complexidade de tempo: f (n)
mais significativas. mede o tempo necessário para executar um
algoritmo em um problema de tamanho n.
• Ex.: algoritmos de ordenação. Consideramos
o número de comparações entre os • Função de complexidade de espaço: f (n)
elementos do conjunto a ser ordenado e mede a memória necessária para executar
ignoramos as operações aritméticas, de um algoritmo em um problema de tamanho n.
atribuição e manipulações de índices, caso
• Utilizaremos f para denotar uma função de
existam.
complexidade de tempo daqui para a frente.
• A complexidade de tempo na realidade não
representa tempo diretamente, mas o número
de vezes que determinada operação
considerada relevante é executada.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 14 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 15
Exemplo - Maior Elemento Exemplo - Maior Elemento
• Considere o algoritmo para encontrar o maior • Teorema: Qualquer algoritmo para encontrar
elemento de um vetor de inteiros o maior elemento de um conjunto com n
A[1..n], n ≥ 1. elementos, n ≥ 1, faz pelo menos n − 1
comparações.
function Max ( var A: Vetor ) : integer ;
var i , Temp: integer ; • Prova: Cada um dos n − 1 elementos tem de
begin ser mostrado, por meio de comparações, que
Temp : = A[ 1 ] ; é menor do que algum outro elemento.
for i := 2 to n do i f Temp < A[ i ] then Temp : = A[ i ] ;
Max : = Temp; • Logo n − 1 comparações são necessárias. 2
end;
• O teorema acima nos diz que, se o número
de comparações for utilizado como medida de
• Seja f uma função de complexidade tal que
custo, então a função Max do programa
f (n) é o número de comparações entre os
anterior é ótima.
elementos de A, se A contiver n elementos.
• Logo f (n) = n − 1, para n > 0.
• Vamos provar que o algoritmo apresentado
no programa acima é ótimo.
5. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 16 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 17
Tamanho da Entrada de Dados Melhor Caso, Pior Caso e Caso Médio
• A medida do custo de execução de um • Melhor caso: menor tempo de execução
algoritmo depende principalmente do sobre todas as entradas de tamanho n.
tamanho da entrada dos dados. • Pior caso: maior tempo de execução sobre
todas as entradas de tamanho n.
• É comum considerar o tempo de execução de
um programa como uma função do tamanho • Se f é uma função de complexidade baseada
da entrada. na análise de pior caso, o custo de aplicar o
algoritmo nunca é maior do que f (n).
• Para alguns algoritmos, o custo de execução
é uma função da entrada particular dos • Caso médio (ou caso esperado): média dos
dados, não apenas do tamanho da entrada. tempos de execução de todas as entradas de
tamanho n.
• No caso da função Max do programa do
• Na análise do caso esperado, supõe-se uma
exemplo, o custo é uniforme sobre todos os
distribuição de probabilidades sobre o
problemas de tamanho n.
conjunto de entradas de tamanho n e o custo
• Já para um algoritmo de ordenação isso não médio é obtido com base nessa distribuição.
ocorre: se os dados de entrada já estiverem • A análise do caso médio é geralmente muito
quase ordenados, então o algoritmo pode ter mais difícil de obter do que as análises do
que trabalhar menos. melhor e do pior caso.
• É comum supor uma distribuição de
probabilidades em que todas as entradas
possíveis são igualmente prováveis.
• Na prática isso nem sempre é verdade.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 18 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 19
Exemplo - Registros de um Arquivo Exemplo - Registros de um Arquivo
• Considere o problema de acessar os • No estudo do caso médio, vamos considerar
registros de um arquivo. que toda pesquisa recupera um registro.
• Cada registro contém uma chave única que é • Se pi for a probabilidade de que o i-ésimo
utilizada para recuperar registros do arquivo. registro seja procurado, e considerando que
para recuperar o i-ésimo registro são
• O problema: dada uma chave qualquer,
necessárias i comparações, então
localize o registro que contenha esta chave.
f (n) = 1 × p1 + 2 × p2 + 3 × p3 + · · · + n × pn .
• O algoritmo de pesquisa mais simples é o
que faz a pesquisa seqüencial. • Para calcular f (n) basta conhecer a
distribuição de probabilidades pi .
• Seja f uma função de complexidade tal que
f (n) é o número de registros consultados no • Se cada registro tiver a mesma probabilidade
arquivo (número de vezes que a chave de de ser acessado que todos os outros, então
consulta é comparada com a chave de cada pi = 1/n, 1 ≤ i ≤ n.
registro). • Neste caso
1 1 n(n+1) n+1
– melhor caso: f (n) = 1 (registro procurado f (n) = n (1+2+3+· · ·+n) = n 2
= 2
·
é o primeiro consultado);
• A análise do caso esperado revela que uma
– pior caso: f (n) = n (registro procurado é o pesquisa com sucesso examina
último consultado ou não está presente no aproximadamente metade dos registros.
arquivo);
– caso médio: f (n) = (n + 1)/2.
7. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 24 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 25
Exemplo - Maior e Menor Elemento (3) Comparação entre os Algoritmos
MaxMin1, MaxMin2 e MaxMin3
• Os elementos de A são comparados dois a
dois e os elementos maiores são comparados • A tabela apresenta uma comparação entre os
com Max e os elementos menores são algoritmos dos programas MaxMin1, MaxMin2
comparados com Min. e MaxMin3, considerando o número de
comparações como medida de complexidade.
• Quando n é ímpar, o elemento que está na
posição A[n] é duplicado na posição A[n + 1] • Os algoritmos MaxMin2 e MaxMin3 são
para evitar um tratamento de exceção. superiores ao algoritmo MaxMin1 de forma
geral.
• Para esta implementação,
f (n) = n + n−2 + n−2 = 3n − 2, para n 0,
2 2 2 2
• O algoritmo MaxMin3 é superior ao algoritmo
para o melhor caso, pior caso e caso médio. MaxMin2 com relação ao pior caso e bastante
próximo quanto ao caso médio.
Os três f (n)
algoritmos Melhor caso Pior caso Caso médio
MaxMin1 2(n − 1) 2(n − 1) 2(n − 1)
MaxMin2 n−1 2(n − 1) 3n/2 − 3/2
MaxMin3 3n/2 − 2 3n/2 − 2 3n/2 − 2
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 26 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 27
Limite Inferior - Uso de um Oráculo Exemplo de Uso de um Oráculo
• Existe possibilidade de obter um algoritmo • Teorema: Qualquer algoritmo para encontrar
MaxMin mais eficiente? o maior e o menor elemento de um conjunto
com n elementos não ordenados, n ≥ 1, faz
• Para responder temos de conhecer o limite
pelo menos 3n/2 − 2 comparações.
inferior para essa classe de algoritmos.
• Prova: A técnica utilizada define um oráculo
• Técnica muito utilizada: uso de um oráculo. que descreve o comportamento do algoritmo
• Dado um modelo de computação que por meio de um conjunto de n–tuplas, mais
expresse o comportamento do algoritmo, o um conjunto de regras associadas que
oráculo informa o resultado de cada passo mostram as tuplas possíveis (estados) que
possível (no caso, o resultado de cada um algoritmo pode assumir a partir de uma
comparação). dada tupla e uma única comparação.
• Para derivar o limite inferior, o oráculo procura • Uma 4–tupla, representada por (a, b, c, d),
sempre fazer com que o algoritmo trabalhe o onde os elementos de:
máximo, escolhendo como resultado da – a → nunca foram comparados;
próxima comparação aquele que cause o – b → foram vencedores e nunca perderam
maior trabalho possível necessário para em comparações realizadas;
determinar a resposta final. – c → foram perdedores e nunca venceram
em comparações realizadas;
– d → foram vencedores e perdedores em
comparações realizadas.
8. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 28 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3 29
Exemplo de Uso de um Oráculo Exemplo de Uso de um Oráculo
• O algoritmo inicia no estado (n, 0, 0, 0) e • A seguir, para reduzir o componente b até um
termina com (0, 1, 1, n − 2). são necessárias n/2 − 1 mudanças de
• Após cada comparação a tupla (a, b, c, d) estado (mínimo de comparações necessárias
consegue progredir apenas se ela assume para obter o maior elemento de b).
um dentre os seis estados possíveis abaixo:
• Idem para c, com n/2 − 1 mudanças de
– (a − 2, b + 1, c + 1, d) se a ≥ 2 (dois estado.
elementos de a são comparados)
• Logo, para obter o estado (0, 1, 1, n − 2) a
– (a − 1, b + 1, c, d) ou (a − 1, b, c + 1, d) ou
(a − 1, b, c, d + 1) se a ≥ 1 (um elemento de partir do estado (n, 0, 0, 0) são necessárias
a comparado com um de b ou um de c) n/2 + n/2 − 1 + n/2 − 1 = 3n/2 − 2
– (a, b − 1, c, d + 1) se b ≥ 2 (dois elementos
de b são comparados) comparações. 2
– (a, b, c − 1, d + 1) se c ≥ 2 (dois elementos • O teorema nos diz que se o número de
de c são comparados) comparações entre os elementos de um vetor
– O primeiro passo requer necessariamente for utilizado como medida de custo, então o
a manipulação do componente a. algoritmo MaxMin3 é ótimo.
– O caminho mais rápido para levar a até
zero requer n/2 mudanças de estado e
termina com a tupla (0, n/2, n/2, 0) (por
meio de comparação dos elementos de a
dois a dois).
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 30 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 31
Comportamento Assintótico de Dominação assintótica
Funções
• A análise de um algoritmo geralmente conta
• O parâmetro n fornece uma medida da com apenas algumas operações elementares.
dificuldade para se resolver o problema. • A medida de custo ou medida de
complexidade relata o crescimento assintótico
• Para valores suficientemente pequenos de n,
da operação considerada.
qualquer algoritmo custa pouco para ser
executado, mesmo os ineficientes. • Definição: Uma função f (n) domina
assintoticamente outra função g(n) se
• A escolha do algoritmo não é um problema
existem duas constantes positivas c e m tais
crítico para problemas de tamanho pequeno. que, para n ≥ m, temos |g(n)| ≤ c × |f (n)|.
• Logo, a análise de algoritmos é realizada para f,g
c f (n)
valores grandes de n.
g(n)
• Estuda-se o comportamento assintótico das
funções de custo (comportamento de suas
n
funções de custo para valores grandes de n) m
• O comportamento assintótico de f (n) Exemplo:
representa o limite do comportamento do • Sejam g(n) = (n + 1)2 e f (n) = n2 .
custo quando n cresce.
• As funções g(n) e f (n) dominam
assintoticamente uma a outra, desde que
|(n + 1)2 | ≤ 4|n2 | para n ≥ 1 e |n2 | ≤ |(n + 1)2 |
para n ≥ 0.
9. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 32 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 33
Notação O Exemplos de Notação O
• Escrevemos g(n) = O(f (n)) para expressar • Exemplo: g(n) = (n + 1)2 .
que f (n) domina assintoticamente g(n). Lê-se
– Logo g(n) é O(n2 ), quando m = 1 e c = 4.
g(n) é da ordem no máximo f (n).
– Isto porque (n + 1)2 ≤ 4n2 para n ≥ 1.
• Exemplo: quando dizemos que o tempo de
execução T (n) de um programa é O(n2 ), • Exemplo: g(n) = n e f (n) = n2 .
significa que existem constantes c e m tais – Sabemos que g(n) é O(n2 ), pois para
que, para valores de n ≥ m, T (n) ≤ cn2 . n ≥ 0, n ≤ n2 .
• Exemplo gráfico de dominação assintótica – Entretanto f (n) não é O(n).
que ilustra a notação O. – Suponha que existam constantes c e m
f,g
c f (n) tais que para todo n ≥ m, n2 ≤ cn.
g(n) – Logo c ≥ n para qualquer n ≥ m, e não
existe uma constante c que possa ser
maior ou igual a n para todo n.
n
m
O valor da constante m mostrado é o menor
valor possível, mas qualquer valor maior
também é válido.
• Definição: Uma função g(n) é O(f (n)) se
existem duas constantes positivas c e m tais
que g(n) ≤ cf (n), para todo n ≥ m.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 34 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 35
Exemplos de Notação O Operações com a Notação O
• Exemplo: g(n) = 3n3 + 2n2 + n é O(n3 ). f (n) = O(f (n))
3 2 3
– Basta mostrar que 3n + 2n + n ≤ 6n , c × O(f (n)) = O(f (n)) c = constante
para n ≥ 0. O(f (n)) + O(f (n)) = O(f (n))
– A função g(n) = 3n3 + 2n2 + n é também O(O(f (n)) = O(f (n))
O(n4 ), entretanto esta afirmação é mais
O(f (n)) + O(g(n)) = O(max(f (n), g(n)))
fraca do que dizer que g(n) é O(n3 ).
O(f (n))O(g(n)) = O(f (n)g(n))
• Exemplo: g(n) = log5 n é O(log n). f (n)O(g(n)) = O(f (n)g(n))
– O logb n difere do logc n por uma constante
que no caso é logb c. Exemplo: regra da soma O(f (n)) + O(g(n)).
logc n
– Como n = c , tomando o logaritmo
• Suponha três trechos cujos tempos de
base b em ambos os lados da igualdade,
execução são O(n), O(n2 ) e O(n log n).
temos que
logb n = logb clogc n = logc n × logb c. • O tempo de execução dos dois primeiros
trechos é O(max(n, n2 )), que é O(n2 ).
• O tempo de execução de todos os três
trechos é então O(max(n2 , n log n)), que é
O(n2 ).
Exemplo: O produto de [log n + k + O(1/n)] por
√ √
[n + O( n)] é n log n + kn + O( n log n).
10. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 36 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 37
Notação Ω Notação Θ
• Especifica um limite inferior para g(n). • Definição: Uma função g(n) é Θ(f (n)) se
• Definição: Uma função g(n) é Ω(f (n)) se existirem constantes positivas c1 , c2 e m tais
existirem duas constantes c e m tais que que 0 ≤ c1 f (n) ≤ g(n) ≤ c2 f (n), para todo
g(n) ≥ cf (n), para todo n ≥ m. n ≥ m.
• Exemplo: Para mostrar que g(n) = 3n3 + 2n2 • Exemplo gráfico para a notação Θ
é Ω(n3 ) basta fazer c = 1, e então f,g
c2 f ( n )
3n3 + 2n2 ≥ n3 para n ≥ 0.
g(n)
• Exemplo: Seja g(n) = n para n ímpar (n ≥ 1)
c1 f ( n )
e g(n) = n2 /10 para n par (n ≥ 0).
– Neste caso g(n) é Ω(n2 ), bastando n
m
considerar c = 1/10 e n = 0, 2, 4, 6, . . .
• Dizemos que g(n) = Θ(f (n)) se existirem
• Exemplo gráfico para a notação Ω constantes c1 , c2 e m tais que, para todo
f,g n ≥ m, o valor de g(n) está sobre ou acima de
g(n)
c1 f (n) e sobre ou abaixo de c2 f (n).
c f (n)
• Isto é, para todo n ≥ m, a função g(n) é igual
a f (n) a menos de uma constante.
n
m
• Neste caso, f (n) é um limite assintótico
• Para todos os valores à direita de m, o valor firme.
de g(n) está sobre ou acima do valor de cf (n).
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 38 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 39
Exemplo de Notação Θ Notação o
• Seja g(n) = n2 /3 − 2n. • Usada para definir um limite superior que não
é assintoticamente firme.
• Vamos mostrar que g(n) = Θ(n2 ).
• Definição: Uma função g(n) é o(f (n)) se,
• Temos de obter constantes c1 , c2 e m tais que
para qualquer constante c 0, então
c1 n2 ≤ 1 n2 − 2n ≤ c2 n2 para todo n ≥ m.
3
0 ≤ g(n) cf (n) para todo n ≥ m.
1 2
• Dividindo por n2 leva a c1 ≤ 3
− n
≤ c2 .
• Exemplo: 2n = o(n2 ), mas 2n2 = o(n2 ).
• O lado direito da desigualdade será sempre
• Em g(n) = O(f (n)), a expressão
válido para qualquer valor de n ≥ 1 quando
0 ≤ g(n) ≤ cf (n) é válida para alguma
escolhemos c2 ≥ 1/3.
constante c 0, mas em g(n) = o(f (n)), a
• Escolhendo c1 ≤ 1/21, o lado esquerdo da expressão 0 ≤ g(n) cf (n) é válida para
desigualdade será válido para qualquer valor todas as constantes c 0.
de n ≥ 7.
• Na notação o, a função g(n) tem um
• Logo, escolhendo c1 = 1/21, c2 = 1/3 e m = 7, crescimento muito menor que f (n) quando n
verifica-se que n2 /3 − 2n = Θ(n2 ). tende para infinito.
g(n)
• Outras constantes podem existir, mas o • Alguns autores usam limn→∞ f (n)
= 0 para a
importante é que existe alguma escolha para definição da notação o.
as três constantes.
11. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.1 40 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 41
Notação ω Classes de Comportamento
Assintótico
• Por analogia, a notação ω está relacionada
com a notação Ω da mesma forma que a • Se f é uma função de complexidade para
notação o está relacionada com a notação O. um algoritmo F , então O(f ) é considerada a
complexidade assintótica ou o
• Definição: Uma função g(n) é ω(f (n)) se, comportamento assintótico do algoritmo F .
para qualquer constante c 0, então
• A relação de dominação assintótica permite
0 ≤ cf (n) g(n) para todo n ≥ m.
comparar funções de complexidade.
n2 n2
• Exemplo: 2
= ω(n), mas 2
= ω(n2 ).
• Entretanto, se as funções f e g dominam
• A relação g(n) = ω(f (n)) implica assintoticamente uma a outra, então os
g(n)
limn→∞ f (n) = ∞, se o limite existir. algoritmos associados são equivalentes.
• Nestes casos, o comportamento assintótico
não serve para comparar os algoritmos.
• Por exemplo, considere dois algoritmos F e G
aplicados à mesma classe de problemas,
sendo que F leva três vezes o tempo de G ao
serem executados, isto é, f (n) = 3g(n), sendo
que O(f (n)) = O(g(n)).
• Logo, o comportamento assintótico não serve
para comparar os algoritmos F e G, porque
eles diferem apenas por uma constante.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 42 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 43
Comparação de Programas Principais Classes de Problemas
• Podemos avaliar programas comparando as • f (n) = O(1).
funções de complexidade, negligenciando as – Algoritmos de complexidade O(1) são
constantes de proporcionalidade. ditos de complexidade constante.
– Uso do algoritmo independe de n.
• Um programa com tempo de execução O(n) é
melhor que outro com tempo O(n2 ). – As instruções do algoritmo são executadas
um número fixo de vezes.
• Porém, as constantes de proporcionalidade
podem alterar esta consideração. • f (n) = O(log n).
– Um algoritmo de complexidade O(log n) é
• Exemplo: um programa leva 100n unidades dito ter complexidade logarítmica.
de tempo para ser executado e outro leva 2n2 .
– Típico em algoritmos que transformam um
Qual dos dois programas é melhor?
problema em outros menores.
– depende do tamanho do problema.
– Pode-se considerar o tempo de execução
– Para n 50, o programa com tempo 2n2 é como menor que uma constante grande.
melhor do que o que possúi tempo 100n. – Quando n é mil, log2 n ≈ 10, quando n é 1
– Para problemas com entrada de dados milhão, log2 n ≈ 20.
pequena é preferível usar o programa cujo – Para dobrar o valor de log n temos de
tempo de execução é O(n2 ). considerar o quadrado de n.
– Entretanto, quando n cresce, o programa – A base do logaritmo muda pouco estes
com tempo de execução O(n2 ) leva muito valores: quando n é 1 milhão, o log 2 n é 20
mais tempo que o programa O(n). e o log10 n é 6.
12. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 44 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 45
Principais Classes de Problemas Principais Classes de Problemas
• f (n) = O(n). • f (n) = O(n2 ).
– Um algoritmo de complexidade O(n) é dito – Um algoritmo de complexidade O(n2 ) é
ter complexidade linear. dito ter complexidade quadrática.
– Ocorrem quando os itens de dados são
– Em geral, um pequeno trabalho é
processados aos pares, muitas vezes em
realizado sobre cada elemento de entrada.
um anel dentro de outro.
– É a melhor situação possível para um
– Quando n é mil, o número de operações é
algoritmo que tem de processar/produzir n
da ordem de 1 milhão.
elementos de entrada/saída.
– Sempre que n dobra, o tempo de
– Cada vez que n dobra de tamanho, o execução é multiplicado por 4.
tempo de execução dobra.
– Úteis para resolver problemas de
• f (n) = O(n log n). tamanhos relativamente pequenos.
– Típico em algoritmos que quebram um • f (n) = O(n3 ).
problema em outros menores, resolvem – Um algoritmo de complexidade O(n3 ) é
cada um deles independentemente e dito ter complexidade cúbica.
ajuntando as soluções depois. – Úteis apenas para resolver pequenos
– Quando n é 1 milhão, nlog2 n é cerca de 20 problemas.
milhões. – Quando n é 100, o número de operações
– Quando n é 2 milhões, nlog2 n é cerca de é da ordem de 1 milhão.
42 milhões, pouco mais do que o dobro. – Sempre que n dobra, o tempo de
execução fica multiplicado por 8.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 46 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 47
Principais Classes de Problemas Comparação de Funções de
Complexidade
• f (n) = O(2n ).
Função Tamanho n
– Um algoritmo de complexidade O(2n ) é de custo 10 20 30 40 50 60
dito ter complexidade exponencial.
n 0,00001 0,00002 0,00003 0,00004 0,00005 0,00006
– Geralmente não são úteis sob o ponto de s s s s s s
vista prático. n2 0,0001 0,0004 0,0009 0,0016 0,0.35 0,0036
s s s s s s
– Ocorrem na solução de problemas quando
n3 0,001 0,008 0,027 0,64 0,125 0.316
se usa força bruta para resolvê-los. s s s s s s
– Quando n é 20, o tempo de execução é n5 0,1 3,2 24,3 1,7 5,2 13
s s s min min min
cerca de 1 milhão. Quando n dobra, o
0,001 1 17,9 12,7 35,7 366
tempo fica elevado ao quadrado. 2n
s s min dias anos séc.
• f (n) = O(n!). 3n 0,059 58 6,5 3855 108 1013
s min anos séc. séc. séc.
– Um algoritmo de complexidade O(n!) é
dito ter complexidade exponencial, apesar
Função de Computador Computador Computador
de O(n!) ter comportamento muito pior do
custo atual 100 vezes 1.000 vezes
que O(2n ).
de tempo mais rápido mais rápido
– Geralmente ocorrem quando se usa força
n t1 100 t1 1000 t1
bruta para na solução do problema.
n2 t2 10 t2 31, 6 t2
– n = 20 → 20! = 2432902008176640000, um 3
n t3 4, 6 t3 10 t3
número com 19 dígitos.
2 n
t4 t4 + 6, 6 t4 + 10
– n = 40 → um número com 48 dígitos.
13. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 48 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 49
Algoritmos Polinomiais Algoritmos Polinomiais × Algoritmos
Exponenciais
• Algoritmo exponencial no tempo de
execução tem função de complexidade • A distinção entre algoritmos polinomiais
O(cn ), c 1. eficientes e algoritmos exponenciais
• Algoritmo polinomial no tempo de execução ineficientes possui várias exceções.
tem função de complexidade O(p(n)), onde
• Exemplo: um algoritmo com função de
p(n) é um polinômio.
complexidade f (n) = 2n é mais rápido que
• A distinção entre estes dois tipos de um algoritmo g(n) = n5 para valores de n
algoritmos torna-se significativa quando o menores ou iguais a 20.
tamanho do problema a ser resolvido cresce.
• Também existem algoritmos exponenciais que
• Por isso, os algoritmos polinomiais são muito são muito úteis na prática.
mais úteis na prática do que os exponenciais.
• Exemplo: o algoritmo Simplex para
• Algoritmos exponenciais são geralmente
programação linear possui complexidade de
simples variações de pesquisa exaustiva.
tempo exponencial para o pior caso mas
• Algoritmos polinomiais são geralmente executa muito rápido na prática.
obtidos mediante entendimento mais
profundo da estrutura do problema. • Tais exemplos não ocorrem com freqüência
na prática, e muitos algoritmos exponenciais
• Um problema é considerado:
conhecidos não são muito úteis.
– intratável: se não existe um algoritmo
polinomial para resolvê-lo.
– bem resolvido: quando existe um
algoritmo polinomial para resolvê-lo.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 50 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.3.2 51
Exemplo de Algoritmo Exponencial Exemplo de Algoritmo Exponencial
• Um caixeiro viajante deseja visitar n cidades • Um algoritmo simples seria verificar todas as
de tal forma que sua viagem inicie e termine rotas e escolher a menor delas.
em uma mesma cidade, e cada cidade deve
• Há (n − 1)! rotas possíveis e a distância total
ser visitada uma única vez.
percorrida em cada rota envolve n adições,
• Supondo que sempre há uma estrada entre logo o número total de adições é n!.
duas cidades quaisquer, o problema é
• No exemplo anterior teríamos 24 adições.
encontrar a menor rota para a viagem.
• Suponha agora 50 cidades: o número de
• A figura ilustra o exemplo para quatro cidades
adições seria 50! ≈ 1064 .
c1 , c2 , c3 , c4 , em que os números nos arcos
indicam a distância entre duas cidades. • Em um computador que executa 109 adições
c1 por segundo, o tempo total para resolver o
problema com 50 cidades seria maior do que
9 4 8 1045 séculos só para executar as adições.
5 c3 3
• O problema do caixeiro viajante aparece com
c2 c4
8 freqüência em problemas relacionados com
transporte, mas também aplicações
• O percurso c1 , c3 , c4 , c2 , c1 é uma solução
importantes relacionadas com otimização de
para o problema, cujo percurso total tem
caminho percorrido.
distância 24.
14. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 52 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 53
Técnicas de Análise de Algoritmos Análise do Tempo de Execução
• Determinar o tempo de execução de um • Comando de atribuição, de leitura ou de
programa pode ser um problema matemático escrita: O(1).
complexo; • Seqüência de comandos: determinado pelo
maior tempo de execução de qualquer
• Determinar a ordem do tempo de execução,
comando da seqüência.
sem preocupação com o valor da constante
envolvida, pode ser uma tarefa mais simples. • Comando de decisão: tempo dos comandos
dentro do comando condicional, mais tempo
• A análise utiliza técnicas de matemática
para avaliar a condição, que é O(1).
discreta, envolvendo contagem ou
enumeração dos elementos de um conjunto: • Anel: soma do tempo de execução do corpo
do anel mais o tempo de avaliar a condição
– manipulação de somas,
para terminação (geralmente O(1)),
– produtos, multiplicado pelo número de iterações.
– permutações, • Procedimentos não recursivos: cada um
– fatoriais, deve ser computado separadamente um a
– coeficientes binomiais, um, iniciando com os que não chamam outros
procedimentos. Avalia-se então os que são
– solução de equações de recorrência.
chamam os já avaliados (utilizando os tempos
desses). O processo é repetido até chegar no
programa principal.
• Procedimentos recursivos: associada uma
função de complexidade f (n) desconhecida,
onde n mede o tamanho dos argumentos.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 54 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 55
Procedimento não Recursivo Análise do Procedimento não
Recursivo
Algoritmo para ordenar os n elementos de um
conjunto A em ordem ascendente. Anel Interno
procedure Ordena ( var A: Vetor ) ; • Contém um comando de decisão, com um
var i , j , min, x : integer ;
comando apenas de atribuição. Ambos levam
begin
tempo constante para serem executados.
(1) for i := 1 to n−1 do
begin • Quanto ao corpo do comando de decisão,
(2) min : = i ; devemos considerar o pior caso, assumindo
(3) for j := i +1 to n do
que serSS sempre executado.
(4) i f A[ j ] A[min]
(5) then min : = j ; • O tempo para incrementar o índice do anel e
{ troca A[min ] e A[ i ] } avaliar sua condição de terminação é O(1).
(6) x : = A[min ] ;
(7) A[min] : = A[ i ] ; • O tempo combinado para executar uma vez o
(8) A[ i ] : = x ; anel é O(max(1, 1, 1)) = O(1), conforme regra
end; da soma para a notação O.
end;
• Como o número de iterações é n − i, o tempo
• Seleciona o menor elemento do conjunto. gasto no anel é O((n − i) × 1) = O(n − i),
conforme regra do produto para a notação O.
• Troca este com o primeiro elemento A[1].
• Repita as duas operações acima com os
n − 1 elementos restantes, depois com os
n − 2, até que reste apenas um.
15. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 56 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 57
Análise do Procedimento não Procedimento Recursivo
Recursivo
Pesquisa(n) ;
Anel Externo (1) i f n ≤ 1
(2) then ‘ inspecione elemento ’ e termine
• Contém, além do anel interno, quatro
else begin
comandos de atribuição. (3) para cada um dos n elementos ‘ inspecione elemento ’ ;
O(max(1, (n − i), 1, 1, 1)) = O(n − i). (4) Pesquisa(n/ 3 ) ;
end;
• A linha (1) é executada n − 1 vezes, e o
tempo total para executar o programa está
limitado ao produto de uma constante pelo • Para cada procedimento recursivo é
somatório de (n − i): associada uma função de complexidade f (n)
n−1 2
(n − i) = n(n−1) = n − n = O(n2 ) desconhecida, onde n mede o tamanho dos
1 2 2 2
argumentos para o procedimento.
• Considerarmos o número de comparações
como a medida de custo relevante, o • Obtemos uma equação de recorrência para
programa faz (n2 )/2 − n/2 comparações para f (n).
ordenar n elementos. • Equação de recorrência: maneira de definir
• Considerarmos o número de trocas, o uma função por uma expressão envolvendo a
programa realiza exatamente n − 1 trocas. mesma função.
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 58 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 59
Análise do Procedimento Recursivo Exemplo de Resolução de Equação de
Recorrência
• Seja T (n) uma função de complexidade que
represente o número de inspeções nos n • Sustitui-se os termos T (k), k n, até que
elementos do conjunto. todos os termos T (k), k 1, tenham sido
substituídos por fórmulas contendo apenas
• O custo de execução das linhas (1) e (2) é
T (1).
O(1) e o da linha (3) é exatamente n.
• Usa-se uma equação de recorrência para T (n) = n + T (n/3)
determinar o no de chamadas recursivas. T (n/3) = n/3 + T (n/3/3)
T (n/3/3) = n/3/3 + T (n/3/3/3)
• O termo T (n) é especificado em função dos
.
. .
.
termos anteriores T (1), T (2), . . ., T (n − 1). . .
T (n/3/3 · · · /3) = n/3/3 · · · /3 + T (n/3 · · · /3)
• T (n) = n + T (n/3), T (1) = 1 (para n = 1
fazemos uma inspeção)
• Adicionando lado a lado, temos
• Por exemplo, T (3) = T (3/3) + 3 = 4, T (n) = n + n · (1/3) + n · (1/32 ) + n · (1/33 ) +
T (9) = T (9/3) + 9 = 13, e assim por diante. · · · + (n/3/3 · · · /3) que representa a soma de
uma série geométrica de razão 1/3,
• Para calcular o valor da função seguindo a
multiplicada por n, e adicionada de
definição são necessários k − 1 passos para
T (n/3/3 · · · /3), que é menor ou igual a 1.
computar o valor de T (3k ).
16. Projeto de Algoritmos – Cap.1 Introdução – Seção 1.4 60 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.5 61
Exemplo de Resolução de Equação de A Linguagem de Programação Pascal
Recorrência
• Os programas apresentados no livro usam
2
T (n) = n + n · (1/3) + n · (1/3 ) + n · (1/3 ) + · · · + 3 apenas as características básicas do Pascal.
+ (n/3/3 · · · /3) • São evitadas as facilidades mais avançadas
• Se desprezarmos o termo T (n/3/3 · · · /3), disponíveis em algumas implementações.
quando n tende para infinito, então • Não apresentaremos a linguagem na sua
T (n) = n i=0 (1/3)
∞ i
=n 1
1 = 3n
· totalidade, apenas examinamos algumas
1− 3 2
características.
• Se considerarmos o termo T (n/3/3/3 · · · /3) e
• As várias partes componentes de um
denominarmos x o número de subdivisões
programa Pascal são:
por 3 do tamanho do problema, então
n/3x = 1, e n = 3x . Logo x = log3 n. program cabeçalho do programa
• Lembrando que T (1) = 1 temos label declaração de rótulo para goto
T (n) = x−1 3i + T ( 3n ) = n x−1 (1/3)i + 1 =
i=0
n
x i=0 const definição de constantes
n(1−( 1 )x ) 3n 1
3
1
(1− 3 )
+1= 2
− 2
· type definição de tipos de dados
var declaração de variáveis
• Logo, o programa do exemplo é O(n).
procedure declaração de subprogramas
ou function
begin
.
.
. comandos do programa
end
Projeto de Algoritmos – Cap.1 Introdução – Seção 1.5 62 Projeto de Algoritmos – Cap.1 Introdução – Seção 1.5 63
Tipos em Pascal Tipos Simples
• Regra geral: tornar explícito o tipo associado • São grupos de valores indivisíveis (integer,
quando se declara uma constante, variável ou boolean, char e real).
função.
• Tipos simples adicionais podem ser
• Isso permite testes de consistência durante o enumerados por meio de:
tempo de compilação. – listagem de novos grupos de valores;
Exemplo:
• A definição de tipos permite ao programador
alterar o nome de tipos existentes e criar um type cor = (vermelho, azul, rosa);
.
.
número ilimitado de outros tipos. .
var c : cor;
• No caso do Pascal, os tipos podem ser: .
.
.
– simples, c := rosa;
– estruturados e – indicação de subintervalos. Exemplo:
– apontadores. type ano = 1900..1999;
type letra = ’A’..’Z’;
.
.
.
var a : ano;
var b : letra;
as atribuições a:=1986 e b:=‘B’ são
possíveis, mas a:=2001 e b:=7 não o são.