Vous êtes sur la page 1sur 14

Apostila Introdut oria de Algoritmos

Guilherme D. da Fonseca Celina M. H. de Figueiredo

Vers ao rascunho para o curso de An alise de Algoritmos da Unirio 2009.

2 de Outubro de 2009

CAP TULO 6

M etodo Guloso
O m etodo guloso consiste em construir a solu c ao aos poucos, mas sem nunca voltar atr as. Uma vez que o m etodo guloso dene que um elemento est a na solu c ao do problema, este elemento n ao ser a retirado jamais. O m etodo procede acrescentando novos elementos, um de cada vez. 6.1. Fecho convexo: Algoritmo de Jarvis J a vimos como e conveniente trabalhar com pol gonos convexos. Muitas vezes, n ao temos um pol gono convexo, ou sequer um pol gono, mas apenas um conjunto S de pontos no plano. Digamos, por exemplo, que desajamos responder uma s erie de consultas de pontos extremos de S para v arias dire c oes d. Neste caso, vale a pena descobrir qual o menor pol gono convexo P que envolve os pontos de S . Este pol gono P e chamado de fecho convexo de S e est a ilustrado na gura 6.1(a). Este e o nosso problema: Problema 15. Dado um conjunto S de pontos no plano, determinar P o fecho convexo de S. geometricamente intuitivo que todos os v E ertices de P s ao pontos de S , mas nem todos os pontos de S precisam ser v ertices de P . Este fato pode ser provado usando combina c ao linear. O fecho convexo pode ser tamb em denido como o pol gono convexo que tem todos os seus v ertices em S e cont em todos os pontos de S em seu interior. A id eia do m etodo guloso e adicionar um elemento de cada vez na solu c ao e, jamais, remover algum elemento dela. Neste problema, vamos come car descobrindo um primeiro v ertice de P . Isto e f acil, o ponto extremo em qualquer dire c ao e um v ertice de P . Podemos pegar o ponto de S mais a direita (de maior coordenada x) e chamar este ponto de v1 . N ao podemos usar o algoritmo da sess ao 2.3 para encontrar um v ertice extremo porque ainda n ao temos um pol gono convexo. Ainda assim, podemos resolver o problema em tempo linear inspecionando todos os v ertices e retornando o de maior coordenada x. J a temos um v ertice. Como fazemos para achar outro? Geometricamente, podemos inclinar aos poucos a reta vertical que passa por v1 at e que ela toque outro v ertice v2 S , ou seja, pegamos o ponto v2 S que minimiza a fun c ao (v1 (0, 1), v1 , v2 ) (a fun c ao (p1 , p2 , p3 ) est a denida na sess ao 2.3 e retorna o angulo p1 p2 p3 medido no sentido anti-hor ario). Para acharmos o pr oximo v ertice, pegamos o ponto v3 S que minimiza (v1 , v2 , v3 ) e assim por diante, at e retornarmos ao ponto v1 (gura 6.1(b)). Caso tenhamos mais de um ponto v S com o mesmo valor de (vi , vi+1 , v ), devemos escolher, dentre esses, o mais distante de vi+1 . Este algoritmo se chama algoritmo de Jarvis ou embrulho para presente. Vamos provar que o algoritmo de Jarvis funciona, isto e, encontra um pol gono convexo cujos v ertices pertencem a S e todos os pontos de S est ao no interior deste pol gono. Para isto, usaremos a deni c ao de que um pol gono v = (v1 , . . . , vn ) e convexo se (vi1 , vi , vi+1 ) < 180 para i de 1 a n com os ndices m odulo n (ver p agina 17 para explica c ao de ndices m odulo n). Teorema 6.1. O pol gono P = (v1 , . . . , v|P | ) gerado pelo algoritmo de Jarvis tendo como entrada um conjunto S de pontos no plano e realmente o fecho convexo de S . o. Claramente, P S . Vamos chamar de P = (v 1, . . . , v|P | ) o fecho Demonstrac a convexo de S . Primeiro assumimos que v1 = v1 , pois como v1 e um ponto extremo de S , ent ao qualquer pol gono com v ertices em S que contenha v1 em seu interior tem que ter v1 como v ertice. Sem perda de generalidade podemos dizer que v1 = v1 .
47

6.2. ARVORE GERADORA M INIMA: ALGORITMO DE PRIM

48

v3 v4 v2

v1

(a)

(b)

