Mochila booleana

Suponha dado um conjunto de objetos, cada um com um certo peso e um certo valor. Quais dos objetos devo colocar na minha mochila para que o valor total seja o maior possível? Minha mochila tem capacidade para 15 kg apenas.

Esse é um exemplo do problema da mochila (= knapsack problem), também conhecido como problema da mochila booleana, problema booleana da mochila, e problema da mochila zero-um. Trata-se de uma generalização do problema subset sum (mas não deve ser confundido com o problema da mochila fracionária).

Esta página usa a estrutura recursiva do problema para desenvolver dois algoritmo de força bruta: um algoritmo exponencial de enumeração e um algoritmo de programação dinâmica. A página foi inspirada na seção 16.2 do CLRS.

O problema

Se p[1 .. n] e v[1 .. n] são vetores numéricos e X é um subconjunto do intervalo 1 .. n, denotaremos por p(X) e v(X) as somas  iX p[i]  e  iX v[i]  respectivamente.

Problema da Mochila Booleana:  Dados vetores p[1 .. n] e v[1 .. n] de números naturais e um número natural c, encontrar um subconjunto X do intervalo 1 .. n que maximize v(X) sob a restrição p(X) ≤ c.

Em outras palavras, queremos encontrar um subconjunto X do intervalo 1 .. n tal que p(X) ≤ c e v(X) ≥ v(Y) para todo subconjunto Y de 1 .. n tal que p(Y) ≤ c.

Diremos 1, 2, … , n são os objetos do problema, que p[i] é o peso do objeto i, e que v[i] é o valor do objeto i. Diremos que c é a capacidade da instância. Diremos ainda que uma mochila (= knapsack) é qualquer subconjunto X de 1 .. n que satisfaz a restrição p(X) ≤ c. O valor de uma mochila X é o número v(X) . Nosso problema é encontrar uma mochila de valor máximo.

Exemplo A:  Considere a instância n = 4, p = (20, 20, 30, 30) e v = (20, 30, 20, 40) do problema da mochila. Represente cada objeto i por um retângulo de altura p[i] e largura v[i].

1

2

3

4

Qualquer conjunto de objetos pode ser disposto em escada, com o canto inferior direito de um objeto tocando o canto superior esquerdo do objeto seguinte. Um conjunto de objetos assim dispostos é uma mochila se couber numa faixa de altura igual à capacidade. O valor da mochila é o comprimento da parte ocupada da faixa. A figura abaixo representa o conjunto de objetos { 2, 4 } na faixa pontilhada de altura c = 60. Esse conjunto é, portanto, uma mochila. O valor da mochila é 70.

  2    
    4  

Exemplo B:  Considere a instância do problema da mochila que tem c = 50, n = 5, e pv dados na tabela abaixo. O conjunto { 2, 3 } é uma mochila de valor máximo. O valor desta mochila é 1000. Os objetos 2 e 3 esgotam a capacidade da instância, mas isso é um acidente, não uma exigência do problema.

p 40 30 20 10 20
v 840 600 400 100 300

Exercícios 1

  1. Seja X uma mochila para uma instância ( p, v, n, c) do problema da mochila booleana. Suponha que p(X) = c. É verdade que X é uma solução do problema? Em outras palavras, é verdade que X tem valor máximo?
  2. Seja ( p, v, n, c) uma instância do problema da mochila booleana. Qual a natureza de uma solução da instância? A solução é um número ou um conjunto?
  3. ★ Critique a seguinte formulação alternativa do problema da mochila booleana: Dados vetores p[1 .. n] e v[1 .. n] de números naturais e um número natural c, encontrar um subconjunto X do intervalo 1 .. n tal que v(X) é máximo e p(X) ≤ c.  Esta formulação está correta? É ambígua? [Solução]
  4. Mostre que o problema da mochila booleana pode ser formulado assim: encontrar um vetor x[1 .. n] com componentes em {0, 1} que maximize a soma x[1] v[1] + ⋯+ x[n] v[n] sob a restrição x[1] p[1] + ⋯+ x[n] p[n] ≤ c.
  5. Problema do pendrive. Mostre que o problema do pendrive (veja a página Método guloso) é um caso particular — ou seja, uma coleção de instâncias — do problema da mochila booleana. Como um algoritmo para o problema da mochila booleana pode ser usado para resolver o problema do pendrive?
  6. Subset sum. Mostre que o problema subset sum (veja a página Subset sum) é um caso particular — ou seja, uma coleção de instâncias — do problema da mochila booleana. Como um algoritmo para o problema da mochila booleana pode ser usado para resolver o problema subset sum?
  7. Heurística gulosa.  Considere a seguinte heurística gulosa para o problema da mochila booleana:  para i = 1, 2, … , n, coloque o objeto i na mochila se ainda houver espaço suficiente; caso contrário, ignore o objeto. Descreva esta heurística em código. Mostre que a heurística não resolve o problema nem mesmo se os objetos estiverem em ordem decrescente de valor. Mostre que a heurística não resolve o problema nem mesmo quando os objetos estão em ordem crescente de valor específico (valor por unidade de peso). Mostre que a heurística gulosa pode cometer um erro arbitrariamente grande.
  8. Redução ao subset sum.  Suponha dado um algoritmo para o problema subset sum. Eu gostaria de usar o algoritmo (sem olhar seu código) para resolver as instâncias do problema da mochila booleana em que v[i] = p[i] para cada i. Como é possível fazer isso?  Quantas vezes será preciso rodar o algoritmo? 

A estrutura recursiva do problema

Tal como o problema subset sum, o problema da mochila booleana tem estrutura recursiva: qualquer solução de uma instância é composta por soluções de suas subinstâncias. Suponha que X é uma solução da instância ( p, v, n, c) do problema. Se n ∉ X então X é solução da subinstância

