• Nenhum resultado encontrado

3.3. Fases do Método Proposto

3.3.3. Seleção das Visões

3.3.3.2. Algoritmos de Otimização Propostos

Os algoritmos propostos são baseados nas meta-heurísticas GRASP (G), Reactive GRASP (RG) e GRASP com Path-Relinking (PR). Todos os algoritmos têm como ponto de partida, o algoritmo proposto em (FIRMINO, MATEUS, et al., 2011), aqui denominado por G,e ilustrado na Figura 3.6 (Algoritmo 3.0). A busca local de G se baseia na escolha aleatória de elementos do conjunto de troca, a fim de que uma

permutação compatível seja realizada. Ela é dita ser compatível porque não excede o limiar de armazenamento e gera uma nova solução de melhor valor. No entanto, devido à baixa probabilidade da permutação ocorrer por causa da escolha aleatória de elementos, a busca local de G foi substituída e um novo algoritmo, chamado de Reactive GRASP (RG), foi especificado e é exibido na Figura 3.7 (Algoritmo 3.1. Este algoritmo RG caracteriza-se por apresentar melhorias na busca local e por apresentar características reativas. Combinando RG como as variantes do PR {Forward (F),

Forward Truncated (FT), Backward (B), Backward Truncated (BT), Mixed (M) e Mixed Truncated (MT)}, oito algoritmos no total foram investigados: G, RG, RGF,

RGFT, RGB, RGBT, RGM e RGMT. Os algoritmos recebem como entrada uma lista de quádruplas, descrita na Seção 3.3.3.1, ordenadas pela densidade. Cada quádrupla da lista representa um vértice. Vale ressaltar que são processadas pelo algoritmo apenas os vértices que tiverem benefício maior que zero.

Algoritmo 3.0: GRASP (

,

,

,

, ) Entrada: uma lista de vértices (

),

espaço disponível para materialização (

), grau de gulosidade para definir a LRC (

),

grau de gulosidade para definir a LRC na busca local (

), número de tentativas de trocas feitas na busca local ( ) Saída: melhor solução encontrada

1 melhorSolução 

;

2 ENQUANTO critério de parada não for satisfeito FAÇA 3

' 

; 4 solução FaseConstrução(

' ,

,

); 5 solução FaseBuscaLocal(

' ,

,

,  , solução); 6 AtualizarSolução(solução, melhorSolução); 7 RETORNE melhorSolução;

Figura 3.6: Algoritmo GRASP (G)

Na primeira linha do Algoritmo 3.0, a melhor solução é inicializada com um conjunto vazio de vértices, ou seja, no início do algoritmo, o benefício da melhor solução é zero. Nas linhas 2 a 7, as iterações do GRASP são realizadas até que o critério de parada seja atendido. Na linha 3, a variável φ é copiada para φ' a cada início de iteração, de modo que a cada início de iteração, a lista de vértice seja a mesma, pois a lista de vértice passada como parâmetro ao procedimento FaseConstrução sofre

alterações durante a execução do procedimento. Na linha 4, uma solução parcial é obtida por meio da fase de construção e armazenada na variável solução. Então, na linha 5, por meio da fase de busca local, uma nova solução, a qual é igual a ou melhor que a solução anterior, é obtida e armazenada na variável solução. Na linha 6, o procedimento

AtualizarSolução é chamado para comparar a melhor solução corrente (melhorSolução)

com a solução obtida na linha anterior (solução). Isto faz com que a solução com o melhor benefício seja atribuída à melhorSolução. O benefício de uma solução é dado pela soma do benefício de cada vértice que compõe a solução. Finalmente, no fim das iterações, na linha 7, a melhor solução até então encontrada, a qual consiste no melhor conjunto de visões para processar consultas de uma dada assinatura, é retornada.

O Algoritmo 3.1 (RG) é similar ao algoritmo Algoritmo 3.0 (G). Eles diferem entre si da seguinte maneira. Primeiro, o grau de gulosidade θ antes obtido em G por passagem de parâmetro, agora é obtido em RG pelo procedimento

ObterGrauGulosidade que reativamente calcula o valor para o grau de gulosidade. Por

fim, RG utiliza uma nova heurística para a fase de busca local que dispensa a utilização de dois parâmetros (ϑ e τ) utilizados em G.

Algoritmo 3.1: Reactive GRASP (

,

) Entrada: uma lista de vértices (

),

espaço disponível para materialização (

), Saída: melhor solução encontrada

1 melhorSolução 

;

2 ENQUANTO critério de parada não for satisfeito FAÇA 3

' 

; 4 solução FaseConstrução (

' ,ObterGrauGulosidade(),

); 5 solução FaseBuscaLocal (

' ,

, solução); 6 AtualizarSolução(solução, melhorSolução); 7 RETORNE melhorSolução;

Figura 3.7: Algoritmo Reactive GRASP (RG)

O procedimento ObterGrauGulosidade funciona da seguinte maneira. Inicialmente, é atribuído um valor inicial entre {0,1} para o grau de gulosidade atual θ, é definido um movimento inicial que pode ser {incrementar (I) ou decrementar (D)} e um valor v para incremento ou decremento. A cada iteração do algoritmo, é verificado se a solução atual melhorou em relação a anterior, ou seja, se a solução atual gerada é

melhor do que a solução anterior. Em caso afirmativo, é realizado o movimento atual, caso contrário é realizado o movimento oposto. Por exemplo, considere que, inicialmente, θ = 0, movimento atual = I e v = 0.05. Na iteração consecutiva , foi verificada uma melhoria na solução gerada, então, foi realizado o movimento atual computando . Em , também foi verificada uma melhoria, então θ é incrementado, sendo agora igual a θ = 0.10. O mesmo acontece em e , então θ é incrementado duas vezes, sendo agora igual a θ = 0.20. Entretanto, em , verificou-se uma piora na solução gerada, então foi realizado o movimento oposto de modo que . Em e , foram verificadas melhorias na solução gerada, continuando o movimento do tipo D, então foi decrementado duas vezes, sendo agora igual a θ = 0.05. Todavia, em , foi gerada uma solução pior que a anterior, então realizou-se o movimento oposto, incrementando o valor de modo que . Assim, comporta-se o algoritmo ao longo de todas as iterações ajustando θ de acordo com a qualidade das soluções geradas em tempo de execução.

O Algoritmo 3.2 é resultante da combinação do RG com o Path-Reliking, e é exibido na Figura 3.8. Este algoritmo, Reactive GRASP com Path-Relinking, na primeira linha, ele inicializa o conjunto elite £ com vazio. As iterações deste algoritmo são computadas nas linhas 2 a 10 até que o critério de parada seja atendido. Durante cada iteração, uma solução resultante da busca local é gerada na linha 5. Na linha 6, se o conjunto elite £ ainda não estiver cheio, então o método adiciona a solução ao £. Isto é realizado na linha 10. Se £ estiver cheio, na linha 7, o path-relinking é aplicado, e depois, para adicionar a solução gerada ao £, o método é chamado na linha 8. Vale ressaltar que a solução é apenas adicionada se a diferença entre ela e todas as soluções contidas em £ for maior que o valor retornado pelo procedimento

ObterLimiarDiferença. Esta restrição é importante para preservar a diversidade das

soluções do conjunto elite. O valor obtido reativamente por ObterLimiarDiferença representa o limiar de diferença entre duas soluções, ou seja, a quantidade mínima de elementos diferentes entre duas soluções de modo a considerar as duas soluções suficientemente distintas. Finalmente, no final da iteração, na linha 11, a melhor solução encontrada no conjunto elite é retornada.

ObterLimiarDiferença funciona da seguinte maneira. Inicialmente, precisa-se ter

entre elas e considera-se este valor como o limiar de diferença atual (Δ). A medida que uma nova solução é adicionada ao conjunto £, o Δ é atualizado pela seguinte equação . e representam respectivamente, a melhor solução e a segunda melhor solução presentes em £.

Perceba que o Path-Relinking foi aplicado em nosso algoritmo dentro das iterações do GRASP, como realizado em (MATEUS, RESENDE e SILVA, 2011). O objetivo desta aplicação interna do Path-Relinking é executar o Path-Relinking com bons e diferentes conjuntos elites, pois a durante as iterações do algoritmo o conjunto elite é ajustado de modo a melhorar o qualidade dos religamentos.

Algoritmo 3.2: Reactive GRASP com PR (

,

, ) Entrada: uma lista de vértices (

),

espaço disponível para materialização (

), variante do path-relinking a ser aplicada ( ) Saída: melhor solução encontrada

1

£

;

2 ENQUANTO critério de parada não for satisfeito FAÇA

3

'

; 4 solução FaseConstrução (

' ,ObterGrauGulosidade(),

); 5 solução FaseBuscaLocal (

' ,

, solução); 6 SE (EstáCheio(

£

)) ENTÃO

7 solução PathRelinking(

£

, solução,); 8 ADD(

£

, solução, ObterLimiarDiferença()); 9 CASO CONTRÁRIO

10 ADD(

£

, solução, ObterLimiarDiferença()); 11 RETORNE MAX(

£

);

Figura 3.8: Algoritmo Reactive GRASP com Path-Relinking

O processo do path-relinking consiste de uma sequência de passos de religação de forward e/ou backward. A Figura 3.9 (Algorithm 3.2.1) mostra o pseudocódigo para o procedimento path-relinking. Inicialmente, uma solução é escolhida aleatoriamente de

£ e é atribuida à variável sol£. Em seguida, nas linhas numeradas de 1 a 3, a solução

passada no parâmetro de entrada do algoritmo (solução) e sol£ são copiadas para sol1 e

sol2, respectivamente, e então solução e sol£ não são modificadas durante a execução

do algoritmo. Na linha 4, inicialmente, a melhor solução até então encontrada pelo PR (melhorSolução) é inicializada com a solução de melhor valor entre as soluções iniciais

sol1 e sol2 . Posteriormente, nas linhas 5 a 9, a variável que registra a condição de truncamento do PR (éCondTruncamento) é inicializada com falso e as informações sobre qual variante do path-relinking (forward, backard, mixed, forward truncated,

backard truncated, mixed truncated) está sendo utilizada é obtida por meio de ξ.

Algoritmo 3.2.1: PathRelinking (

£

,solução, ) Entrada: conjunto elite (

£

),

solução construída na fase de busca local (solução), path-relinking variant ()

Saída: melhor solução encontrada 1 sol£  SelecionarAleatoriamente(£); 2 sol1  Copiar(solução);

3 sol2  Copiar(sol£);

4 melhorSolução  Max(sol1,sol2); 5 éCondTruncamento falso; 6 éTruncated  ÉTruncated(); 7 éMixed  ÉMixed(); 8 éForward  ÉForward(); 9 éBackward  ÉBackward();

10 ENQUANTO ((!éTruncated) OU (éTruncated E NÃO éCondTruncamento)) 10 E Diferença(sol1, sol2) != 0) FAÇA

11 SE (éMixed) ENTÃO 12 SE (éPar(count)) ENTÃO 13 Forward(sol1, sol2); 14 CASO CONTRÁRIO 15 Backward(sol1, sol2);

16 CASO CONTRÁRIO SE (éForward) ENTÃO 17 Forward(sol1, sol2);

18 CASO CONTRÁRIO SE (éBackward) ENTÃO 19 Backward(sol1, sol2);

20 melhorSolução  AtualizarSolucao(melhorsSolução,sol1, sol2); 21 éCondTruncamento  NÃO EstáRazoavelmenteMelhorando(); 22 RETORNE melhorSolução;

Figura 3.9: Algoritmo Path-Relinking

Inicializadas todas as variáveis do algoritmo, nas linhas numeradas de 10 a 21, as iterações do path-relinking são realizadas. A condição de parada do PR é verificada na linha 10 e ela acontece em dois momentos: (1) a variante utilizada é truncated e a condição de truncamento foi atingida, i.e éCondTruncamento = verdade; (2) as duas

soluções são idênticas, i.e Diferença(sol1,sol2)=0, assim todo o caminho de religamento já foi concluído. Durante as iterações é verificado, na linha 11, se a variante é do tipo mixed, então os passos forward e backward são aplicados de forma intercalada nas linhas 12 a 15.

No final de cada iteração, o procedimento AtualizarSolução é chamado para comparar e retornar a melhor solução corrente entre (melhorSolução) e as soluções sol1 e sol2. Em seguida, a variável éCondTruncamento é atribuída reativamente pelo procedimento EstáRazoavelmenteMelhorando. Este procedimento retorna se percentualmente o número de melhorias nas iterações do PR for razoavelmente maior do que o número de não melhorias. Finalmente, no final das iterações, na linha 22, a melhor solução até então encontrada é retornada. Os procedimentos Forward e

Backward executam um movimento que pode ser uma troca, adição ou remoção. A

escolha do movimento a ser executado é realizada computando todos os movimentos possíveis e selecionando o movimento de maior benefício.

Em cada iteração do algoritmo GRASP, o procedimento FaseConstrução é chamado. O algoritmo do FaseConstrução (Figura 3.10) objetiva gerar uma boa solução inicial para ser melhorada nas fases seguintes do GRASP. Na primeira linha, a solução é inicializada com um conjunto vazio de vértices. Nas linhas numeradas de 2 a 9 são realizadas as iterações enquanto o tamanho da solução não é maior que o espaço disponível para materialização ( ). Cada iteração desse ciclo começa com a definição da lista restrita de candidatos LRC. Essa lista é formada pelos elementos de melhor avaliação pela abordagem gulosa, ou seja, pelos vértices que possuem maiores densidades. Mais detalhes sobre a LRC serão dados adiante.

Na linha 4, é verificado se existem elementos na LRC para serem adicionados à solução. Caso exista algum elemento na LRC, na linha 4, o procedimento

SelecionarAleatoriamente é chamado para realizar a escolha aleatória de um vértice o

qual é armazenado na variável . Essa aleatoriedade é o diferencial do GRASP, pois a escolha dos vértices que compõem a solução é feita de forma semi-gulosa, ou seja, de forma gulosa, mas também aleatória. Desta forma, são geradas soluções de boa qualidade, devido à característica gulosa, e diversificadas devido à característica aleatória. Nas linhas 6 e 7, o vértice selecionado é removido de φ' e em seguida, adicionado à solução. A linha 9 é alcançada caso não existam elementos na LRC. Isto significa que não existem mais elementos para serem adicionados à solução, logo o

clico de iteração é interrompido a partir do redirecionamento para linha 10. Ao término das iterações, na linha 10, é retornada a solução construída.

Algoritmo 4: FaseConstrução (

'

,

,

) Entrada: uma lista de vértices (

'

), espaço disponível para materialização (

), grau de gulosidade para definir a LRC (

) Saída: melhor solução encontrada

1 solução

;

2 ENQUANTO (Tamanho(solução) <

) FAÇA 3 RCL CriarLRC(

'

,

,

); 4 SE (NÃO EstáVazio(LRC)) ENTÃO 5   SelecionarAleatoriamente (LRC); 6

'

' - ; 7 solução  solução  ; 8 CASO CONTRÁRIO 9 IR PARA LINHA 10; 10 RETORNE melhorSolução;

Figura 3.10: Algoritmo Fase Construtiva do GRASP

O procedimento CriarRCL inicializa a LRC que será retornada como um conjunto vazio de vértices. Em seguida, são obtidas, dentre os vértices de φ', os valores de densidade máxima e a mínima. O limite de gulosidade é dado pela seguinte fórmula , na qual é o grau de gulosidade e e são respectivamente, os valores de densidade máxima e a mínima. Este limite de gulosidade estabelece os elementos que comporão a LRC. São percorridos todos os vértices em φ' e adicionados à LRC desde que a densidade do vértice não seja maior que o limite de gulosidade e que seu tamanho somado ao tamanho da solução seja maior que o espaço disponível para materialização. Caso algum vértice possua densidade maior que o limite de gulosidade, o procedimento encerra e retorna a LRC construída.

O processo de obtenção da LRC também pode ser ilustrado graficamente na Figura 3.11, na qual os extremos da lista representam a densidade máxima e mínima encontradas nos vértices de φ'. Os itens da esquerda são os vértices que possuem densidade entre a máxima encontrada e o limite de gulosidade. Em resumo, os vértices que comporão a LRC, são aqueles cujas densidades estão entre a densidade máxima e o limite de gulosidade.

Figura 3.11: Lista Restrita dos Candidatos LRC

A segunda fase do GRASP, fase de busca local objetiva por refinar a solução obtida na fase de construção para encontrar soluções melhores. Dada uma solução s, a busca por melhores soluções é feita por meio da busca por soluções vizinhas de modo a encontrar na vizinhança, soluções melhores que s. A técnica de busca local utilizada em G consiste em permutar os vértices da solução construída na fase de construção (solução) com os vértices que não foram escolhidos para compor a solução (os vértices de φ'). Usando φ' e solução, duas LCRs, ExtRCL e IntRCL, são construídas respectivamente. ExtRCL consiste nos vértices de φ' com melhor densidade, enquanto

IntRCL é composta dos vértices com menor densidade contidos em solução. O objetivo

é trocar vértices de baixa densidade de solução por vértices de alta densidade que estão fora de solução.

Diferente da técnica utilizada em G, foi proposta uma nova técnica de busca local por causa da baixa eficiência das trocas realizadas em G, como falado anteriormente no início desta seção. O algoritmo da nova técnica é exibido na Figura 3.12. Ele consiste em ordenar os vértices presentes em φ' e solução e localizar permutações nas listas ordenadas. A ordenação se dá pelo valor do vértice de maneira decrescente e em caso de empate, pelo tamanho do vértice de maneira ascendente, como realizado na linha 1. Graças à ordenação dos elementos, é possível conhecer de antemão, por meio do elemento corrente, se não for possível realizar a troca com ele, também não será possível realizá-la com os elementos subseqüentes a ele. Na linha 2, uma lista booleana de tamanho igual à soma dos tamanhos das duas listas (φ' e solução) é inicializada com todos valores iguais a falso. Esta lista serve para marcar os elementos que durante a execução foram selecionados para troca (listaSelecionados). Na linha 3, dois ponteiros são inicializados (posInt e posExt). O posInt é utilizado para percorrer a

lista ordenada de vértices em solução, do final da lista ao início da lista. O posExt é utilizado para percorrer a lista ordenada de vértices em φ' do início da lista até o final da lista. Nas linha 4 e 5, seqüencialmente são inicializadas a variável de controle das iterações (éParada) e a lista para armazenar as trocas encontradas durante as iterações (listaTrocas).

Uma vez inicializadas todas as variáveis do algoritmo, as iterações do algoritmo são executadas da linha 6 à linha 25. No início da iteração, o ponteiro é atualizado, para apontar para o próximo vértice em φ' que não tenha sido selecionado para troca. Por meio dos ponteiros e do procedimento Obter, é obtido o vértice que é apontado pelo ponteiro. Os procedimentos Valor e Tamanho recebem como parâmetro de entrada um vértice e retornam respectivamente, o valor e o tamanho deste vértice. O procedimento

TamanhoMais retorna o tamanho do vértice somado ao espaço restante em solução. O

espaço restante representa o espaço disponível em solução para receber outros vértices de modo que o tamanho de solução são seja maior que o espaço disponível para materialização. O espaço restante é calculado subtraindo o espaço total disponível para materialização (

) pelo tamanho de solução. O tamanho de solução é calculado pelo somatório do tamanho de todos os vértices contidos em solução.

Na linha 10, é verificado se o vértice dentro da solução (ElmInt) possui o mesmo valor do vértice de fora (ElmExt). Desta forma, caso eles possuam o mesmo valor, na linha 11, é verificado se ElmInt possui um tamanho maior que ElmExt, e caso positivo, a troca é registrada (linha 12) pois com a troca tem-se um ganho de espaço restante em

solução sem prejuízo de valor em solução. Entretanto, caso ElmInt não possua tamanho

maior que ElmExt, as iterações cessam (linha 14). Isto ocorre porque devido à ordenação dos elementos, os próximos elementos a serem percorridos em φ' possuem tamanho igual ou maior que ElmExt, o que inviabiliza a permutação com qualquer elemento em φ'. Quando o valor de ElmInt é maior que o valor ElmExt , na linha 15, as iterações também cessam. Isto acontece por causa da ordenação dos vértices, e os próximos vértices a serem percorridos em φ' possuem valores iguais ou menores que

ElmExt, e assim inviabilizam qualquer permutação com ElmInt ou qualquer elemento

presente em solução. Na linha 18, se o valor de ElmInt for menor que o de ElmExt e o tamanho de ElmInt somado ao espaço restante de solução for maior que o tamanho de

Algoritmo 5: FaseBuscaLocal (

'

,

, solução) Entrada: uma lista de vértices (

'

), espaço disponível para materialização (

),

solução construída na fase de busca local (solução), Saída: solução igual ou melhor que (solução) 1 OrdenarPorValorDescETamanhoAsc(solução,

'

) ; 2 AtribuirATodos(listaSelecionados,falso);

3 posInt  NúmeroItens(solução) - 1; posExt  0; 4 éParada  false;

5 listaTrocas

;

6 ENQUANTO (NÃO éParada) FAÇA

7 posExt  ObterPróximoElementoNãoSelecionado(listaSelecionados,posExt); 8 ElmInt  Obter(solução,posInt);

9 ElmExt Obter(

'

,posExt);

10 SE (Valor(ElmInt) = Valor(ElmExt)) ENTÃO

11 SE (Tamanho(ElmInt) > Tamanho(ElmExt)) ENTÃO

12 RegistrarTroca(listaSelecionados, listaTrocas, posInt, posExt) 13 CASO CONTRÁRIO

14 éParada  verdade;

15 CASO CONTRÁRIO SE (Valor(ElmInt) > Valor(ElmExt)) ENTÃO 16 éParada  verdade;

17 CASO CONTRÁRIO SE (SizePlus(ElmInt) > Tamanho(ElmExt)) ENTÃO 18 RegistrarTroca(listaSelecionados, listaTrocas, posInt, posExt)

19 CASO CONTRÁRIO 20 LocalizarCombinação(posInt, posExt); 21 SE posExt >= NúmeroItens(

' ) ENTÃO 22 posInt  posInt - 1; 23 posExt  0; 24 SE posInt < 0 ENTÃO 25 éParada  true; 26 RETORNE ExecutarTrocas(listaTrocas,

' , solução);

Figura 3.12: Algoritmo da Fase de Busca Local

Neste ponto do algoritmo, foram feitas comparações sobre o tamanho e o valor dos vértices ElmInt e ElmExt para averiguar os casos nos quais é possível realizar troca entre estes vértices. Todavia, ainda existe um caso no qual ElmInt possui valor menor que ElmExt e o tamanho de ElmInt somado ao espaço restante de solução é menor que o tamanho de ElmExt, ou seja, ElmExt é muito grande para realizar troca. Neste caso, é executado, na linha 20, o procedimento LocalizarCombinação. Este procedimento

busca vértices mais próximos a ElmInt, na lista de ordenação, de maneira a encontrar uma combinação de vértices que juntos possuam o tamanho superior a ElmExt e valor menor que ElmExt. Tendo sido encontrada esta combinação, o procedimento

LocalizarCombinação executa internamente o procedimento RegistrarTroca para

registrar a troca. O procedimento RegistrarTroca armazena em listaTrocas as trocas a serem realizadas e ajusta os ponteiros para continuar a busca por novas permutações. O ajuste dos ponteiros é feito pelo código das linhas 22 e 23.

No final de cada iteração do algoritmo, é feita a checagem dos ponteiros. Nas linhas 22 e 23, os ponteiros são ajustados, caso o ponteiro posExt tenha percorrido todos os elementos em φ' e não tenha encontrado qualquer permutação compatível com

ElmInt atual. Além disso, na linha 25, caso seja verificado que todos os elementos em solução foram percorridos, i.e posInt < 0, então as iterações cessam. Por fim, o

procedimento ExecutarTrocas realiza todas as trocas registradas em listaTrocas e retorna a solução resultante das trocas realizadas. Esta solução é o retorno deste algoritmo.

Documentos relacionados