Figura 6.1. (a) Fecho convexo de um conjunto de pontos no plano. (b) Algoritmo de Jarvis fazendo o embrulho para presente. angulo (vi2 , vi1 , vi ) > ertice de P tal que vi = vi . Se o Vamos supor que vi seja o primeiro v (vi2 , vi1 , vi ) temos um absurdo, pois o v ertice escolhido teria sido vi e n ao vi . Se o angulo (vi2 , vi1 , vi ) < (vi2 , vi1 , vi ) ent ao podemos tra car a reta r que passa por vi1 = vi1 e por vi . Como esta reta r cont em uma aresta de P e separa os pontos vi2 e vi , ent ao ou P n ao e convexo, ou P n ao cont em vi em seu interior. Ambas as possibilidades s ao absurdas, pois P e o fecho convexo convexo de S . Nesta argumenta c ao consideramos i > 2. Caso i = 2 devemos considerar um ponto auxiliar v0 = v0 = v1 (0, 1). Tamb em consideramos que n ao h a empate na avalia c ao da fun c ao (p1 , p2 , p3 ). Podemos colocar como crit erio de desempate a dist ancia de p2 a p3 . Qual a complexidade de tempo do algoritmo de Jarvis? Cada passo leva tempo (|S |), tanto no melhor quanto no pior caso, e, no pior caso, todos os pontos de S s ao v ertices de P . Ent ao a complexidade de tempo no pior caso e (|S |2 ). Normalmente, por em, |P | e muito menor que |S |. Por isso eu til dizer que a complexidade de tempo deste algoritmo e (|S ||P |). Dizemos que este algoritmo e sens vel a sa da, pois sua complexidade e medida n ao s o em fun c ao do tamanho da entrada, mas tamb em em fun c ao do tamanho da sa da. Teorema 6.2. A complexidade de tempo do pior caso do algoritmo de Jarvis e (nh), onde n e o n umero de pontos de entrada e h e o n umero de v ertices do fecho convexo. 6.2. Arvore geradora m nima: Algoritmo de Prim Um problema de grafos com diversas aplica c oes em telecomunica c oes, redes de computadores, confec c ao de placas de circuitos etc e o da arvore geradora m nima. Alguns termos de grafos ser ao necess arios agora. Se voc e n ao est a familiar com eles a nota c ao b asica para grafos est a na sess ao 11.2. Problema 16. Seja G um grafo com pesos reais nas arestas, obter a arvore geradora m nima de G. Seja G um grafo com pesos reais nas arestas. Chamamos o peso c(e) de uma aresta e de custo de e. Uma arvore geradora de G e uma arvore T com V (T ) = V (G) e E (T ) E (G) (conseq uentemente |E (T )| = |V (G) 1|). O custo desta arvore c(T ) e denido como: c(T ) =
eE (T )

c(e).

Uma arvore geradora m nima de G e uma arvore geradora T de G que minimiza c(T ). Note que esta arvore n ao precisa ser u nica. Vamos construir esta arvore usando um algoritmo guloso. Neste caso, n ao e muito intuitivo que o algoritmo funcione. Precisamos provar com cuidado este

6.2. ARVORE GERADORA M INIMA: ALGORITMO DE PRIM

49

fato, ou seja, que o algoritmo realmente encontra uma arvore geradora (f acil) e que esta arvore geradora e m nima (bem mais dif cil). Come camos pegando uma aresta que sabemos pertencer a uma arvore geradora m nima de G. Que aresta poder amos escolher? Uma op c ao e a aresta de custo m nimo, mas escolheremos uma outra op c ao. Vamos escolher um v ertice v qualquer e pegar a aresta e1 incidente a v que tenha custo m nimo. Ser a que e1 realmente pertence a alguma arvore geradora m nima de G? Vamos chamar de T uma arvore geradora m nima de G que n ao cont em e1 e usar esta arvore para construir uma arvore geradora m nima de G que cont em e1 . Adicionando e1 a T obtemos um grafo com um u nico ciclo que, abusando da nota c ao, chamamos de T {e1 }. Qualquer aresta removida deste ciclo faz com que obtenhamos uma arvore geradora, pois quebraremos o u nico ciclo do grafo. Se removemos uma aresta e incidente a v deste ciclo obtemos uma arvore com custo c(T {e1 } {e }) = c(T ) + c(e1 ) c(e ). Como e tamb em e incidente a v , temos c(e1 ) c(e ) e c(T {e1 } {e }) c(T ). Portanto existe arvore geradora T {e1 } {e } de custo m nimo que cont em e1 . Como podemos obter a pr oxima aresta? Tamb em existem v arias respostas que funcionam para esta quest ao (ver exerc cio 6.3, para um outro exemplo). Vamos pegar uma aresta que seja adjacente em exatamente um dos extremos ` as arestas j a escolhidas para a arvore geradora m nima e que tenha custo m nimo (guras 6.3(b) e 6.3(c)). Este e o algoritmo de Prim (pseudoc odigo na gura 6.2), que funciona devido ao teorema que veremos a seguir. Entrada: G: Grafo conexo com custos associados ` as arestas. Sa da: T : Arvore geradora m nima de G. Prim(G) V (T ) um v ertice qualquer de V (G) Enquanto |V (T )| = |V (G)| Encontre aresta (u, v ) com u V (T ) e v / V (T ) de custo m nimo Adicione v ` a V (T ) e (u, v ) ` a E (T ) Retorne T Figura 6.2. Solu c ao do Problema 16

Teorema 6.3. Seja T uma arvore geradora m nima de G e T uma arvore tal que E (T ) E (T ). Seja e = (v1 , v2 ) uma aresta de G que tenha custo m nimo dentre as arestas (v1 , v2 ) tais que v1 V (T ) e v2 / V (T ). Ent ao existe uma arvore geradora m nima de G que cont em E (T ) {e}. o. Suponha que T seja uma Demonstrac a arvore geradora m nima de G que n ao cont em e mas cont em todas as arestas de T . Caso n ao exista T , o teorema j a est a provado. Se adicionamos e a T obtemos o grafo T {e} que cont em um u nico ciclo. Como v1 V (T ), v2 / V (T ) e T e uma arvore, ent ao existe uma aresta e = (v1 , v2 ) neste ciclo tal que v1 V (T ) e v2 / V (T ), arvore obtida a partir de T pela remo c ao de e e adi c ao como ilustra a gura 6.3(a). O custo da de e e c(T {e} {e }) = c(T ) + c(e) c(e ). Como c(e) c(e ) ent ao c(T {e} {e }) c(T ). Portanto existe arvore geradora T {e} {e } de custo m nimo que cont em E (T ) {e}. Existem v arias maneiras de implementar este algoritmo, usando diferentes estruturas de dados. Cada implementa c ao tem uma complexidade de tempo diferente. A alternativa mais simples n ao usa nenhuma estrutura de dados sosticada, usando apenas vetores, como mostra a gura 6.4. Basta olharmos para os loops para vermos que o algoritmo tem complexidade de tempo (n2 ), onde n e o n umero de v ertices do grafo. Considerando apenas n, isto e o melhor que

6.2. ARVORE GERADORA M INIMA: ALGORITMO DE PRIM

50

6
T v2 e v1 v1 e v2

6 6 5 r 1 4 3
(c)

5 r 1 4 3 2

5 2

6 5 1 2

1 2
Prxima aresta

T
(a)

5
(b)

Figura 6.3. (a) Ilustra c ao referente a prova do teorema 6.3. (b) Execu c ao do algoritmo de Prim na 3a itera c ao. (c) Avore gerada pelo algoritmo de Prim.

Entrada: G: Grafo conexo com custo custo(u, v ) associado a toda aresta (u, v ). Se (u, v ) / E (G), ent ao custo(u, v ) = . Sa da: T : Arvore geradora m nima de G. PrimVetor(G) V (T ) v um v ertice qualquer de V (G) Marcar v Para todo v ertice u = v u.custo G.custo(u, v ) u.vizinho v Enquanto |V (T )| = |V (G)| v v ertice n ao marcado com menor custo Adicione v ` a V (T ) e (u, v ) ` a E (T ) Marcar v Para todo v ertice u n ao marcado Se custo(u, v ) < u.custo u.custo G.custo(u, v ) u.vizinho v Retorne T Figura 6.4. Solu c ao do Problema 16 usando apenas vetores.

podemos fazer, pois o n umero de arestas do grafo pode ser (n2 ) e todas precisam ser examinadas. Na pr atica, grafos espar cos (poucas arestas) s ao muito mais comuns do que grafos densos. Por isso, seria bom que consegu ssemos expressar a complexidade de tempo n ao s o em fun c ao de n, mas tamb em em fun c ao do n umero de arestas m. O algoritmo de Prim pode ser facilmente modicado para ter complexidade de tempo O(m lg n), usando um heap bin ario. O novo pseudo-c odigo est a na gura 6.5. S ao executadas O(n) inser c oes, O(n) extra c oes de m nimo e O(m) redu c oes de custo no heap. Em um heap bin ario, todas estas opera c oes levam tempo O(lg n), totalizando tempo O(m lg n), pois como G e conexo m n 1. Na teoria, podemos usar um heap de Fibonacci, onde o tempo amortizado da opera c ao de redu c ao de custo e O(1). Neste caso, a complexidade de tempo do

DE DADOS: ARVORES 6.3. COMPACTAC AO DE HUFFMAN

51