( p, v, n−1, c)

e se n ∈ X então X − { n } é solução da subinstância

( p, v, n−1, c − p[n]) .

Reciprocamente, se Y é solução da instância ( p, v, n−1, c) e Y′ é solução da instância ( p, v, n−1, c − p[n]) então Y ou Y′ ∪ { n } é solução da instância ( p, v, n, c).

Isso sugere um algoritmo recursivo para o problema (veja exercício abaixo). O algoritmo usa força bruta pois faz uma enumeração (implícita) de todos os subconjuntos de 1 .. n. O algoritmo consome Ω(2n) unidades de tempo e portanto só é útil para valores muito pequenos de n.

Exercícios 2

  1. Estrutura recursiva. Suponha que X é uma solução de uma instância ( p, v, n, c) do problema da mochila booleana. Prove que se n ∉ X então X é solução da subinstância ( p, v, n−1, c). Prove que se n ∈ X então X − { n } é solução da subinstância ( p, v, n−1, c − p[n]) .
  2. ★ Escreva um algoritmo Mochila-R que resolva o problema da mochila booleana apoiando-se na estrutura recursiva do problema. Para simplificar, basta que seu algoritmo devolva o valor de uma solução (não é necessário devolver a solução propriamente dita). (Dica: adapte o código do algoritmo Subset-Sum-R.)  Quanto tempo seu algoritmo consome? Quanto espaço consome?
  3. Escreva um algoritmo iterativo que resolva o problema da mochila booleana por enumeração explícita. Em cada iteração, seu algoritmo deve gerar um novo subconjunto de 1 .. n a partir do anterior. Depois de gerar um subconjunto, o algoritmo deve calcular seu peso e valor.
  4. Considere o algoritmo 1.415^n para o problema subset sum (veja a página Subset sum). Adapte o algoritmo para resolver o problema da mochila booleana. Para simplificar, basta que o algoritmo devolva o valor de uma solução. Quanto tempo seu algoritmo consome? Quanto espaço consome?

Algoritmo de programação dinâmica

A exemplo do que fizemos com o problema subset sum, podemos resolver o problema da mochila booleana por programação dinâmica.

A ideia é guardar em uma tabela as soluções das subinstâncias da instância dada. Vamos tratar de todas as subinstâncias que tenham vetor de pesos p[1 .. i] para algum i entre 0 e n e alguma capacidade j entre 0 e c. A tabela de programação dinâmica, que chamaremos t, é então definida assim: para cada i no intervalo 0 .. n e cada j no intervalo 0 .. c,

t[i, j]  é o valor de uma solução da instância ( p, v, i, j )

do problema. Em particular, t[0, j] = 0 para todo j e t[i, 0] = 0 para todo i. Graças à estrutura recursiva do problema, a tabela satisfaz a recorrência

t[i, j] = t[i−1, j] se p[i] > j
max (t[i−1, j], v[i] + t[i−1, j − p[i]]) se p[i] ≤ j

para todo i ≥ 1 e todo j ≥ 1. (Observe que todos os números da forma j − p[i] são inteiros não-negativos e portanto a posição [i−1, j − p[i]] da tabela está bem definida.)

A partir dessa recorrência é fácil escrever um algoritmo Mochila-Prog-Din (veja exercício abaixo) para preencher a tabela t e, a partir dela, calcular uma solução X. O consumo de tempo do algoritmo é proporcional ao tamanho da tabela t. Portanto o algoritmo consome

Θ(nc)

unidades de tempo. Como já observamos ao estudar o desempenho da programação dinâmica para o problema subset sum, é melhor dizer que o algoritmo consome Θ( n 2lg c) unidades de tempo para deixar claro que o algoritmo é exponencial.

Exemplo C:  Nesse exemplo, o problema da mochila tem n = 4 objetos e capacidade c = 5. Os pesos p e os valores v estão abaixo à esquerda. À direita, temos a tabela t da programação dinâmica. Os números da tabela têm um erro que você deve encontrar.

  1 2 3 4
p 4 2 1 3
v 500 400 300 450
 
  0 1 2 3 4 5
0 303 0 0 0 0 0
1 0 0 0 0 500 500
2 0 0 400 400 500 500
3 0 300 400 400 700 800
4 0 300 400 450 750 850

Exercícios 3

  1. Considere a instância do problema da mochila booleana que tem n = 5 objetos, capacidade c = 50, pesos p = (40, 30, 20, 10, 20) e valores v = (840, 600, 400, 100, 300). Resolva essa instância do problema usando programação dinâmica. (Sugestão: simplifique os números antes de começar.)
  2. ★ Suponha que t[0 .. n, 0 .. c] é a tabela de programação dinâmica correspondente a uma instância ( p, v, n, c) do problema da mochila booleana. Qual o valor de t[i, 0] para i entre 1 e n?
  3. ★ Escreva um algoritmo de programação dinâmica Mochila-Prog-Din para o problema da mochila booleana. O seu algoritmo deve preencher a tabela de programação dinâmica (basta adaptar o código do correspondente algoritmo para o problema subset sum) e em seguida usar a tabela para calcular uma solução do problema. [Solução]
  4. ★ O algoritmo Mochila-Prog-Din do exercício anterior é polinomial?
  5. Desafio.  Invente um algoritmo que resolva o problema da mochila booleana em tempo Ο(n9). (Você pode trocar 9 por seu expoente favorito.)
  6. Desafio.  Invente um algoritmo que resolva o problema da mochila booleana em tempo Ο((n lg c)9). (Você pode trocar 9 por seu expoente favorito.)