• Nenhum resultado encontrado

A tarefa de implementar o GRASP para o PMM, já definido na aula8, ficou muito fácil, pois boa parte do código já foi implementado na aula9, e como a busca local do GRASP pode ser determinada conforme a necessidade e o problema, então é possível utilizar uma das buscas locais já implementadas, como o método da melhor melhora ou da

primeira melhora. A primeira função que deve ser implementada, é a responsável

pela heurística aleatória gulosa, pois como pode ser observado na linha 4 do pseudo-código, esta heurística é uma função acessória que será necessária na implementação do GRASP. Desta forma, siga os passos a seguir para implementar a função responsável pela heurística construtiva aleatória gulosa:

Passos

1. Copie o código-fonte disponibilizado a seguir;

2. Cole o código em um editor de textos plano de sua preferência;

3. Salve o arquivo com o nome: heuConAleGul.py na pasta C:\AulaPython.

1 import numpy as np 2 import random as rand 3

4 #gerando a semente

5 rand.seed() 6

7 def heuConAleGul(lrc, numObj, numMoc, vetValObj, vetPesObj, vetCapMoc):

8 #montando a LRC

9 vetAux = [0] * numObj 10 vetVal = [0] * lrc 11 vetLrc = [0] * lrc 12 for i in range(lrc):

13 obj = rand.randint(0, numObj-1)

14 while vetAux[obj] == 1: #impede o sorteio de um numero repetido

15 obj = rand.randint(0, numObj-1) 16

17 vetLrc[i] = obj

18 vetVal[i] = vetValObj[obj] 19 vetAux[obj] = 1

20

21 #inserir os objetos da LRC na mochila de forma gulosa

22 #obtendo os indices dos objetos ordenados (descendente)

Aula 10. Meta-heurística GRASP 143

24 sol = [0] * numObj 25 vetPes = [0] * numMoc 26 for i in range(lrc):

27 for j in range(numMoc):

28 if (vetPes[j] + vetPesObj[vetLrc[vAuxIndLRC[i]]]) <= vetCapMoc[j]: 29 sol[vetLrc[vAuxIndLRC[i]]] = j

30 vetPes[j] = vetPes[j] + vetPesObj[vetLrc[vAuxIndLRC[i]]] 31 break

32

33 #inserir os demais objetos na mochila de forma gulosa

34 vAuxIndObj = np.argsort(vetValObj)[::-1] 35 for i in range(numObj):

36 if vetAux[vAuxIndObj[i]] == 0: #se verdadeiro, entao nao foi alocado ainda

37 for j in range(numMoc):

38 if (vetPes[j] + vetPesObj[vAuxIndObj[i]]) <= vetCapMoc[j]:

39 sol[vAuxIndObj[i]] = j

40 vetPes[j] = vetPes[j] + vetPesObj[vAuxIndObj[i]]

41 break

42

43 return sol

A heurística construtiva aleatória e gulosa teve que ser implementada, pois ela deve construir a solução com base na LRC, que é uma particularidade do GRASP, além disso, cada problema demandará um tratamento diferente. Para o PMM, esta implementação aqui apresentada, é uma possível solução, dentre várias. Se observar atentamente o código, perceberá que essa heurística é uma adaptação da heurística construtiva gulosa que foi implementada na aula9, a diferença, é que neste caso, precisamos considerar a lista restrita de candidatos, LRC. Veja que entre as linhas 8 e 19, foi construida a lista

LRC de forma aleatória. Na linha 12 foi realizado um laço com o número de iterações

do tamanho da LRC e para cada iteração é sorteado um item para compor a lista. O laço

while na linha 14 é para garantir que em cada iteração seja sorteado um objeto que não

tenha sido sorteado ainda. Após sortear um objeto, na linha 17, este é armazenado na lista LRC, na linha 18 é registrado o valor do objeto que foi sorteado e na linha 19, o objeto é marcado, como já tendo sido sorteado.

Após construir a lista LRC, entre as linhas 21 e 31, a solução é registrada em sol, de forma gulosa apenas com os itens que foram incluídos na lista restrita de candidatos. Observe que antes de incluir, é realizada uma validação da capacidade atual da mochila, na linha 28, se o objeto couber então ele é armazenado na mochila, na linha 29, depois o peso do objeto é adicionado ao peso da mochila, na linha 30, e o laço é interrompido para evitar que o objeto seja armazenado em outra mochila, na linha 31.

Após armazena os itens da LRC na solução, o procedimento é novamente realizado, entre as linhas 33 e 41, mas agora com o objetivo de alocar o restante dos objetos. Observe que o processo é praticamente igual ao anterior, a diferença é que em cada objeto visitado no laço da linha 35, é realizada uma verificação para validar se o objeto não foi armazenado em alguma mochila, na linha 36, caso não tenha sido, então é um laço percorre as mochilas, na linha 37 para verificar se o objeto cabe em alguma e alocá-lo, se for o caso. Ao terminar este último laço pelos objetos, a solução estará completa e pronta para ser devolvida ao método que invocou a função, conforme a linha 43. O próximo passo é a implementação do método GRASP, faça:

Aula 10. Meta-heurística GRASP 144

Passos

1. Copie o código-fonte disponibilizado a seguir;

2. Cole o código em um editor de textos plano de sua preferência; 3. Salve o arquivo com o nome: grasp.py na pasta C:\AulaPython.

1 import time

2 import calcFO as CF

3 import heuConAleGul as HCG 4 import heuPM as PM

5

6 def grasp(lrc, tempo, numObj, numMoc, vetValObj, vetPesObj, vetCapMoc): 7 ini = time.time()

8 achouT = time.time()

9 melhorFO = float("-inf") #obtendo valor infinito negativo

10 lrc = (lrc * numObj) / 100 #calculando o % da LRC com base nos objetos

11 mSol = [0] * numObj 12

13 while 1:

14 sol = HCG.heuConAleGul(lrc, numObj, numMoc, vetValObj, vetPesObj, vetCapMoc)

15

16 #busca local

17 sol = PM.heuPrimeiraMelhora(sol, numObj, numMoc, vetValObj, vetPesObj, vetCapMoc)

18 FO = CF.calcFO(sol, numObj, numMoc, vetValObj, vetPesObj, vetCapMoc) 19 20 if FO > melhorFO: 21 mSol = sol 22 melhorFO = FO 23 achouT = time.time() 24 25 fim = time.time()

26 #verifica se deve continuar executando

27 if fim <= (ini + tempo): 28 continue

29 else:

30 break

31

32 return mSol, (achouT - ini)

O código-fonte do GRASP em Python tem as mesmas características do código base já visto, o que muda, são as questões relacionadas à arquitetura da linguagem e os detalhes relativos ao PMM, como por exemplo, os parâmetros que são específicos do problema, número de objetos, número de mochilas, valores dos objetos, etc. O GRASP, aqui implementado, adota o tempo como estratégia para manter o laço ativo, por isso, foi importada a biblioteca com funções de tempo, na linha 1, foi também inicializada uma variável com a hora atual na linha 7 e foi verificado se o laço continua ou não, por meio do tempo, entre as linhas 25 e 30, observe que a variável tempo utilizada na linha 27 é um argumento da função GRASP conforme a linha 6. Na linha 9 a variável

melhorFO recebe o valor infinito negativo, pois o PMM é de maximização. Na linha 10 a

Aula 10. Meta-heurística GRASP 145

pois o nosso código, até o presente momento tem sido implementado de forma a permitir configurações diferentes para a quantidade de objetos e mochilas, ou seja, essas informações não são fixas, então é importante converter o tamanho da LRC em termos percentuais.

Na linha 13 inicia o laço while com a condição que será sempre verdadeira, pois como já mencionado, a interrupção do laço será controlada entre as linhas 25 e 30 com base no tempo. Na linha 14 foi invocada a função responsável por gerar a solução de forma construtiva aleatória e gulosa, após obter a solução é realizada a busca local tendo esta como solução inicial, na linha 18 é invocada a função que calcula a FO para a solução gerada e na linha 20 é realizada a verificação se a FO obtida para a solução atual é melhor que a FO já registrada no passo anterior, que no caso da primeira execução, será o valor infinito negativo, e nas demais execuções, será realmente o maior valor observado até o momento. Se a condição da linha 20 retornar verdadeiro, então a solução, até então a melhor, será registrada na variável mSol, a FO da solução atual será registrada em melhorFO e o tempo será registrado em achouT. O objetivo de registrar o momento em que a melhor solução foi encontrada, é poder, por exemplo, avaliar se o tempo de execução do GRASP está sendo suficiente ou não, para encontrar o ótimo local. A seguir, os passos para testar o GRASP com os dados do PMM.

Passos

1. Copie o código-fonte disponibilizado a seguir;

2. Abra o arquivo prgMain.py presente na pasta C:\AulaPython; 3. Cole o código substituindo o código atual do arquivo;

4. Salve o arquivo. 1 import leDados as LD 2 import calcFO as CF 3 import grasp as GR 4 5 numObjetos = numMochilas = 0

6 LRC = 50 #neste exemplo adotou-se uma LRC de 50% dos objetos

7 tempoExec = 10 #neste exemplo adotou-se 10 segundos de tempo de execucao

8 vetValoresObjetos = [] 9 vetPesosObjetos = [] 10 vetCapacidadeMochilas = []

11 numObjetos, numMochilas, vetValoresObjetos, vetPesosObjetos, vetCapacidadeMochilas = LD.leDados()

12

13 #obtendo uma solucao pela meta-heuristica GRASP

14 sol, tempo = GR.grasp(LRC, tempoExec, numObjetos, numMochilas, vetValoresObjetos, vetPesosObjetos, vetCapacidadeMochilas)

15

16 #calculando a FO da solucao obtida

17 FO = CF.calcFO(sol, numObjetos, numMochilas, vetValoresObjetos, vetPesosObjetos, vetCapacidadeMochilas)

18

19 print "Solucao: ", sol 20 print "FO: ", FO

Aula 10. Meta-heurística GRASP 146

Na linha 3 foi importado arquivo do código-fonte do GRASP, na linha 6 foi definida a LRC com 50, ou seja, pretende-se que a lista restrita de candidatos comporte 50% dos objetos. Na linha 7 foi definido o tempo como sendo 10 segundos. O GRASP foi invocado na linha 14, veja que além dos argumentos do PMM, o método recebeu também o tamanho da LRC e o tempo de execução, além disso, observe também que o método retorna dois valores, a solução, armazenada em sol e o tempo para encontrar a solução, armazenado na variável tempo. Na linha 17 foi calculado o valor da FO para a solução e por fim, os valores foram impressos entre as linhas 19 e 21. Agora é possível testar a meta-heurística GRASP, para executar abra o interpretador do Python e execute o programa principal, conforme o trecho a seguir.

1 >>> runfile('C:/AulaPython/prgMain.py', wdir='C:/AulaPython')

2 Solucao: [2, 2, 2, 0, 2, 1, 2, 2, 0, 2, 0, 0, 1, 1, 1, 1, 2, 2, 1, 1] 3 FO: 10357

4 Achou em: 6.35

Como o GRASP foi configurado para executar por 10 segundos, então, as linhas 2, 3 e 4 vão demorar em torno de 10 segundos para aparecer. É possível que, embora tenhamos configurado para executar por 10 segundos, o tempo de execução real não seja exatamente 10 segundos, pois a saída do laço do GRASP só é verificada após executar todos os comandos, inclusive a heurística construtiva e a busca local que são operações que demandam maior processamento, o que pode levar a execução a exceder o tempo configurado.

Na linha 2 foi impressa a melhor solução encontrada pelo GRASP, na linha 3 o valor da FO desta solução e na linha 4 o tempo que levou para encontrar essa solução que foi de 6 segundos aproximadamente, ou seja, as soluções encontradas após esse tempo eram todas com o valor de FO inferior a esta. É importante destacar que, o fato de o método ter obtido a melhor solução em 6 segundos, não quer dizer que o tempo de execução possa ser reduzido para 6 segundos, pois esse tempo provavelmente vai variar nas próximas execuções, assim, para determinar o tempo, é importante executar o GRASP várias vezes, talvez dezenas, para que seja observado o desvio-padrão do tempo, e assim, inferir um valor com maior segurança.