Entrada: G: Grafo conexo com custo custo(u, v ) associado a toda aresta (u, v ). Se (u, v ) / E (G), ent ao custo(u, v ) = . Sa da: T : Arvore geradora m nima de G. Observa c oes: H : Heap m nimo que armazena v ertices usando como chave o campo custo. PrimHeap(G) V (T ) v um v ertice qualquer de V (G) Marcar v Para todo v ertice u = v u.custo G.custo(u, v ) u.vizinho v Inserir(H ,u) Enquanto |V (T )| = |V (G)| v ExtrairM nimo(H ) Adicione v ` a V (T ) e (u, v ) ` a E (T ) Marcar v Para todo v ertice u n ao marcado e adjacente ` av Se custo(u, v ) < u.custo ReduzirCusto(H , u, G.custo(u, v )) u.vizinho v Retorne T Figura 6.5. Solu c ao do Problema 16 usando um heap.

algoritmo ca O(n lg n + m). Na pr atica, por em, as costantes multiplicativas no tempo do heap de Fibonacci tornam-o mais lento do que o heap bin ario para qualquer grafo trat avel. 6.3. Compacta c ao de dados: Arvores de Human Vamos estudar agora um problema de compacta c ao de dados. N os vamos nos concentrar em arquivos de texto, para simplicar os exemplos, mas as t ecnicas estudadas aqui independem do tipo de arquivo. Para armazenar um arquivo de texto em um computador cada caractere e 8 armazenado em um byte (8 bits ). Certamente, nem todos os 2 = 256 caracteres poss veis s ao usados. Uma alternativa f acil para reduzir o tamanho do arquivo e vericar se menos de 2k caracteres s ao usados, usando apenas k bits neste caso, mas isto n ao compactar a praticamente nada. Uma t ecnica mais inteligente e considerar as freq u encias dos caracteres e codicar cada caractere com um n umero diferente de bits. Vejamos um exemplo. Queremos compactar a palavra cabana. As freq u encias dos caracteres nesta palavra s ao f (c) = f (b) = f (n) = 1/6, f (a) = 1/2. Temos 4 caracteres, ent ao poder amos usar 2 bits por caractere, totalizando 12 bits. Outra op c ao e usar os seguintes c odigos: c : 000, b : 001, n : 01, a : 1. Com isto obtemos a palavra compactada 00010011011 com 11 bits. N ao parece que ganhamos muito, mas este exemplo e pequeno e cont em pouca redund ancia (tente algo semelhante com a palavra aaabaaacaaad ). De fato, a compacta c ao de Human sozinha n ao e muito eciente, mas ela est a presente como parte importante de praticamente todos os melhores compactadores usados atualmente. Um ponto importante e como podemos decodicar a mensagem. Primeiro, precisamos saber o c odigo de cada caractere. Depois veremos como fornecer esta informa c ao. Al em disso, precisamos ter uma maneira de descobrir quando acaba um caractere e come ca outro. Vejamos o

DE DADOS: ARVORES 6.3. COMPACTAC AO DE HUFFMAN

52

0 0 0 c 1 b 1 n

1 a

de Human da palavra cabana. Figura 6.6. Arvore exemplo do par agrafo anterior. Come camos lendo 0. Os caracteres iniciados por 0 s ao c, b e n. Lemos ent ao outro 0. Agora sabemos que se trata ou de um c ou um b. Ao lermos 0 novamente, temos certeza que e um c. Isto e poss vel porque geramos um c odigo de prexo. Um c odigo de prexo e uma atribui c ao de uma seq u encia distinta de bits para cada caractere de modo que uma seq u encia n ao seja um prexo da outra. Um c odigo ser de prexo e uma condi c ao suciente para permitir que qualquer mensagem escrita com ele possa ser decodicada de modo u nico, mas n ao e uma condi c ao necess aria para isto. N ao provaremos aqui, mas, se estamos limitados a atribuir uma seq u encia de um n umero inteiros de bits para cada caractere, sempre existe c odigo de prexo que usa o m nimo de bits para codicar a mensagem dentre todos os c odigos que permitiriam que qualquer mensagem escrita com ele fosse decodicada de modo u nico. Assim, n ao estamos perdendo nada por nos limitarmos a c odigos de prexo. Existe uma rela c ao direta entre arvores bin arias e c odigos de prexo. A cada folha podemos associar um caractere. Cada bit indica se devemos seguir para a direita ou esquerda na arvore. A arvore correspondente ao c odigo para a palavra cabana est a na gura 6.6. Vamos chamar de C = {c1 , . . . , cn } o nosso conjunto de caracteres e de f (ci ) a freq u encia do caractere ci . Queremos construir uma arvore bin aria T que tenha como conjunto de folhas o conjunto C e que minimize
n

c(T ) =
i=1

f (ci )l(ci ),

onde l(ci ) e o n vel da folha ci , denido como sua dist ancia at e a raiz, ou seja, l(ci ) e o n umero de bits usados para codicar ci . Esta arvore e chamada de arvore de Human de C . Chamamos c(T ) de custo da arvore T . Note que as freq u encias dos caracteres podem ser multiplicadas livremente por qualquer constante, por isso podemos usar freq u encias que somem 1 ou o n umero de apari c oes de cada caractere livremente. Problema 17. Dado um conjunto de caracteres C , com suas freq u encias, construir uma arvore de Human de C . O algoritmo que resolve este problema e bem diferente dos outros algoritmos gulosos que vimos. No problema de fecho convexo, quer amos encontrar um conjunto de v ertices (embora ordenado) que satiszesse certa propriedade. No problema de arvore geradora m nima, quer amos encontrar um conjunto de arestas que satiszesse uma propriedade. Agora, a nossa solu c ao n ao e mais um conjunto. O algoritmo que apresentaremos n ao e nem um pouco intuitivo, tanto que, por muitos anos, foi utilizado para resolver este problema um algoritmo que encontrava arvores sub- otimas. Come camos criando um n o para cada caractere. Estas ser ao as folhas da arvore e t em como freq u encia a pr opria freq u encia do caractere. A cada passo do algoritmo, os dois n os de menor freq uencia s ao colocados como lhos de um novo n o. A freq u encia do novo n o e a soma das freq u encias de seus lhos. Este procedimento est a ilustrado no pseudo-c odigo da gura 6.7 e exemplicado na gura 6.8.

DE DADOS: ARVORES 6.3. COMPACTAC AO DE HUFFMAN

53

Entrada: C : Vetor de caracteres a serem codicados. f : Vetor com as freq uencias dos caracteres. Sa da: V ertice raiz da arvore de Human de C . Observa c oes: H : Heap m nimo que armazena v ertices usando como chave o campo f req . Human(C , f ) Para i de 1 at e |C | v CriarV ertice() v.caractere C [i] v.f req f [i] Inserir(H, v ) Para i de 1 at e |C | 1 v CriarV ertice() v.dir ExtrairM nimo(H ) v.esq ExtrairM nimo(H ) v.f req v.dir.f req + v.esq.f req Inserir(H, v ) Retorne ExtrairM nimo(H ) Figura 6.7. Solu c ao do Problema 17

a:3 b:2 c:4 d:1 e:4 f:9 a:3 c:4 3 b:2 d:1 e:4 f:9

c:4 f:9 3 b:2 6

e:4 c:4 f:9 a:3 d:1 b:2 f:9 3

8 e:4 6 a:3 d:1

8 14 8 c:4 e:4 b:2 3 d:1 f:9 6 a:3 c:4 8

14 6 e:4 b:2 3 d:1 a:3

Figura 6.8. Algoritmo de Human passo a passo, para uma entrada C = (a, b, c, d, e, f ) e f = (3, 2, 4, 1, 4, 9). Para fazer a an alise de complexidade notamos que o loop e repetido n vezes em um alfabeto com n caracteres e a cada repeti c ao as opera c oes feitas no heap levam tempo O(lg n). No total temos a complexidade de tempo de O(n lg n). A prova de que o algoritmo funciona, ou seja, de fato gera uma arvore de Human, e bem mais complicada. Esta prova ser a feita por indu c ao. Primeiro, provaremos que podemos colocar os dois v ertices de menor freq u encia como folhas irm as na arvore. Em seguida, provaremos que podemos considerar cada arvore de Human j a constru da pelo algoritmo como um u nico v ertice

DE DADOS: ARVORES 6.3. COMPACTAC AO DE HUFFMAN

54

cuja freq u encia e a soma das freq u encias dos dois lhos, na cria c ao de uma arvore maior. Antes disso, provaremos que toda arvore de Human e estritamente bin aria. Lema 6.4. Toda arvore de Human e estritamente bin aria. o. Por deni Demonstrac a c ao a arvore de Human e bin aria. Suponha que um v ertice v tenha apenas um lho. Neste caso podemos remover v da arvore, fazendo que o lho de v seja lho do pai de v . A arvore obtida tem custo menor do que uma arvore de Human, o que e absurdo. Lema 6.5. Seja C = {c1 , . . . , cn } um alfabeto onde todo caractere ci tem freq u encia f (ci ) e f (ci ) f (ci+1 ). Neste caso, existe arvore de Human onde c1 e c2 s ao folhas irm as. o. Usaremos uma Demonstrac a arvore de Human T onde c1 e c2 n ao s ao folhas irm as para construir uma arvore de Human T onde c1 e c2 s ao folhas irm as. Como T e estritamente bin aria, existem pelo menos duas folhas irm as no u ltimo n vel de T . Como c1 e c2 s ao os dois caracteres menos freq u entes, se colocarmos c1 e c2 nestas duas folhas, e colocarmos os caracteres que estavam nelas nas posi c oes de c1 e c2 , obtemos uma arvore T com c(T ) c(T ). Lema 6.6. Se TC e uma arvore de Human para o alfabeto C = {c1 , . . . , cn }, ent ao TC , obtida acrescentando dois lhos c1 e c2 a uma folha ck de TC onde f (ck ) = f (c1 ) + f (c2 ) e f (c1 ), f (c2 ) f (ci ) para 1 i n, e uma arvore de Human para o alfabeto C {ck } {c1 , c2 }. o. O custo de TC e c(TC ) = c(TC ) + f (c1 ) + f (c2 ), ent ao c(TC ) = c(TC ) Demonstrac a f (c1 ) f (c2 ). Para obter um absurdo, suponha que TC seja uma arvore de Human de C com c(TC ) < c(TC ). Pelo lema 6.5 podemos considerar que c1 e c2 s ao folhas irm as em TC . Removendo estas duas folhas c1 e c2 e atribuindo o caractere ck ao pai delas obtemos uma arvore TC com c(TC ) = c(TC ) f (c1 ) f (c2 ) < c(TC ), o que contradiz o fato de c(TC ) ser uma arvore de Human para C . Teorema 6.7. O algoritmo gera uma arvore de Human para C . o. O lema 6.5 Demonstrac a e a base da indu c ao. O lema 6.6 fornece o passo indutivo. Note que provamos que a arvore e uma arvore de Human na ordem contr aria a que ela e constru da. Come camos provando que a raiz com seus dois lhos e uma arvore de Human e vamos descendo na arvore, como mostra a gura 6.8. Para que o descompactador decodique um arquivo compactado com o c odigo de Human ele precisa ter conhecimento da arvore. Analisaremos 4 alternativas para este problema: 1) Usar uma arvore pr e-estabelecida, baseada em freq u encias m edias de cada caractere. Esta t ecnica s o e vi avel em arquivos de texto. Ainda assim, de um idioma para outro a freq u encia de cada caractere pode variar bastante. 2) Fornecer a arvore de Human, direta ou indiretamente, no in cio do arquivo. A arvore de Human para 256 caracteres pode ser descrita com 256 caracteres mais 511 bits usando percurso em arvore. Outra op c ao mais simples e informar a frequ encia de cada caractere necess e deixar que o descompactador construa a arvore. E ario cuidado para garantir que a arvore do compactador e do descompactador sejam id enticas. 3) Fornecer a arvore de Human, direta ou indiretamente, para cada bloco do arquivo. Esta t ecnica divide o arquivo em blocos de um tamanho xo e constr oi arvores separadas para cada bloco. A vantagem e que se as freq u encias dos caracteres s ao diferentes ao longo do arquivo, pode-se obter maior compacta c ao. A desvantagem e que v arias arvores tem que ser fornecidas, gastando espa co e tempo de processamento. 4) Usar um c odigo adaptativo. Inicia-se com uma arvore em que todo caractere tem a mesma freq u encia e, a cada caractere lido, incrementa-se a freq u encia deste caractere, atualizando a arvore. Neste caso n ao e necess ario enviar nenhuma arvore, mas n ao h a compacta c ao signicativa no in cio do arquivo. N ao apresentamos aqui algoritmo para fazer esta atualiza c ao na arvore ecientemente.

DE DADOS: LZSS 6.4. COMPACTAC AO

55

6.4. Compacta c ao de dados: LZSS Uma t ecnica simples que produz bons resultados de compacta c ao e o m etodo chamado LZSS. Este m etodo, completamente diferente do m etodo de Human, se baseia no fato de algumas seq u encias de caracteres se repetirem ao longo do arquivo. A id eia e, ao inv es de escrevermos todos os caracteres do arquivo explicitamente, fazermos refer encias a seq u encias anteriores. Modelaremos formalmente esta id eia a seguir. Os primeiros modelos n ao fornecem um compactador eciente, mas ajudam a entender as id eias centrais da t ecnica. Temos como entrada uma sequ encia de caracteres (o arquivo descompactado) e queremos gerar uma seq u encia de simbolos correspondente ao arquivo (o arquivo compactado). Um s mbolo ou e um caracter ou um par (p, l). Temos que incluir no arquivo uma maneira de distinguir entre estes dois tipos de s mbolo, mas s o discutiremos este detalhe bem mais tarde. O signicado de um par (p, l) e uma refer encia a posi c oes anteriores do arquivo: os l caracteres iniciados a partir de p caracteres anteriores no arquivo descompactado. Vejamos alguns exemplos: Descompactado1: m etodogulosogulosom etodo Compactado1: m etodoguloso(6,6)(18,6) Descompactado2: bananadadebanana Compactado2: ban(2,3)dade(10,6) Note que em um s mbolo (p, l), p nunca pode referenciar um caractere que ainda n ao foi codicado, portanto p 1. Podemos tamb em for car l 2, pois preferimos escrever um u nico caractere explicitamente a referenci a-lo. O descompactador para este arquivo e bem simples e seu pseudo c odigo est a na gura 6.9. Neste exemplo trabalhamos com vetores e n ao arquivos.

Entrada: C : Vetor compactado. D: Vetor onde ser a escrito o arquivo descompactado. Sa da: Retorna o n umero de caracteres do arquivo descompactado. DescompactadorLZSS1(C , D) Di 1 Para Ci de 1 at e |C | Se C [Ci] e um caractere D[Di] C [Ci] Di Di + 1 Sen ao Para i de 0 at e C [Ci].l 1 D[Di] D[C [Ci].l + i] Di Di + 1 Retorne Di 1 Figura 6.9. Descompactador LZSS em vetor.

Temos alguns problemas no m etodo de compacta c ao que denimos. Um deles e como representarmos no arquivo um par (p, l) j a que tanto p quanto l podem ser t ao grandes quanto o tamanho do arquivo descompactado. Outro problema e que o descompactador teria que voltar no arquivo v arias vezes para encontrar as refer encias, o que tornaria a descompacta c ao lenta. A solu c ao para estes dois problemas e limitarmos o valor de p e de l e usarmos um buer circular em mem oria. Assim nos limitamos a armazenar em mem oria as u ltimas posi c oes escritas no arquivo descompactado. Limitaremos p ao valor p (1 p p ) e l ao valor l (2 l l ). O nosso buer precisa armazenar apenas p caracteres. O descompactador em arquivo usando um buer circular est a ilustrado no pseudo-c odigo da gura 6.10.

DE DADOS: LZSS 6.4. COMPACTAC AO

56

Entrada: C : Arquivo compactado. D: Arquivo onde ser a escrito o arquivo descompactado. p : Valor m aximo de p em uma s mbolo (p, l). Observa c oes: A opera c ao i mod n e denida como: i mod n e o resto da divis ao de i por n se i n ao e divis vel por n e i mod n = n se i e divis vel por n. DescompactadorLZSS2(C , D, p ) B AlocarVetor(p ) Bi 1 Enquanto o arquivo C n ao tiver chegado ao m c LerS mbolo(C ) Se c e um caractere Escrever(D,c) B [Bi] c Bi Bi + 1 mod p Sen ao Para i de 1 at e c.l Escrever(D,B [Bi c.p] mod p ) B [Bi] c Bi Bi + 1 mod p Figura 6.10. Descompactador LZSS em arquivo.

V arios arquivos compactados diferentes podem corresponder a um mesmo arquivo compactado. Podemos por exemplo listar todos os caracteres explicitamente, n ao produzindo qualquer compacta c ao. Queremos compactar o m aximo poss vel. Vamos denir o tamanho do arquivo compactado como o n umero de s mbolos que ele cont em. Embora esta medida n ao seja totalmente el a realidade, ela e necess aria para nossos resultados te oricos e nos leva a bons resultados pr aticos (para obtermos realmente o menor arquivo poss vel, ter amos que minimizar a soma dos bits gastos, que dependem do s mbolo ser um caracter ou um par de valores, mas este problema e bem mais dif cil). Problema 18. Dada uma seq u encia de caracteres D e dois valores p e l , encontrar a seq u encia de s mbolos C correspondente a D que cont em o menor n umero de s mbolos com a restri c ao de que todo s mbolo (p, l) satisfaz 1 p p e 1 l l O nosso algoritmo guloso e bastante simples. Sempre tentamos gerar o par (p, l) com o maior valor poss vel de l. Se este valor for 0 ou 1, geramos o caractere explicitamente. Ser a obvio que este algoritmo gera o m nimo de s mbolos? Vejamos um exemplo de varia c ao do problema onde o m etodo guloso n ao funciona bem. Uma varia c ao e quando temos um dicion ario denido e ou fornecemos o caractere explicitamente ou uma refer encia a palavra no dicion ario. Por exemplo se o dicion ario cont em as palavras: p1 = ab, p2 = de e p3 = bcdef , ent ao podemos codicar abcdef como a p3 , mas o m etodo guloso codicaria como p1 c p2 f . Visto isso, parece bem razo avel que devemos provar que o m etodo guloso gera o m nimo de s mbolos no caso do nosso problema. Teorema 6.8. O algoritmo guloso gera uma seq u encia de simbolos correspondente a entrada com o n umero m nimo de s mbolos poss vel. o. Seja C1 , . . . , Cn uma seq u encia de s mbolos gerada pelo algoritmo guloso. Demonstrac a Suponha, para obter um absurdo, que C1 , . . . , Cn seja uma outra seq u encia correspondente a

6.5. RESUMO E OBSERVAC OES FINAIS

57

entrada com n < n. Denimos o tamanho de um s mbolo |Ci | como |Ci | = 1 se Ci e um caractere e |Ci | = l se Ci = (p, l). Vamos denir
k k

T (k ) =
i=1

|Ci | e T (k ) =
i=1

|Ci |.

Como n < n, todo s mbolo tem tamanho positivo e T (n) = T (n ), ent ao existe um menor inteiro k tal que T (k ) > T (k ), com 1 k n . Claramente Ck = (p, l). Neste caso, o algoritmo guloso teria escolhido o par (p, l T (k 1) + T (k 1)) ou algum de tamanho maior. Assim T (k ) = T (k 1) + l T (k 1) + T (k 1) = T (k 1) + l = T (k ). Vale notar que como T (k 1) T (k 1) e l l ent ao l T (k 1) + T (k 1) l . V arios detalhes pr aticos foram omitidos na apresenta c ao do problema. Veremos alguns deles, sem nos aprofundarmos. Para distinguirmos um par (p, l) de um caractere, podemos colocar um bit antes de cada s mbolo que indica se o s mbolo seguinte e um caractere ou um par (p, l). Mais pr atico, por em, e agruparmos esses bits de 8 em 8. Assim temos um byte que indica a natureza dos pr oximos 8 s mbolos do arquivo. razo Uma escolha comum de p e l e p = 4096 (12 bits ) e deixarmos 4 bits para l. E avel for carmos l > 2, pois, quando l 2, n ao ganhamos muito escrevendo um par (p, l). Ent ao podemos fazer 3 l 18. O compactador precisar a manter um buer de hist oria com p caracteres para encontrar as refer encias e um buer com os pr oximos l caracteres a serem lidos. Estes dois buers devem ser de fato um u nico buer circular. Se zermos a busca do maior par (p, l) examinando os buers de maneira trivial a complexidade de tempo do compactador ser a O(np ), onde n e o n umero de caracteres do arquivo descompactado. Para que a busca do maior par (p, l) seja feita ecientemente uma alternativa e usar uma a rvore bin aria balanceada (AVL, rubro-negra, splay, treap...). Esta arvore deve conter as p cadeias de comprimento l que podem ser referenciadas. A cada caractere avan cado do arquivo de entrada, deve-se atualizar a arvore. Neste caso, a complexidade de tempo do compactador ser a O(nl lg p ) 6.5. Resumo e Observa c oes Finais A id eia central do m etodo guloso e construir aos poucos a solu c ao, sem nunca voltar atr as. Muitas vezes, desejamos encontrar um subconjunto da entrada que satisfaz uma propriedade espec ca, e procedemos incluindo um elemento em cada passo. Normalmente os algoritmos contru dos usando o m etodo guloso s ao extremamente simples, por em muitas vezes provar que eles funcionam corretamente e uma tarefa delicada, que merece aten c ao especial. O algoritmo de Jarvis encontra o fecho convexo de um conjunto de pontos no plano, seguindo sucessivamente pelos pontos do fecho, como um embrulho para presente. Este algoritmo e sens vel a sa da, ou seja, sua complexidade de tempo n ao depende somente do tamanho da entrada, mas tamb em do tamanho da sa da. No caso de n pontos na entrada e h pontos na sa da, a complexidade de tempo do algoritmo de Jarvis e (nh). Os melhores algoritmos poss conhecidos para este problema, tem complexidade de tempo (n lg h). E vel provar que n ao e poss vel reduzir ainda mais esta complexidade, portanto esses algoritmos de complexidade de tempo theta(n lg h) s ao otimos. O algoritmo de Prim constr oi a arvore geradora de custo m nimo de um grafo. Este algoritmo segue acrescentando sempre a aresta de menor custo que tem um extremo na parte j a constru da da arvore e outro extremo fora dela. Duas implementa c oes diferentes s ao estudadas, uma usando heap e outra n ao, com diferentes complexidades de tempo. A escolha por uma ou outra implementa c ao depende de qu ao esparso e o grafo, ou seja, quantas arestas tem nele. Uma terceira alternativa usa um heap de Fibonnaci, tendo excelente complexidade de tempo,

EXERC ICIOS

58

por em n ao sendo muito u til na pr atica. Este e um exemplo em que a complexidade de tempo assint otica deve ser vista com cautela, pois pode n ao reetir diretamente a performance pr atica. A arvore de Human tem muitas aplica c oes em compacta c ao de dados. O algoritmo que estudamos constr oi essa arvore de forma bastante engenhosa, unindo sempre os dois elementos menos freq uentes em um novo elemento cuja freq u encia e a soma das freq u encias dos lhos. Outro algoritmo importante em compacta c ao de dados e o LZSS. Estudamos como o m etodo guloso constr oi uma seq u encia eciente de refer encias a palavras de um buer. N ao vimos aqui uma varia c ao do m etodo guloso onde, ao inv es de construirmos a solu c ao incrementalmente, construimos a solu c ao decrementalmente. Em outras palavras, no lugar de acrescentarmos aos poucos elementos a solu c ao, vamos descartando elementos que sabemos n ao pertencer a solu c ao, um de cada vez. Exerc cios 6.1) Escreva o pseudo-c odigo da fun c ao (p1 , p2 , p3 ).

6.2) O envelope superior de um conjunto de retas S no plano cartesiano e a seq u encia de segmentos de retas de S com valor y m aximo para x variando de ` a + (gura 6.11). Escreva um algoritmo guloso que determina o envelope superior de um conjunto de retas no plano. Calcule a complexidade de tempo deste algoritmo em fun c ao do tamanho da entrada e da sa da.
Envelope Superior

Envelope Inferior

Figura 6.11. Envelope superior e envelope inferior de um conjunto de retas. 6.3) O algoritmo de Kruskal resolve o problema da arvore geradora m nima escolhendo a cada passo a aresta de menor custo que n ao adiciona ciclos ao grafo. Ao longo do algoritmo n ao temos uma arvore crescendo, mas v arias arvores surgindo e crescendo at e se unirem em uma u nica arvore. Prove que este algoritmo resolve corretamente o problema. 6.4) Forne ca um u nico conjunto de caracteres, todos com freq u encias distintas, para o qual existem duas arvores de Human n ao isomorfas. 6.5) Dado um grafo direcionado G, escreva um algoritmo para colorir suas arestas de modo que n ao haja duas arestas com a mesma cor entrando em um v ertice nem duas arestas com a mesma cor saindo de um v ertice. A solu c ao obtida deve minimizar o n umero de cores diferentes usadas. Prove que seu algoritmo est a correto. A mesma abordagem funciona para o caso do grafo n ao direcionado? 6.6) Digrafos ac clicos t em diversas aplica c oes. Eles podem ser usados, por exemplo, para representar o sistema de mat erias com pr e-requisitos de uma faculdade. Uma informa c ao extremamente u til sobre os digrafos ac clicos e sua ordena c ao topol ogica. Dado um digrafo ac clico, uma ordena c ao topol ogica dele e uma ordena c ao de seus v ertices onde todas as arestas partam de um v ertice anterior para um v ertice posterior na ordena c ao. Escreva um algoritmo guloso que encontre a ordena c ao topol ogica de digrafos ac clicos, prove que seu algoritmo est a correto e analise sua complexidade de tempo.

EXERC ICIOS

59

6.7) Um problema natural em grafos e encontrar o caminho mais curto entre pares de v ertices. Na vers ao com pesos nas arestas, o comprimento de um caminho e a soma dos pesos das arestas pertencentes a ele. Escreva um algoritmo guloso que, dados um grafo com pesos nas arestas e um v ertice v deste grafo, encontre a dist ancia de v a todos os demais v ertices do grafo. Prove que seu algoritmo est a correto. Sugest ao: seu algoritmo deve ser bastante semelhante ao algoritmo de Prim para arvore geradora m nima, mas deve construir a arvore formada pela uni ao dos caminhos mais curtos que partem de v . Este algoritmo e chamado de algoritmo de Dijkstra. 6.8) Um pa s possue moedas de 1, 5, 10, 25 e 50 centavos. Voc e deve programar uma m aquina capaz de dar troco com essas moedas de modo a fornecer sempre o n umero m nimo de moedas, qualquer que seja a quantia. Considere que a m aquina possui quantidades ilimitadas de todas essas moedas. Prove que seu algoritmo funciona. O algoritmo continuaria funcionando se a m aquina tivesse apenas moedas de 1, 10 e 25 centavos? 6.9) Escreva um algoritmo guloso onde: A entrada e um conjunto de palavras (cadeias de caracteres quaisquer) que formam um dicion ario D e uma frase f (outra cadeia de caracteres), onde todo caractere de f est a em D. A sa da e uma seq u encia de segmentos de palavras que concatenados formam f . Um segmento de palavra e representado por uma tripla (p, ini, f im) onde p e o n umero da palavra no dicion ario D, ini e o n umero do primeiro caractere do segmento e f im e o n umero do u ltimo caractere do segmento. Por exemplo: D = (camelo, aguia, sapo), f = guloso; sa da:(2, 2, 3), (1, 5, 6), (3, 1, 1), (1, 6, 6). Claro que o seu algoritmo deve gerar o m nimo de triplas poss vel. Prove que o seu algoritmo resolve o problema com este n umero m nimo de triplas. 6.10) Deseja-se realizar o m aximo poss vel de tarefas de um conjunto de tarefas onde cada tarefa tem um hor ario de in cio e um hor ario de t ermino. Duas tarefas n ao podem estar sendo realizadas simultaneamente. Toda tarefa que for realizada dever a iniciar exatamente no seu hor ario de in cio e terminar exatamente no seu hor ario de t ermino. Escreva um algoritmo guloso que fornece o maior n umero poss vel de tarefas que podem ser realizadas. Prove que seu algoritmo funciona. *6.11) O fecho convexo de um conjunto de pontos no espa co e o menor poliedro convexo que cont em todos estes pontos. Dado um conjunto de n pontos no espa co tridimensional, escreva um algoritmo para determinar seu fecho convexo em tempo O(nh), onde h eo n umero de v ertices do poliedro do fecho convexo.

Vous aimerez peut-être aussi