PROBLEMA DA MOCHILA VALIOSA COM VALOR MINIMO DE UTILIDADE

999 views
887 views

Published on

Este trabalho aborda o Problema da Mochila Valiosa, em que temos que decidir dentre os objetos disponíveis quais levar para a viagem na mochila, sendo que não podemos ultrapassar o peso suportado pela mochila e devemos levar o máximo possível de objetos mais valiosos. Foram implementados diversos algoritmos para resolver o problema, indo desde um algoritmo exato, como por Força Bruta, até heurísticas, como os algoritmos Gulosos por Peso, Utilidade e Custo Relativo como também um Algoritmo Genético. No final deste trabalho expomos os testes realizados com várias instâncias e um gráfico comparando os algoritmos.

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
999
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
40
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

PROBLEMA DA MOCHILA VALIOSA COM VALOR MINIMO DE UTILIDADE

  1. 1. UNIVERSIDADE ESTADUAL DO CEARÁ CENTRO DE CIÊNCIAS E TECNOLOGIA CURSO DE GRADUAÇÃO EM CIÊNCIA DA COMPUTAÇÃO JOÃO GONÇALVES FILHO & FELIPE DE ALMEIDA XAVIER TRABALHO DE COMPLEXIDADE: PROBLEMA DA MOCHILA VALIOSA PROFESSOR: VALDISIO VIANA FORTALEZA - CEARÁ 2012
  2. 2. RESUMO Este trabalho aborda o Problema da Mochila Valiosa, em que temos que decidir dentre os objetos disponíveis quais levar para a viagem na mochila, sendo que não podemos ultrapassar o peso suportado pela mochila e devemos levar o máximo possível de objetos mais valiosos. Foram implementados diversos algoritmos para resolver o problema, indo desde um algoritmo exato, como por Força Bruta, até heurísticas, como os algoritmos Gulosos por Peso, Utiliadade e Custo Relativo como também um Algoritmo Genético. No final deste trabalho expomos os testes realizados com várias instâncias e um gráfico comparando os algoritmos.
  3. 3. SUMÁRIO 1 DEFINIÇÃO DO PROBLEMA DA MOCHILA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2 ALGORITMOS PARA O PROBLEMA DA MOCHILA . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1 Força Bruta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2 Programação Dinâmica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.3 Algoritmo Guloso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.4 Algoritmo Genético . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 3 RESULTADOS DOS TESTES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.1 Tempo de execução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.2 Convergência das soluções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 BIBLIOGRAFIA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
  4. 4. 3 1 DEFINIÇÃO DO PROBLEMA DA MOCHILA Figura 1: Problema da mochila. Fonte: Wikipédia No problema clássico 0-1 da mochila (knapsack problem), temos n itens que podem ser carregados na mochila, onde cada item j possui associado um peso p j e um valor de utilidade u j e a mochila possui capacidade c para transportar os itens, nesse trabalho a soma das utilidade dos itens deve ser pelo menos d. Então temos um problema de otimização combinatória onde queremos carregar itens que somem o máximo possível de utilidade sem estourar a capacidade da mochila. Na modelagem desse problema utilizamos um vetor de tamanho n, onde cada elemento do vetor x j é um item, se x j = 1, então o item está na bolsa, senão não está caso x j = 0. Para exemplificar, suponha que Bob esteja indo viajar e sua mochila so comporte 15kg como mostrado na Figura 2, então ele precisa decidir quais itens irá levar consigo, querendo ele somar o máximo possível de dinheiro de seus itens, tendo em mente que ele precisa lever no mínimo d reais em de itens. Podemos modelar o problema utilizando programação linear inteira da seguinte forma: Maximizar z = ∑n u j x j j=1 Sujeito a d ≤ ∑n p j x j ≤ c j=1 x j ∈ {0, 1}, j ∈ {1, ...n} O problema da mochila é NP-Díficil (MARTELLO; PISINGER; TOTH, 2000) existem várias soluções propostas, tanto exatas como de heurísticas, aqui abordaremos o de força bruta, com heurística gulosa, com programação dinâmica e algoritmo genético.
  5. 5. 4 2 ALGORITMOS PARA O PROBLEMA DA MOCHILA Dividimos os algoritmos implementados em duas categorias exatos e heurísticas. Os exatos irão retornar o valor ótimo para o problema, mas terá um custo computacional muito mais alto do que usando uma heurística que tenta chegar o mais próximo possível da solução ótima, os algoritmos gulosos e o de programação dinâmica pode ser visto em (VIANA; MOREIRA, 2011). 2.1 Força Bruta Esse algoritmo exato é o mais trivial de se pensar, a ideia dele é bem simples, teste todas as possibilidades que existem e guarde a melhor, essa será a solução ótima para o problema. Ele pode ser implementado de várias formas, mas nesse trabalho utilizamos contagem de números binários para poder fazer o teste de todas as possibilidades. Como uma solução para mochila representada como vetor de 0’s e 1’s, inicializamos o vetor com 0’s, sendo essa a primeira solução, então para gerar as demais possibilidades é feito um incremento de mais um em binário até que o algoritmo passe por todos os possível valores do vetor. Podemos ver abaixo o pseudocódigo do algoritmo: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 entrada : c: capicidade total da mochila n: numero de itens da mochila d: valor minimo de peso carregado P [1... n ]: vetor dos pesos dos itens U [1... n ]: vetor dos valores de utilidade dos itens saida : um vetor X [1.. n] que representa a solucao encontrada , o valor da solucao encotrada e o somatorio das valores de utilidade que estao na mochila forcaBruta (c ,n ,d ,P [] , U []) totaldeIteracoes = potencia (2 , n ) - 1 melhorSolucao = 0 melhorNumero = 0 para i = 0 ate totaldeIteracoes pegaBinario (X ,n , i) // coloca em X a solucao corrente solucaoCorrente = pegaSolucao (X ,b ,c , d) // retorna o valor da solucao para o vetor atual de X
  6. 6. 5 21 22 23 24 25 26 27 // caso nao seja uma solucao valida , ou seja menor que d , // retorna -1 se ( solucaoCorrente > melhorSolucao ) melhorSolucao = solucaoCorrente melhorNumero = i pegaBinario (X ,n , melhorNumero ) retorne X É fácil notar que a complexidade do pior caso desse algoritmo é de O(2n ), pois como temos um vetor binário de tamanho n, existirão todas essas possibilidades. Por exemplo para n = 3 temos 8 possibilidades, para vermos isso basta aplicarmos um pouco de análise combinatória, para cada posição do vetor temos dois valores possíves 0 e 1, como o tamanho é 3, então calculamos 2 ∗ 2 ∗ 2 = 8. Esse algoritmo é inviável, quando n já chegar a valores altos, o tempo de resposta desse algoritmo será de anos. 2.2 Programação Dinâmica A ideia do uso da programação dinâmica é de reaproveitar soluções anteriores para conseguir as posteriores, guardando elas em uma tabela. Para conseguirmos montar essa tabela transformamos uma recursão em iteração. Para isso definimos a tabela t da seguinte forma: t[i,Y ], onde i = 0, 1..n é a quantidade de itens para essa solução e Y = 0, 1...c é a quantidade máxima de peso carregado para essa solução. Assim notamos que t[0,Y ] = 0 para todo Y , agora se i > 0 temos que: t[i,Y ] = A se p[i] > Y e t[i,Y ] = max(A,B) se p[i] <= Y , onde A = t[i − 1,Y ] e B = t[i − 1,Y − p[i]] + u[i] Quando p[i] > Y indica que esse item tem uma peso maior que a capacidade da mochila para essa solução, então ela tem o mesmo valor que anterior, por isso atribuimos à ela o valor máximo anterior t[i − 1,Y ]. Caso contrário existe capicidade na mochila então ne caso atribuimos à B o valor de solução máxima com Y − p[i] de capacidade e acrescentamos a utilidade do item i, somando u[i]. O pseudocódigo desse algoritmo é mostrado abaixo: 1 2 3 4 5 c: capicidade total da mochila n: numero de itens da mochila d: valor minimo de peso carregado P [1... n ]: vetor dos pesos dos itens U [1... n ]: vetor dos valores de utilidade dos itens
  7. 7. 6 6 7 saida : um vetor X [1.. n] que representa a solucao encontrada , 8 o valor da solucao encotrada e o somatorio das valores 9 de utilidade que estao na mochila 10 11 PGDinamica (c ,n ,d ,P [] , U []) 12 melhorSolucao = 0 13 14 para y = 0 ate c 15 t [0][ y ] = 0 16 para i = 1 ate n 17 a = t[i -1][ y] 18 se p[i ] > Y 19 b = 0 20 senao 21 b = t [i -1][ y -P [i ]] + U [i] 22 t[i ][ y ] = max (a ,b ) 23 24 // essa parte monta X 25 26 para i = n ate 1 27 se t [i ][ y] = t [i -1][ y ] 28 X[ i] = 0 29 senao 30 X[ i] = 1 31 y = y - P[i ] 32 melhorSolucao = melhorSolucao + U [i] 33 retorne X Para montar o vetor X, é feito o 1o se, que indica que solução da posição [i, j] é igual a de [i − 1, y], então é por que não houve inclusão de item entre essas duas soluções nesse caso X[i] = 0, indicando que esse item não foi colocado, caso contrário ele é colocado setando 1, e então diminuindo Y de P[i], indicando que i está na mochila, logo seu peso deve ser descontado de Y. Assim prossegue até ter percorrido todos os itens. Enxergamos que as dimensões da tabela é nXc e ela que indica a complexidade desse algoritmo, ou seja ele possui O(nc), isso indica que temos uma grande melhoria em relação ao de força bruta, mas ainda assim existem alguns problemas, podemos ter problemas com poucos itens, porém com algoritmo com custo elevado, isso vai depender da proporção de c escolhida, além disso podemos temos que checar o uso da memória que pode ser muito extenso.
  8. 8. 7 2.3 Algoritmo Guloso Diferente dos anteriores o algoritmo guloso não garante a solução ótima, ele consegue uma aproximada, em alguns casos ele pode até conseguir retornar a solução ótima. O algoritmo busca o colocar os melhores itens até que não haja mais espaço na mochila, para saber quais são os melhores itens, o algoritmo guloso pode se basear, no valor de utilidade do item, no peso do p item, ou no custo relativo que é u jj . Para colocarmos os itens na mochila, precisamos fazer uma ordenação, se nos basearmos pelo valor de utilidade, então nesse caso ordenamos o vetor de utilidades, em ordem decrescente e assim vamos percorrendo esse vetor e "colocando"os itens na mochila enquanto houver capacidade e não acabar os itens. Podemos ver a implementação no código abaixo (o modo de fazer o algoritmo se baseando no peso e no custo relativo é de maneira semelhante) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 entrada : c: capicidade total da mochila n: numero de itens da mochila d: valor minimo de peso carregado P [1... n ]: vetor dos pesos dos itens U [1... n ]: vetor dos valores de utilidade dos itens saida : um vetor X [1.. n] que representa a solucao encontrada , o valor da solucao encotrada e o somatorio das valores de utilidade que estao na mochila gulosoPorUtilidade (c ,n ,d ,P [] , U []) ordeneBaseadoNoValorUtilidade (P [] , U [] , n ) X = {0 ,0 ,...0} pesoLivreCorrente = c melhorSolucao = 0 para i = 1 ate n se P [i] <= pesoLivreCorrente pesoLivreCorrente = pesoLivreCorrente - P[ i] X[ i] = 1 melhorSolucao = melhorSolucao + U [i] se melhorSolucao >= d retorne X senao retorne naoEncontrouSolucao Pode ser visto que pode ser ele não consiga encontrar nenhuma solucação devido a
  9. 9. 8 restrição que a soma dos valores de utilidade deve ser pelo menos d, em nossos testes, tanto o guloso baseado peso como custo relativo, tentam executar o por valor de utilidade, caso eles não consigam atigir d, mas se o de valor de utilidade não conseguir solução, então realmente não é retornado nenhuma solução. A complexidade desse algoritmo fica dependente do método de ordenação usado, se usarmos por exemplo o MergeSort, teremos complexidade de O(nlogn + n) que é mesmo que O(nlogn). Em nossa implementação utilizamos o QuickSort que apesar de ter uma complexidade no pior caso de O(n2 ), ele possui no caso médio Θ(nlogn) e utilizando técnicas para evitar pior caso, ele consegue resultados semelhantes ao MergeSort ou até melhores como obtidos em (HOARE, 1962)
  10. 10. 9 2.4 Algoritmo Genético Algoritmo Genético é uma técnica inspirada pela biologia evolutiva, sendo uma classe particular de algoritmos evolutivos, que é utilizada para encontrar soluções para problemas de otimização combinatória, cujo objetivo é descobrir a melhor combinação dos recursos disponíveis para otimizar seu uso. Algoritmos Genéticos são implementados como uma simulação de computador em que as melhores soluções são encontradas em uma população que vai evoluindo a cada geração. O processo de evolução geralmente se inicia com uma população criada aleatoriamente. A cada geração, as soluções na população sofrem cruzamentos e mutações, suas adaptações então são avaliadas e alguns indivíduos são selecionados para a próxima geração. A seguir temos o processo do Algoritmo Genético: Inicializa População Avalia População Seleciona Reprodutores Cruza Selecionados Muta Resultantes Avalia Resultantes Atualiza População Deve Parar? Não Sim FIM Figura 2: Processo de AG Explicando o algoritmo implementado: O Algoritmo Genético para o Problema da Mochila Valiosa (PMV) foi implementado seguindo a estrutura como mostrado abaixo: Cromossomo O cromossomo do AG para a Mochila foi montado utilizando os próprios itens da
  11. 11. 10 Mochila, onde cada alelo representa a presença de um objeto na Mochila com 0 ou 1. Assim, para um PMV com 5 objetos temos a representação de um cromossomo: 10110 De acordo com o cromossomo acima, a Mochila estaria carregando os objetos 1, 3 e 4; Gerar População Inicial A população inicial é criada de forma aleatória. Como a representação de um indivíduo é de forma binária, é sorteado um número aleatoriamente entre 1 e 2Nob jetos . Em seguida, esse número é transformado em binário para fazer parte da população inicial. Com isso, conseguimos buscar um indivíduo em todo o espaço de possíveis soluções. No entando, o cromossomo só é considerado válido se a soma do peso dos objetos contidos na Mochila não ultrapassa c e a soma do valor desses objetos seja pelo menos d (Checagem de Validade). O valor do tamanho da população inicial é parametrizado. Fitness A função Fitness é baseada no somatório dos Valores dos objetos contidos em cada Mochila. Ela é formulada através uma regra de três simples, onde para cada indivíduo temos: f itnessi = Valori ∗100 Sumvalores i i ∈ N, onde N é a quantidade de alelos do cromossomo, o que nos dá um valor entre zero e cem. Taxa A função da taxa é utilizada para conseguirmos classificar os indivíduos em termos de proporção baseado agora no valor do fitness de cada indivíduo. Também é formulado através de uma regra de três simples, onde para cada um calculamos: ratei = f itnessi ∗100 Sum f itnessi i ∈ N, onde N é a quantidade de alelos do cromossomo, o que nos dá um valor entre zero e cem, sendo que agora, o somatório das taxas nos dá um valor fechado em cem, para podermos aplicar o próximo passo. Roleta A função da roleta foi implementada justamente utilizando o valor da taxa de cada indivíduo, como essas taxas são valores que somam um total de cem, é possível determinar um valor máximo para cada indivíduo da população que será utilizado para a seleção desses indivíduos para o cruzamento. Assim, uma população com quatro indivíduos, teríamos:
  12. 12. 11 1 0 1 1 0, 32.7 1 0 1 1 1, 41.5 1 0 0 1 0, 15.0 0 0 0 1 0, 10.8 onde os valores ao lado do cromossomo representam a taxa de cada um, com isso conseguimos que o 1a indivíuo tenha um valor máximo de 32.7, o 2o um valor de 74.2, o 3o um valor de 89.2 e o 4a 100. Crossover Antes de realizar o Crossover, é feita uma seleção de pares de indivíduos que irão participar do cruzamento mútuamente, onde os que possuem maior fitness têm uma melhor chance de participarem do cruzamento juntos. Assim, para o exemplo anterior do valor máximo, seriam gerados 4 números aleatórios em que cada um cairia dentro de um espaço de um dos indivíduos. Com isso, supondo que teríamos sorteado na seguinte ordem 2, 3, 1, 4, o cruzamento seria aplicado entre os indivíduos 2, 3 e depois 1, 4. O cruzamento irá gerar novos indivíduos com a mesma quantidade que a população inicial. Ele é baseado em um ponto de corte que é parametrizado. O cruzamento é então aplicado em cima desse ponto de corte, onde o 1o filho recebe do ponto de corte ao começo do cromossomo pai 1 junto do ponto de corte ao final do cromossomo do pai 2. Isso de modo inverso é feito para o filho 2. Dessa forma geramos os indivíduos novos para a população. Mutação A mutação é ralizada por meio da aleatoriedade, onde a quantidade de indivíduos que sofrerão mutação é dependente do tamanho do cromossomo. A taxa de mutação também é parametrizada, e é com ela que é decidido se cada alelo escolhido para mutação sofrerá ou não a mutação naquele cromossomo. A mutação quando aplicada sobre o alelo, ela apenas inverte seu valor, saindo de zero para um ou de um para zero, dependendo do valor antes da mutação. Avaliação A função de avaliação é utilizada justamente para tratar os novos indivíduos gerados. Ela vai calcular o somatório dos valores de cada objeto para cada cromossomo. Em seguida, aplica a função fitness, junto com a população inicial para poder classificar todos os indivíduos. Atualização A última etapa do processo evolutivo se baseia em atualizar a população escolhendo os melhores indivíduos que irão para a próxima geração. A função de atualização ordena os indivíduos baseado na fitness para poder capturar os melhores cromossomos. Um detalhe nessa etapa é que a possibilidade de repetição de indivíduo pode gerar uma convergência precoce para
  13. 13. 12 a solução do problema, onde a população seria preenchida com o mesmo indivíduo repetido. Para evitar esse problema, nessa etapa de atualização, eliminamos todos os indivíduos repetidos dentre os indivíduos pai e indivíduos filho. Ao terminar a fase de atualização, a nova população está pronta para ir para a próxima geração. A quantidade de geração realizadas também é parametrizada. Um detalhe de implementação sobre o algoritmo genético é que em um caso, para efeito de testes, chamamos os algoritmos Gulosos por custo e por volume para gerar a população inicial, os demais indivíduos foram gerados de forma aleatória como explicado anteriormente. No próximo tópico poderemos ver o resultado da aplicação dos algoritmos Gulosos na geração da população inicial. Como o cálculo para o tempo de execução é obtido depois que roda todas as iterações, não é tão notável a diferença de tempo para encontrar a melhor solução, mas abservando as execuções do algoritmo, notamos uma rápida convergência para a melhor solução possível encontrada.
  14. 14. 13 3 RESULTADOS DOS TESTES Para os testes foram utilizadas 11 (0..10) instâncias, onde a primeira tem a seguinte entrada: n=30 c=200 d=1000 P= { 12, 11, 1, 22, 5, 12, 12, 13, 14, 90, 10, 10, 99, 17, 1, 2, 3, 5, 20, 7, 3, 12, 23, 43, 45, 2,7,5,1,1 } U= { 121,11, 22, 499, 112, 122, 133, 144, 190, 110, 120, 199, 171, 10, 2, 322, 153, 320, 177, 333, 212, 203, 43, 45, 27, 75, 251, 101, 100, 100 } Paras as 10 restantes é usada variação da seguinte entrada: n=100 c=5000 d=8000 P= { Números randômicos inteiros entre 10 e 100 } U= { Números randômicos inteiros entre 10 e 1000 } 3.1 Tempo de execução Para realização dos testes cada instância foi rodada 30 vezes e depois tirada a média do tempo gasto na execução, Gráfico do Tempo de Execução 0.09 Dinamica GulosoUtilidade GulosoPeso GulosoRelativo Genetico GeneticoGuloso 0.08 Tempo de Execução(s) 0.07 0.06 0.05 0.04 0.03 0.02 0.01 0 0 2 4 6 Número da Instância Figura 3: Gráfico de teste 8 10
  15. 15. 14 calculado a partir do comando time do linux e capturando o real time. Os testes foram executados em uma máquina com processador quadcore, com 2GB de ram com sistema operacional Ubuntu 12.10. Não calculamos para o força bruta, devido ele gastar muito tempo executando para instância com n = 100, para o algoritmo genético foi utilizado as seguintes configurações: Para instâcia 0 população = 10 ponto de corte = 10 número de iterações = 1000 taxa de mutação = 10% Para instâcias de {1..10} população = 10 ponto de corte = 40 número de iterações = 1000 taxa de mutação = 10% Na Figura 3 vemos que em termo de tempo gasto o algoritmo que mais custoso é o genético, isto é devido ele ter sido configurado para ter 1000 iterações e também por suas checagens e tratamentos, diferente dos gulosos que o tempo de execução depende apenas de um método de ordenação mais iterações para colocar os itens na mochila. Para a de programação dinâmica podemos ver que para instância 0, onde n = 30, ele consegue um tempo similar aos gulosos, sendo ele exato, isso se deve a capacidade pequena da mochila dessa instância c = 200, ficamos apenas com 30*200 passos para a de programação dinâmica. Mas vemos que nas demais instâncias os gulosos conseguem manter um tempo praticamente fixo, mas a de programação dinâmica sobe um pouco devido c = 5000 ter sofrido um aumento grande. 3.2 Convergência das soluções Tirando o algoritmo de programação dinâmica, temos heurísticas para o problema, então pode ser que não retornem a solução ótima, na tabela seguintes pegamos 3 instâncias e mostramos a melhor solução achada das 30 execuções de cada algoritmo:
  16. 16. 15 Algoritmo Solução Instância Dinâmica 49810 1 Genético 48994 1 GenéticoGuloso 49810 1 GulosoUtilidade 49810 1 GulosoPeso 48137 1 GulosoRelativo 46404 1 Dinâmica 49411 2 Genético 48760 2 GenéticoGuloso 49411 2 GulosoUtilidade 49361 2 GulosoPeso 47081 2 GulosoRelativo 47416 2 Dinâmica 51979 3 Genético 51572 3 GenéticoGuloso 51979 3 GulosoUtilidade 51979 3 GulosoPeso 49767 3 GulosoRelativo 50704 3 Na instância 1 vemos que o genético sozinho não conseguiu atingir a solução ótima, apesar de chegar perto, mas quando juntamos os dois, o genético guloso nas 3 instâncias ele consegue atingir a solução ótima(solução da de programação dinâmica). O melhor guloso foi por utilidade apenas na instância 2 que ele não conseguiu atingir o ótimo, os demais guloso não conseguiram atingir ótimo em nenhuma instância.
  17. 17. 16 BIBLIOGRAFIA HOARE, C.A.R. Quicksort. The Computer Journal, Br Computer Soc, v. 5, n. 1, p. 10–16, 1962. MARTELLO, S.; PISINGER, D.; TOTH, P. New trends in exact algorithms for the 0–1 knapsack problem. European Journal of Operational Research, Elsevier, v. 123, n. 2, p. 325–332, 2000. VIANA, G. V. R.; MOREIRA, F. V. C. Técnicas de divisão e conquista e de programação dinâmica para a resolução de problemas de otimização. Revista Científica da Faculdade Lourenço Filho, v. 8, n. 1, p. 5–27, 2011.

×