Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
UNIVERSIDADE DE FORTALEZA
CENTRO DE CIÊNCIAS TECNOLÓGICAS
TÓPICOS ESPECIAIS DE OTIMIZAÇÃO
NOTAS DE AULA
2001
Seja A um conjunto não vazio. Chama-se partição de A, ao conjunto P com as seguintes propriedades.
Essa notação é usada para expressar a complexidade de tempo de execução de um algoritmo, em função
de um valor “n” que correspondente ao tamanho da entrada do problema. Assim, O( f(n) ) indica que o tempo
necessário para obter um resultado do algoritmo é proporcional a f(n).
Essa medida tem por objetivo avaliar a eficiência do algoritmo em termos de sua complexidade,
normalmente considerada no pior caso. O valor de f(n) não tem uma relação exata com o tempo e sim com
sua ordem de grandeza, o que serve de parâmetro de comparação entre diferentes algoritmos; por exemplo,
para valores de “n” suficientemente grandes as funções f1 (n) = n2 , f2 (n) = 2.n2 + 3 e f3 (n) = 3.n2 , têm a
mesma ordem de grandeza, a qual é proporcional a n2 , ou seja, O(n2). Observe na Tabela 2.1 que a medida
que “n” cresce as ordens de grandeza ( potências de 10 ) de f1 , f2 e f3 tornam-se iguais.
Por definição, se existir uma função g(n) tal que f(n) ≤ c.g(n) , para uma constante c > 0, diremos que
O( f(n) ) = O( g(n) ).
Normalmente a função “g” representa uma família de funções conhecidas como: O(n), O(n2 ), O(n3 ),
O(2n), O(n!), O(log2 n), O(n.log2 n), etc. A notação O(1) é usada para indicar uma complexidade constante,
ou seja, independente de “n”; no entanto, isso é bastante incomum.
No exemplo anterior observa-se que: n2 ≤ 3n2 e 2n2 + 3 ≤ 3n2 ; logo, c=3 e a função g(n) = n2 .
Cuidados devem ser tomados para valores de “c” elevados, pois, é óbvio que f1 = (50!).n e f2 = 3n não tem
realmente a mesma ordem de grandeza; casos como estes devem ter tratamento especial. A Tabela 2.2
permite fazer uma comparação entre funções comumente usadas na medição da complexidade de um
algoritmo para alguns valores de “n”.
Para uma melhor visualização, os valores indicados representam o tempo médio esperado de execução,
expressos em segundos quando não indicado. Foi considerado o tempo de 1 µs para cada ciclo.
De acordo com análise feita na Tabela 2.2 concluímos que para valores de “n” não superiores a 1000 a
complexidade cúbica O(n3 ) seria a máxima admitida. Para grandes conjuntos de dados ( n ≥ 10000 ) o ideal
seria a complexidade não superior a O(n2 ). Em geral, O(nm) para m > 3, ou complexidade exponencia l, são
indesejáveis.
Posteriormente, no item 2.7, veremos que os problemas são classificados em função da complexidade
de seus algoritmos. No item 2.5 daremos uma idéia, através de alguns exemplos, como se obtém esta função
de complexidade.
Era de se esperar que, se um computador resolve um problema de tamanho “n” num determinado
tempo, um outro, de velocidade 100 vezes maior, deveria resolver neste mesmo tempo um problema similar
de tamanho “100.n”.
Entretanto, conforme podemos observar na Tabela 2.3, isto não ocorre. Veja que para complexidade
O(n3), um aumento de 1000 vezes na velocidade, corresponde a um aumento de 10 vezes no tamanho; ou,
para o mesmo caso, se a complexidade fosse O(10n ) o aumento no tamanho seria de apenas 3 unidades (valor
constante).
Por isso, temos variação na medição deste tempo para diferentes compiladores e ambientes
computacionais, mas invariavelmente, as operações de soma ( + ) e subtração ( − ) tem tempo quase igual e
bem menor que a multiplicação ( * ) e divisão ( / ). Entre as funções, geralmente a raiz quadrada ( sqrt ) é a
mais rápida, enquanto que as funções: logaritmo ( log ), seno ( sin ), cosseno ( cos ), exponencial ( exp ) e
arcotangente ( arctg ) são intermediárias quando comparadas com a potenciação com expoente não inteiro (
xy , y∈ℜ ) e a função tangente ( tg ) que se apresentam como as menos rápidas.
Para avaliação destas medidas utilizamos o Algoritmo 2.1, onde clock() é uma função que fornece o
tempo atual em segundos e o símbolo Θ substitui um dos operadores ( +, −, * , / , ^ ) na expressão S = X Θ Y
para dois valores arbitrários de X e Y. Quando S = f (X) , f representa uma das funções citadas
anteriormente.
2
Tabela semelhante à contida na referência [GAR79] p.8
Pesquisa Operacional / Uma Introdução a Complexidade 6
{Início – A2.1}
ler (X,Y)
T1 = clock()
{ Computa tempo de: “loop”, “+” e “=” }
enquanto (I ≤ 1000000) faça
I = I +1
S=I
fim_enquanto
T2 = clock()
TLOOP = T2 − T1
I=0
{ Computa tempo da operação Θ (ou f ) }
enquanto (I ≤ 1000000) faça
I = I +1
S=XΘY { ou S = f (X) }
fim_enquanto
T1 = clock()
T = T1 − T2 − TLOOP
imprimir (T)
{Fim - A2.1}
Algoritmo 2.1 - Avalia Tempo Computacional de uma Operação ou Função
Vale lembrar que a composição destes tempos depende da máquina utilizada e em geral estes valores
encontram-se em manuais específicos for necidos pelos fabricantes.
3
RAM (Random Access Memory) significa Memória de Acesso Randômico ou Memória Principal
Pesquisa Operacional / Uma Introdução a Complexidade 7
Operação / Tempo Médio Unidade
Função ( em µs ) (Tempo
Relativo)
+ 0.5 1
− 0.5 1
* 2.8 6
/ 3.0 6
sqrt 7.2 14
log 14.4 29
sin 15.9 32
cos 15.9 32
arctg 16.2 32
exp 20.3 40
^ 34.52 69
tg 34.84 70
Tabela 2.4 - Tempo Computacional para Operações e Funções
Com estes resultados, é esperado que se um determinado programa que só realiza operações de adição (
+ ) necessita de 1 segundo para sua execução, seria necessário em torno de 69 segundos quando esta
operação fosse trocada pela potenciação ( ^ ).
Sem perda de generalidade, poderíamos computar as operações e funções num dado algoritmo
indistintamente, apesar dos tempos de processamento serem diferentes. Isso é válido porque levamos em
consideração apenas a ordem de grandeza deste tempo em função da complexidade do algoritmo. Por
exemplo, se t1 , t2 e t3 são respectivamente os tempos computacionais para cálculo das funções logaritmo (
log(x) ) e raiz quadrada ( sqrt(x) ) e da operação de multiplicação (*) temos que t 1 ≈ 2 t2 ≈ 5 t3 .
Ou seja, a grosso modo poderíamos substituir o cálculo de log(x) pelo cálculo de sqrt(x) duas vezes ou
pela computação da operação “*” cinco vezes, isso sem alterar o tempo de execução do processo; de forma
que, se f 1 , f2 e f3 são seus respectivos algoritmos teríamos, O( f 1 ) = O( 2.f 2 ) = O( 5.f 3 ) , ou O( f1 ) = O( f 2
) = O( f 3 ), pois conforme citado anteriormente, O( f ) = O( c.f ), sendo “c” uma constante positiva.
seria executada nessa máquina num tempo aproximado de 78.3 µs, conforme mostra a ordem de execução
das operações indicadas na Tabela 2.5 (foi considerado desprezível o tempo de execução do comando de
atribuição).
Antes de definirmos classes de problemas iremos relembrar que de acordo com os resultados
apresentados na Tabela 2.2, algoritmos de tempo polinomial são preferíveis aos algoritmos de tempo
exponencial. Complexidade de O( nm ), para m > 3, ou O ( xn ), para x > 1 são indesejáveis e os problemas
correspondentes são denominados de intratáveis. Mesmo nas complexidades de O ( c.n2 ), para grandes
valores de c ( c > 106 ), também são consideradas assim.
Existem problemas dificílimos, no sentido de que nenhum algoritmo pode resolvê-los; estes são
denominados indecidíveis, não-computáveis ou fortemente intratáveis.
Por exemplo, sendo “n” o tamanho da entrada do problema, f (n) > 1010 já é motivo de preocupação,
pois máquinas convencionais com unidade de tempo para instruções e operações da ordem de 10− 6 segundos,
requereriam um tempo de execução em torno de 3 horas. Se f (n) > 1011 (10 vezes) este tempo já superaria
um dia e se fosse da ordem de 1014 ultrapassaria 3 (três) anos.
Chamam-se determinísticos aqueles algoritmos que para uma dada entrada do problema apresentam
apenas uma solução. Caso sejam implementadas operações cujos resultados permitem um número finito de
soluções, então os algoritmos seriam chamados de não-determinísticos.
Existe um modelo abstrato para representação de algoritmos desta natureza, chamado de Máquina de
Turing ( Alan Mathison Turing, 1936 ) cujo detalhamento pode ser encontrado em [CAM94] p. 59-61. São
definidas primitivas que levam ao sucesso (sim) ou fracasso (não) na sinalização do término de um algoritmo
quando aplicados a uma instância genérica do problema, de forma que, se houver uma seqüência de
operações que conduza ao término com sucesso esta será verificada no menor número de passos.
Um algoritmo não-determinístico tem complexidade O ( f(n) ), para instâncias de tamanho “n” para
término com sucesso e O ( 1 ), ou constante, com término com fracasso.
1.4.3 Classes P e NP
Esta classificação, fundamentada na teoria desenvolvida por Stephen Cook [COO71], pode ser feita
através de algumas conotações.
Em termo da função de complexidade, podemos dizer que problemas com complexidade de tempo
polinomial constituem a classe P, enquanto que aqueles com tempo exponencial pertencem à classe NP.
Outra conotação desta classificação seria que problemas de decisão que admitem algoritmos
determinísticos polinomiais formam a classe P, enquanto que algoritmos não-determinísticos polinomiais
formam a classe NP.
Conjectura-se que P ≠ NP, pois diversos pesquisadores até hoje não conseguiram elaborar algoritmos
polinomiais eficientes para um grande número de problemas (veja a relação de alguns deles no item 2.7.5),
de forma que na Figura 2.23, o conjunto ( NP − P ) seria não vazio. Porém, se alguém conseguir mostrar um
algoritmo polinomial para resolver pelo menos um daqueles problemas, então P = NP, pois todos os outros
problemas poderiam ser transformados ( reduzidos ) àquele. Outra dificuldade para provar que um problema
de decisão π ∉ P, é necessário mostrar uma prova concreta que todo algoritmo possível para resolver π não é
polinomial.
Observe que P ⊆ NP, pois além das considerações feitas no item 2.7.1, tem-se que um algoritmo
determinístico é um caso particular de algoritmo não-determinístico, sendo P, portanto, uma subclasse de
NP.
Por fim indicamos que a classe NP compreende àqueles problemas de decisão π para os quais existe um
algoritmo não-determinístico para justificar a resposta “sim”, que será reconhecida por um algoritmo
polinomial; observando que uma justificativa para a resposta “não” geralmente é exaustiva.
Quando existe uma transformação em tempo polinomial de um problema de decisão π 1 para outro π 2 (
escreve-se: π 1 ∝ π 2 ), mostra-se [GAR79] que se π 2 ∈ P ⇒ π 1 ∈ P, ou de forma equivalente, se π 1 ∉ P ⇒
π 2 ∉ P. Podemos então dizer que π 1 é um caso particular de π 2 ,ou seja, π 2 é pelo menos tão difícil quanto π 1 .
Observe que esta relação é transitiva, ou seja, se π 1 ∝ π 2 e π 2 ∝ π 3 então π 1 ∝ π 3 .
A denominação de problemas de decisão como NP-Completo deve-se a Stephen Cook [COO83] que
estabeleceu que o problema SAT (satisfiabilidade), referido no exemplo 2.14, é o mais difícil da classe NP e
todos os outros problemas em NP podem ser reduzidos polinomialmente a ele. Portanto, a classe NP-
Completo contém os problemas de maior dificuldade entre todos em NP.
A Figura 2.23 mostra uma estrutura de problemas NP numa visão simplificada dos problemas de
decisão; os problemas NP-Árduos estão fora desta classificação.
Uma técnica de prova para problemas NP-Completos consiste em utilizar a chamada redução mestre:
SAT ∝ π, sendo a redução feita em tempo polinomial. Neste caso, o problema de decisão π é pelo menos tão
difícil quanto SAT, logo π também é NP-Completo. Em geral, usando a propriedade transitiva, se várias
reduções da forma: SAT ∝ π 1 ∝ π 2 ∝ . . . ∝ π m podem ser feitas em tempo polinomial, então π i , ∀ i=1,...,m,
é um problema da classe N P-Completo.
Se n-SAT é uma classificação do problema SAT, onde cada cláusula tem exatamente “n” literais é fácil
verificar que 2-SAT pertence a classe P e para n≥3, n-SAT é NP. Veja o exemplo:
Essas substituições podem ser feitas em tempo polinomial, de forma que qualquer expressão n-SAT
pode ser transformada numa expressão 3-SAT.
Um problema de otimização é N P-Árduo, pois sendo composto de vários problemas de decisão seria
pelo menos tão difícil quanto estes, logo, não poderia ser NP-Completo. Assim, se π 0 for um problema de
Portanto, se π 0 fosse de ordem O ( f(n) ), π seria de ordem O ( m.f(n) ), logo π só poderia ser resolvido
em tempo polinomial se π 0 também o fosse, donde se conclui que π 0 é NP-Completo e π é NP-Árduo.
Em resumo, se NPA = NP-Árduo e NPC = NP-Completo, então NPA ≥ NPC ≥ NP, onde o sinal “≥ ”
significa “de dificuldade não menor”. Concluímos também que os problemas da classe NPC são os mais
difíceis em NP, para estes não são conhecidos, ou não existem, algoritmos determinísticos, de complexidade
polinomial, para resolvê-los.
Garey & Johnson, [GAR79] p. 190-288, apresentam uma extensa lista de problemas de decisão NP-
Completo, pertencentes às áreas de Otimização Combinatória, Teoria dos Números, Construção de
Compiladores, Planejamento de Tarefas, Teoria dos Grafos, Projeto de Redes de Computadores,
Programação Matemática, Conjuntos e Partições, Lógica, Jogos etc. Aqui selecionamos alguns deles:
Entrada : Grafo G = ( V, E ).
Questão : G contém um Caminho Hamiltoniano ?
Redução:Cobertura de Vértices [KAR72]
Entrada : Grafos G = ( V1 , E1 ) e H = ( V2 , E2 ).
Questão : G contém um subgrafo isomorfo à H ?
Um subgrafo G′ = ( V, E ) de G, V ⊆ V1 e E ⊆ E1 , é isomorfo a um grafo H, se | V | =
| V2 |, | E | = | E2 | e ∃ f : V2 → V {u, v} ∈ E2 ⇔ { f (u) , f (v) } ∈ E.
Redução:Clique [COO71]
Entrada : Conjunto C com “m” cidades; distância d ( ci , cj ) ∈ Z+ para cada par de cidade ci , cj
∈ C; e um inteiro positivo B.
Questão : ∃ uma permutação < ck(1) , ck(2) , ... , ck(m) > tal que:
m−1
∑ d ( ck ( i ) , c k ( i+1) ) + d ( ck ( m) , ck (1) ) ≤ B ? ?
i=1
Redução:Circuito Hamiltoniano [KAR72]
Entrada : Grafo misto G = (V, A, E), onde A é o conjunto de arestas direcionadas e E o conjunto
de arestas não-direcionadas de G; tamanho L(e) de cada aresta e ∈ (A ∪ E); e um inteiro positivo B.
Questão : ∃ um ciclo em G que inclui cada aresta (direcionada ou não) pelo menos uma vez com
tamanho total ≤ B ?
Redução:3-SAT [PAP76]
Redução:Partição [KAR72]
Entrada : Conjunto finito X de pares ( x , b), onde x é uma m-tupla de inteiros e “b” um inteiro;
uma m-tupla c de inteiros e um inteiro B.
Questão : ∃ uma m-tupla y de inteiros tal que x.y ≤ b para todo ( x , b) ∈ X e tal que c.y ≥ B.
Obs.: u.v é o produto escalar do vetor u = ( u1 , u2, ... , um ) pelo vetor v = ( v1, v2 , ... , vm ), ou seja, u.v =
u1 .v1 + u2 .v2 + ... + um .vm.
Redução:3-SAT [KAR72]
Entrada : Conjunto finito X de pares ( x , b), onde x é uma m-tupla de reais e “b” um real; duas
m-tuplas c e d de reais e um real B.
Questão : ∃ uma m-tupla y de reais tal que x.y ≤ b para todo ( x , b) ∈ X e tal que
∑
m
( ci . y i + d i . yi ) ≥ B , onde ci , d i , yi são os i- ésimos componentes de c, d e y, respectivamente.
2
i =1
Redução:Partição [SHA74]
Entrada : Conjunto finito U de itens; um tamanho s(u) ∈ Z+ para cada u ∈ U e um valor v(u) ∈
Z+ e os inteiros positivos B e K
Questão : ∃ U′⊆ U tal que ∑ u∈U '
s( u) ≤ B e ∑ u∈U '
v (u) ≥ K ?
Redução:Partição [KAR72]
xx) Números Compostos ( Composite Numbers )
Introdução
Durante vários anos, o método simplex pareceu bastante eficiente do ponto de vista
computacional. Entretanto várias questões fo ram abordadas tais como:
Princípio da Invariância
O princípio da invariância diz que duas diferentes implementações do mesmo
algoritmo não diferirão em eficiência computacional por mais que uma constante
mult i plicativa.
Definição: Sejam f:Á → Å + e g:Á → Å + duas funções, a notação f∈O(g), que se lê: “f é
da ordem de g”, significa que existe uma constante k > 0 e n0 ∈ Á, tal que f(n) ≤ k.g(n) para
todo n > n0 .
Propriedade: Se f:Á → Å + e g :Á → Å + são duas funções tais que f(n) ≤ g(n), então
i −1
s.a ( 2∑10 i− j x j ) + x i ≤ 100 i −1 (i = 1, 2,..., n )
j=1
xj ≥ 0 (j=1,2,...,n)
Para n = 2 tem- se
máx Z = 10x1 + x2
s.a x1 ≤ 1
2 0 x1 + x2 ≤ 100
x1 ≥ 0, x2 ≥ 0
Graficamente:
(0,0)
(1,0) (5,0)
Fazendo z’ = -z
Mín z’ = -10x1 – x2
s.a x1 + x3 =1
20x1 + x2 + x4 = 100
x1, x2 ≥ 0
Base X1 X2 X3 X4 B
X3 1 0 1 0 1
X4 20 1 0 1 100
- Z’ -10 -1 0 0 0
Base X1 X2 X3 X4 B
X1 1 0 1 0 1
X4 0 1 -20 1 80
- Z’ 0 -1 10 0 10
Base X1 X2 X3 X4 B
X1 1 0 1 0 1
X2 0 1 -20 1 80
- Z’ 0 0 -10 1 90
Base X1 X2 X3 X4 B
X3 1 0 1 0 1
X2 20 1 0 1 100
- Z’ 10 0 0 1 100
VB à X3 = 1; X2 = 100; VNB à X1 = X4 = 0
Pesquisa Operacional / Uma Introdução a Complexidade 21
Para n = 3 tem-se
Máx Z = 100x1 + 1 0 x2 + x 3
s.a x1 ≤ 1
2 0 x1 + x2 ≤ 100
2 0 0 x1 + 20x2 + x3 ≤ 10000
xi ≥ 0, i = 1,2,3
Quando o simplex é usado pa ra resolver este PPL, toda solução básica viável deve
ser examinada antes da solução ótima ser descoberta. Generalizando este exemplo, Klee &
Minty (1972) construíram (para n = 2, 3,...) um PPL com n decisões variáveis e n
expressões para que o algoritmo s implex examinassem 2 n -1 soluções básicas viáveis antes
da solução ótima se descoberta. Assim, existe um PPL com 10 variáveis e 10 expressões
para que o simplex requeira 21 0 - 1= 1023 pivôs para descobrir a solução ótima. Assim
sendo, tão patológico PPL raramente ocorre em aplicações práticas.
Graficamente tem- se: