• Nenhum resultado encontrado

Algoritmo Híbrido para o Problema de Roteamento de Veículos com

Roteamento de Veículos com frotas

Heterogêneas

Para este trabalho usaremos um dos algoritmos meméticos propostos por Prins (2009) chamado SMA. Ele se utiliza do procedimento de splitting que permite avaliar as soluções, representadas por cromossomos. Para entendermos como funciona o procedimento splitting para o PRVFH, vamos entender primeiro como ele se processa num contexto de roteamento de veículos. Posteriormente, descreveremos os operadores utilizados e finalizaremos com o algoritmo proposto.

3.1 Algoritmo Memético para o PRVFH

3.1.1 Splitting para o problema de roteamento de veículos

Splitting é um procedimento que visa particionar uma rota gigante em rotas menores que serão exibidas em um grafo auxiliar. Uma rota gigante é vista como um percurso de um único veículo que parte do depósito e retorna ao mesmo depois de atender todos os clientes. Esse contexto se refere ao bastante conhecido Problema do caixeiro viajante (PCV).

A partir daí, vamos subdividir essa rota em rotas menores, que devem respeitar as restrições impostas pelo problema. Cada rota formada é vista como um caso particular de PCV.

As rotas formadas devem obedecer às seguintes condições:

( 6 ) ( 5 )

30 Onde Ci,..., Cj é o conjunto de clientes que formam a rota do vértice i ao j, Wij a

demanda total do cliente C1 a Cj, Q a capacidade do veículo que serve as cidades.

A primeira equação garante que a demanda das cidades não exceda a capacidade dos veículos. A segunda equação afirma que o custo Zij da subseqüência equivale ao

comprimento da mesma, Lij, que compreende a distância do depósito a cidade i, da

cidade i a cidade j, e o retorno da cidade j ao depósito.

Essas rotas vão compor um grafo auxiliar acíclico H= (X, A) com n+1 vértices, numerados de 0 a n, onde o vértice 0 representa o depósito.

Um ótimo Splitting é dado pelo caminho de custo mínimo do vértice 0 ao vértice n em H.

Abaixo vemos o Splitting aplicado a um PRV com 5 (cinco)clientes, de custos indicados nas arestas e demandas dentro de parênteses próximos aos clientes.

Possível solução b(4) 30 c(4) b c 25 10 d(2) 60 d 25 30 55 40 15 a 90 a(5) 20 35 e (7) e Grafo Auxiliar ab:55 cd:95 40 115 150 a:40 b:50 c:60 d:80 e:70 205 0 bc:85 bcd:120 de:90

Figura3.1: Exemplo de splitting num grafo com 5 clientes . À direita vemos o exemplo de uma possível solução. Abaixo vemos o grafo auxiliar que indica em negrito a melhor

solução, isto é, solução com menor valor.

31

3.1.2 Splitting para o PRVFH

O procedimento de Splitting para o PRVFH é um pouco mais complexo. O grafo auxiliar H possui um arco para cada viagem (Ci, Ci+1,..., Cj) tal que Wij ≤ Qt, isto é, a

demanda total Wij do cliente Ci a Cj não excede a capacidade do veículo, Qt.

No geral, a demanda de clientes que precisam de um veículo do tipo k ou maior não deve exceder a capacidade total desses veículos. De acordo com o Prins (2009), os tipos de veículos são organizados em uma ordem crescente de capacidade.

Para cada tipo de veículo k, 1≤ k ≤ t ( 7 )

( 8 )

Um ótimo Splitting corresponde ao caminho de menor custo do vértice 0 a n em H, com não mais do que ak arcos para cada veículo de tipo k. Isto é, na verdade estamos

utilizando o Problema de menor caminho com restrições de recurso (PMCRR), onde cada tipo de veículo pode ser visto como um recurso disponível em ak unidades e cada

arco requer uma unidade de um tipo de veículo compatível.

Para resolvermos este problema, utilizaremos o método de programação dinâmica, que nada mais é que uma técnica de otimização que utiliza uma equação recursiva para particionar um problema difícil em problemas menores, mais tratáveis.

Seja P(j, x1,..., xt) o custo do ótimo splitting para a subseqüência (C1, C2,...,Cj), com 1≤ j ≤ n e uma frota mista com xk veículos de cada tipo k, 0 ≤ xk ≤ ake 1≤ k ≤ t.

Este problema pode ser definido pelas seguintes equações recursivas:

P(0, x1, ..., xt) = 0, 1≤ k ≤ t, 0 ≤ xk≤ ak ( 9 )

j > 0, P(j, x1,..., xt) = min ( P( i, x1,…,xk-1, …, xt) + Zijk / i ≤ j, Wij≤ Qk,

32 A equação (9) indica que o custo em relação ao depósito é zero. A equação (10) exprime o custo de ir do vértice 0 ao vértice j, que é o menor valor entre o custo de chegar em i adicionado do custo de ir de i a j com o veículo k (Zijk).

Seja ψ o número máximo de distintos vetores x, isto é, o número máximo de

vetores do tipo ( x1, x2,..., xt ) distintos. Então

(11)

A equação (11) é válida, pois para um vetor x = ( x1, x2,..., xt ) com 0 ≤ xk≤ ak,

temos o vetor a = ( a1, a2, ..., at ) definindo o limite superior de cada posição do vetor x, com 0 ≤ ak ≤ k. Então existem ak + 1 valores diferentes que xk pode assumir. Como o

vetor x tem t posições, para a posição 1 (x1), tem-se a1 + 1 maneiras, para x2 tem-se a2 +

1 maneira, e assim por diante. O que resulta na equação (7).

Abaixo vemos um exemplo com 3 (três) tipos de veículos com disponibilidades de: 2 veículos para o 1º tipo; 1 veículo para o 2º tipo e 3 veículos para o 3º tipo.

Exemplo: Quantos são os possíveis vetores x distintos? x = ( x1, x2, x3 ) , t=3 e a = ( 2, 1 ,3).

x1 poderia assumir: ( a1+1) = 2+1= 3 valores, que seriam 0, 1 e 2.

x2 poderia assumir: ( a2+1) = 1+1= 2 valores, que seriam 0, 1.

x3 poderia assumir: ( a3+1) = 3+1= 4 valores, que seriam 0, 1, 2 e 3.

Então poderíamos ter 3 * 2 * 4 = 24 vetores distintos.

Os cálculos podem ser organizados usando uma matriz de rótulos Y, de ordem

(n + 1) x ψ. As linhas indexadas de 0 a n correspondem aos clientes e as colunas

indexadas de 0 a ψ – 1 correspondem aos vetores que indicam as quantidades de carros usados do tipo 1 ao tipo t, para cada coluna.

Um rótulo Yjh representa o menor caminho do vértice 0 a j no grafo auxiliar H.

Cada rótulo guarda a informação referente ao menor caminho usado para chegar naquele vértice, que é o custo (Yjh.Z) e o vetor que representa a frota parcial usada para

aquele caminho (Yjh.x).

Para facilitar o manuseio e diminuir a complexidade, usa-se uma função h que associa um vetor referente a uma coluna a um número inteiro. Então em vez de trabalhar

33 com colunas indexadas por vetores, usa-se números que representam esses vetores. Assim, usa-se a matriz Y de colunas numeradas de 0 a ψ.

Essa função h pode ser calculada segundo as seguintes equações recursivas: h (x, 0) = 0 ( 12 ) h( x, k) = h(x, k-1)(ak +1) +xk, para 0≤ k ≤ t. ( 13 )

Para melhor entendimento de h(x), vamos utilizar um exemplo com a = (2, 3, 1). Se fossemos avaliar os possíveis vetores de x, e organizássemos os vetores variando da última casa, x3, para a primeira, x1, teríamos:

(0, 0, 0) (0, 0, 1); (0, 1, 0) (0, 1, 1) (0, 2, 0) (0, 2, 1) (0, 3, 0) (0, 3, 1); (1, 0, 0)(1, 0, 1)... Observe que os vetores poderiam ser organizados de outra forma. A quantidade total de vetores distintos seria (2+1)*(3+1)*(1+1) = 24. Para se chegar nas variações de x1 teríamos que passar por todas as combinações possíveis para a segunda e terceira

casa. Ou seja, para se chegar em x1 é preciso ter passado por (a2 +1)*(a3+1) números.

Para facilitar, sejam A1= a1 +1, A2= a2 +1 e A3= a3 +1.

Então, para se ter x1 = p, precisamos passar por p* A2* A3. O mesmo se quisesse

chegar em p na segunda casa: passaríamos por p* A3 números primeiro. E assim por

diante. Então para se ter um número p na primeira casa (x1) de um vetor de t posições,

teríamos que passar por p* A2* A3*...*At.

Agora, se quiséssemos chegar em p na primeira casa e r na segunda casa, teríamos que passar por p* A2*...*At + r* A3*...* At. Então

A seguir vê-se um exemplo que simplifica a obtenção da função h(x): (14)

34 Exemplo: Para t=3:

h(x1,x2,x3) = x1 *A2*A3 + x2*A3+ x3 = A3*(x1*A2 + x2) + x3

Chamemos de h(x,k). h(x,3) = A3*(h (x,2)) + x3

Por indução chegamos em h(x,k) = Ak* h( x,k-1) + xk = h(x, k-1)* (ak+1) + xk.

Então, tudo isso foi para entendermos o significado dessa fórmula.

Se quiséssemos saber o número inteiro que representará o vetor (2,1,1) com a = (2,3,1), teríamos:

h(x,3) = h(x, 3)*(a3 + 1) + x3 = [2*(a2 + 1) + 1]*(a3 + 1)+x3= [2*4 + 1]*2 + 1= 19.

Após o cálculo de h, podemos então observar o funcionamento do Splitting. A figura 3.2 descreve o algoritmo do Splitting:

Figura3.2: Algoritmo Splitting para o PRVFH.

A matriz Y é inicializada com todos os vetores x recebendo 0, os custos dos rótulos na fila 0 com 0 e os custos dos outros rótulos com infinito. Então, para cada vértice i, para cada arco do grafo H, cada rótulo Yip usado por i, e cada possível veículo

Inicializa matriz Y Para t:= 0 a n faça

Para cada arco (i,j) de H faça

Para p:= 0 a ψ – 1 com Yip.Z < ∞ faça

Para k:= 1 a t com ( Yip.xk < ak ) e ( Wij≤ Qk ) faça

U := Yip;

U.xk := U.xk + 1;

U.Z := U.Z + fk + vk. Lij;

Calcule h para o consumo do vetor U.x; Se U.Z < Yjh.Z então Yjh := U; Fim; Fim; Fim; Fim.

35 do tipo k, um rótulo U é calculado para o caminho obtido pela adição do arco (i,j) com veículo k para o caminho definido pelo rótulo Yip.

U é inicializado como uma cópia de Yip, U.xk é incrementado e o custo do arco ( i,j )

para o veículo k é adicionado em U.Z. Então chamamos a função h para o vetor U.x e o rótulo Yjh é substituído por U em caso de melhoria.

3.1.3 Seleção por torneio binário

Dois cromossomos da população atual são escolhidos aleatoriamente. O melhor cromossomo (melhor fitness) escolhido será o primeiro Pai. De forma análoga, o segundo pai será escolhido.

3.1.4 Order crossover (ox crossover)

Neste trabalho, utilizaremos o operador order crossover. Esse operador é inicializado com a seleção aleatória de dois pontos nos cromossomos (pais). Chamemos esses pontos de i e j. Esses pontos determinam uma substring que será copiada na mesma posição nos filhos.

Para o primeiro filho, por exemplo, observamos o segundo pai e retiramos as cidades que já estão na substring desse filho. As cidades restantes serão adicionadas ao filho respeitando a cidades sucessivas nas posições da esquerda para direita a partir da posição j + 1. Um procedimento similar é feito para o segundo filho.

Para entendermos melhor, se considerarmos os cromossomos Pai 1 e Pai 2:

i j

Pai 1

Pai 2

Figura 3.3: Cromossomos pais.

As substrings escolhidas são 3, 4 e 5 e 1, 3 e 4.

Então os filhos receberão as substrings nas respectivas posições:

1 2 5 6 5 3 4 3 1 6 2 7 4 7

36 Filho 1

Filho 2

Figura 3.4: Início do processo do ox crossover.

Agora para o primeiro filho, observamos o segundo pai, pai 2, que possui as seguintes cidades: 5 6 1 3 4 7 2. Então excluímos as cidades 3, 4 e 5, e acrescentamos as cidades restantes na ordem de sucessão a partir da posição j+1 . O processo do segundo filho é idêntico a este.

Filho1

Filho 2

Figura3.5: Cromossomos resultantes do ox crossover.

A partir daí, um desses filhos é selecionado aleatoriamente e será avaliado pelo procedimento Split, que depois substituirá um cromossomo Pr encontrado na pior metade da população, isto é, entre os nc/2 últimos cromossomos. Finalmente, C é trocado para manter organizado em uma ordem de custo crescente.

6 5 4 3 3 4 1 1 3 4 5 7 2 7 4 3 1 5 2 6

37

3.1.5 Busca Local

Neste trabalho, a busca local é utilizada numa probabilidade PLS para o filho

resultante do ox crossover e é realizada após o procedimento Splitting, da mesma forma com que é feito em Prins (2009).

Cada iteração da busca local decompõe os movimentos de realocação de clientes, troca de dois clientes e movimento 2-OPT, que executam movimentos para uma ou duas viagens e promovem melhorias. Esse processo é repetido até não haver mais melhoria. As figuras abaixo ilustram esses movimentos para 2 viagens:

Figura 3.6: Esquema de realocação de um cliente em duas viagens.

38

Figura 3.8: Esquema do 1º caso 2-OPT. Neste movimento 2-OPT ocorre uma inversão no sentido de uma viagem.

Figura 3.9: Esquema do 2º caso 2-OPT.

As viagens são finalmente concatenadas em um único cromossomo (rota gigante) que é re-avaliado pelo Splitting. O Splitting se comporta como um operador de melhoria adicional capaz de trocar todos os limites de viagens e reembaralhar veículos.

A busca local utilizada no algoritmo memético é a LS1, que não permite

mudança nos veículos designados para as viagens. Após os movimentos da busca local, obtemos novas distancias entre os vértices e, portanto teremos novos custos fixo e variáveis a serem calculados. O custo fixo é removido se uma viagem se tornar vazia.

39

3.1.6 Algoritmo Memético (SMA)

Utilizaremos o segundo algoritmo proposto pelo Prins (2009), SMA. É um algoritmo memético com population management (AM/PM), uma nova estrutura de algoritmo memético desenvolvida por Sorensen e Sevaux (2006). Essa estrutura controla a diversidade da população usando uma medida de distância no espaço de solução.

As principais características desse algoritmo são a presença de pequenas soluções, o uso da busca local e da population management. Segundo Sorensen e Sevaux (2006), a population management “controla a diversidade de uma pequena

população com soluções de alta qualidade”.

Para avaliar se uma solução diversifica a população, AM/PM usa uma medida de distância no espaço de solução. Então dada uma medida de distância, e duas soluções A e B, definimos que d(A,B) é a distância entre essas soluções.

A distância de uma solução C a população P é determinada por

D(P,C) = min {d(A, C) :A ϵ P}, (15)

que significa que a distancia de uma solução C a população P é a menor distancia entre C e uma solução que já qualidade suficiente, pois pertence a P.

Para calcular a distância de uma solução à população calculamos |P| medidas de distância. |P| representa a cardinalidade da população.

Para que uma solução seja adicionada a população, é necessário que sua distância a população seja menor que um parâmetro ∆, chamado parâmetro de diversidade. Assim, evitamos que a solução possua uma distância pequena em relação à população, o que não contribuiria com a diversidade da mesma (Sorensen e Sevaux, 2006).

Então um filho C é aceito a população se

D(P, C) ≥ ∆. (16)

Neste trabalho, o conceito de distância utilizado é a distância para R permutações, que provém de Campos et al (2005), comumente chamada de distância de pares quebrada.

Para dois cromossomos A e B, d(A, B) é a quantidade de pares adjacentes em A que são quebrados em B, isto é, que não estão na mesma ordem de sequência em B. Por exemplo, tomando A = (1,2,3,4,5,6) e B = (4,5,6,2,3,1), (1,2) e (3,4,) são quebrados em

40 B, isto é, eles não estão na mesma ordem em B. Então d(A,B) = 2. A distância pode variar entre 0 e n-1.

O algoritmo de SMA é descrito abaixo:

 A população inicial é constituída de nc cromossomos (rotas gigantes), onde

cada uma é armazenada em Pk.T.

 A população P é mantida organizada em uma ordem ascendente de custo: a

melhor solução atual é sempre P1.

 O procedimento Splitting (Pk, possível) avalia a rota gigante Pk.T e se existir

uma solução viável, a solução resultante é armazenada em Pk.S e o custo em

Pk.F. Selecionamos as três primeiras rotas gigantes que serão melhoradas pela

aplicação da busca local LS1, descrita em (3.4). Essas soluções promovem

rapidez na busca e as restantes (nc -3 soluções) trazem mais diversidade.

 Repetimos esses passos para cada cromossomo na população inicial e Pk só é aceito na linha 6 se D({P1,...,Pk-1}, Pk) ≠ 0.

 O próximo laço executa np fases, onde cada uma começa organizando a

população em ordem crescente de custos, executados em ni iterações básicas e finaliza por uma renovação parcial da população. O procedimento renovação_parcial mantém as nk melhores soluções e substitui as nc - nk outras por novas soluções aleatórias.

 Dois pais são selecionados pelo método do torneio binário descrito em (3.3).

Através do operador ox crossover descrito em (3.4), esses pais vão gerar um filho C que será avaliado pelo Split.

 Se possível, a nova solução sofre busca local com uma probabilidade fixa de

PLS. Selecionamos aleatoriamente um cromossomo Pr na pior metade de P.  Se D(P\{Pr},C) ≥ ∆ ou o custo do filho C for menor que o da melhor solução,

guardado em P1 (isso evita a perda de uma melhor solução), Pr receberá C e Pr

será trocado para manter P ordenado.

 No fim, a melhor solução está em P1.

Nós consideramos que o valor do parâmetro de diversidade, ∆, é inicializado com um valor constante. A estratégia é que ∆ decresça linearmente de um ∆máx para um

∆min em cada fase. ∆máx será limitado na prática por uma fração de distância máxima, isto é, ∆máx = 0,5.(n-1).

41 A tabela abaixo indica os parâmetros usados neste algoritmo:

Método nc np ni Nk LS PLS política ∆min ∆máx

SMA 30 5 4000 1 1 0.50 decrescente 1 0.5.(n-1)

Tabela 3.1: Parâmetros do algoritmo.

O pseudocódigo do SMA está descrito abaixo:

Figura 3.10: Estrutura do algoritmo memético SMA.

Para k:= 1 até nc faça Repita

Geração aleatória da rota gigante em Pk.T

Split (Pk, possível)

Se (possível) e (k ≤ 3) então LS1(Pk)

Fim-se;

Até (possível) e (D ({P1,..., Pk-1}, Pk) ≠ 0 )

Fim_para;

Para p:= 1 até np faça

Organize P em ordem crescente de custo Para i:= 1 até ni faça

Selecione dois pais A e B pelo torneio binário

Aplique Ox crossover Para A e B para gerar um filho C Split (Pk, possível)

Se possível então

Se aleatório < PLS então LS1(C)

Fim_se;

Selecione aleatoriamente Pr para substituição, na pior metade de P.

Se (C.F < P1.F) ou (D(P\{Pr},C) ≥ ∆) então

Pr := C

Troca Pr para manter P ordenado

Fim_se; Fim_se;

Fim_para;

Renovação_parcial (P, nk); Fim_para;

42

3.2 Algoritmo Híbrido proposto

O algoritmo híbrido proposto se utiliza do algoritmo memético SMA associado ao Vocabulary Building. A idéia é inserir a técnica utilizando a noção de contração de vértices utilizada anteriormente por Guedes (2006) e Neto (2009) num contexto de Problema do Caixeiro Viajante, e posteriormente por Girão (2008) e Oliveira (2010) num contexto de Anéis Sonet.

Abaixo se descreve o processo de contração de vértices proposto neste trabalho:

 Selecionamos as melhores soluções que vão compor um conjunto elite.  Identificamos as arestas presentes em todas elas.

 Concatenamos os trechos formados por arestas consecutivas.

 Criamos um grafo auxiliar em que cada trecho representará um único

vértice.

 Criamos um conjunto de soluções para este grafo auxiliar através de

heurísticas construtivas e busca local. A esse conjunto damos o nome de população auxiliar.

 Expandimos os nós contraídos em trechos para trazer as soluções

produzidas no grafo auxiliar para soluções viáveis no grafo original.

 Aplicamos a busca local nas soluções da população auxiliar (já

expandida).

 Exclui-se a pior solução da população auxiliar e uni-se com a melhor

43 Abaixo ilustramos o processo de identificação de arestas comuns em quatro soluções de um PRVFH:

Figura 3.11: Exemplo de identificação de arestas comuns.

A seguir vemos como cada trecho representará um único nó:

44 A idéia é identificar fragmentos de boas soluções a fim de obter soluções ainda melhores. Abaixo vemos a estrutura do algoritmo híbrido proposto:

Figura 3.13: Estrutura do algoritmo híbrido proposto.

Para k:= 1 até nc faça Repita

Geração aleatória da rota gigante em Pk.T

Split (Pk, possível)

Se (possível) e (k ≤ 3) então LS1(Pk)

Fim_se;

Até (possível) e (D ({P1,..., Pk-1}, Pk) ≠0)

Fim_para;

Organize P em ordem crescente de custo Para i:=1 até ni faça

Selecione dois pais A e B pelo torneio binário Aplique ox crossover para A e B gerar um filho C Split (Pk, possível)

Se possível então

Se aleatório < PLS então LS1(C)

Fim_se;

Selecione aleatoriamente Pr para substituição, na pior metade de P

Se (C.F < Pi.F) ou (D (P\{Pr} , C) ≥ ∆) então

Pr:= C

Troca Pr para manter P ordenado

Fim_se; Fim_se; Fim_para; Contração_de_vértices (pop_atual); Renovação_parcial(P, nk); Fim_para.

45

Capítulo IV

Documentos relacionados