Mochila booleana aproximada

O algoritmo de programação dinâmica para o problema da mochila booleana (veja a página Mochila booleana) consome muito tempo. Esta página trata de um algoritmo muito mais rápido que pode não encontrar uma mochila de valor máximo, mas produz uma mochila cujo valor é pelo menos 50% do máximo.

Esta página serve de introdução aos algoritmos de aproximação mencionados na página 1043 do livro CLRS.

O problema

Dados números naturais  $p_1, \ldots, p_n$  e  $v_1, \ldots, v_n\text{,}$  diremos que $p_i$ é o peso e $v_i$ é o valor do objeto $i\text{.}$ Para qualquer subconjunto $X$ de $\conj{1, \ldots, n}\text{,}$ denotaremos por $p(X)$ a soma $\sum_{i\in X} p_i\,\text{.}$ Dado um número natural $c\text{,}$ diremos que $X$ é uma mochila (= knapsack) se $p(X) \leq c\text{.}$  (Em vez de dizer $X$ é uma mochila, poderíamos dizer $X$ é viável.)  O valor de $X$ é o número $v(X) = \sum_{i\in X} v_i\,\text{.}$

Queremos encontrar uma mochila de valor máximo. É claro que todo objeto de peso maior que $c$ pode ser ignorado. É claro também que todo objeto de peso nulo pode ser incluído em todas as mochilas. Suporemos então que $1 \leq p_i \leq c$ para todo $i\text{.}$ Esta hipótese não fazia parte de nossa formulação anterior do problema da mochila booleana, mas é importante na presente página.

Problema da mochila booleana: Dados números naturais $p_1, \ldots, p_n\text{,}$ $v_1, \ldots, v_n$ e $c$ tais que $1 \leq p_i \leq c$ para todo $i\text{,}$ encontrar uma mochila de valor máximo, ou seja, um subconjunto $X$ de $\conj{1, \ldots, n}$ que maximize $v(X)$ sob a restrição $p(X) \leq c\text{.}$

Discutiremos a seguir um algoritmo de aproximação para o problema. O algoritmo produz uma mochila que pode não ter valor máximo, mas tem valor pelo menos metade do máximo.

Algoritmo de aproximação

Nosso algoritmo tem caráter guloso e dá preferência aos objetos de maior valor específico. O valor específico de um objeto $i$ é o número $v_i/p_i\,\text{.}$ Para simplificar a descrição do algoritmo, suporemos que os objetos são dados em ordem decrescente de valor específico: \begin{equation}\label{value-per-weight} \frac{v_1}{p_1} \ \geq \ \frac{v_2}{p_2} \ \geq \ \dots \ \geq \ \frac{v_n}{p_n}\,. \end{equation}

O algoritmo recebe uma instância do problema que satisfaz \eqref{value-per-weight} e devolve uma mochila $X$ tal que \[\textstyle v(X) \ \geq \ \frac{1}{2}\,v(M) \] para qualquer mochila $M\text{.}$ A desigualdade vale, em particular, se $M$ for uma mochila de valor máximo.

Mochila-Quase-Ótima$\,(p, \ v, \ n, \ c)$
1 . $s := 0$
2 . $k := 1$
3 . enquanto $k \leq n$ e $s + p_k \leq c$
4 .ooo $s := s + p_k$
5 .ooo $k := k+1$
6 . $X := \conj{1,2, \ldots, k-1}$
7 . se $k > n$ ou $v(X) \geq v_k$
8 .ooo devolva $X$
9 . senão devolva $\conj{k}$

O bloco de linhas 3 a 5 não faz mais que encontrar o maior $k$ tal que $p_1 + \cdots + p_{k-1} \leq c\text{.}$

O algoritmo está correto.  No início da linha 7 de Mochila-Quase-Ótima, é claro que $X$ é uma mochila, ou seja, que $p(X) \leq c\text{.}$ Se $k > n$ então $X$ é o conjunto de todos os $n$ objetos e portanto $X$ é uma (a única) mochila de valor máximo. Assim, a desigualdade $v(X) \geq \frac{1}{2} v(M)$ vale trivialmente.

Suponha agora que $k \leq n$ no início da linha 7. De acordo com o enunciado do problema, $p_i \leq c$ para todo $i\text{.}$ Portanto, o conjunto $\conj{k}$ é uma mochila. O algoritmo devolve a mais valiosa das duas mochilas, $X$ e $\conj{k}\text{.}$ Resta provar que \[\textstyle \max\,(v(X), v_k) \ \geq \ \frac{1}{2}\,v(M) \] para qualquer mochila $M\text{.}$ O primeiro passo da prova consiste na seguinte observação: \[\textstyle \max\,(v(X), v_k) \ \geq \ \frac{1}{2}\,(v(X) + v_k)\,. \] Podemos resumir isso dizendo que $\max\,(v(X), v_k) \geq \frac{1}{2}\,v(Y)\text{,}$ onde \[ Y := X \cup \conj{k}\text{.} \] O segundo passo da prova mostra que $v(Y) > v(M)$ qualquer que seja a mochila $M\text{:}$ \begin{align*} v(Y) - v(M) & \ = \ v(Y{-}M) \ - \ v(M{-}Y) \\ & \ = \ \textstyle \sum_{i\in Y-M} v_i \ - \ \sum_{i\in M-Y} v_i \\ & \ = \ \textstyle \sum_{i\in Y-M} \frac{v_i}{p_i}p_i \ - \ \sum_{i\in M-Y} \frac{v_i}{p_i}p_i \\ & \ \geq \ \textstyle \frac{v_k}{p_k} \sum_{i\in Y-M} p_i \ - \ \frac{v_k}{p_k} \sum_{i\in M-Y} p_i \\ %% & \ = \ \textstyle \frac{v_k}{p_k}\,p(Y{-}M) \ - \ \frac{v_k}{p_k}\,p(M{-}Y) \\ & \ = \ \textstyle \frac{v_k}{p_k}\big(p(Y{-}M) \ - \ p(M{-}Y)\big) \\ & \ = \ \textstyle \frac{v_k}{p_k}\big(p(Y) \ - \ p(M)\big) \\ & \ > \ \textstyle \frac{v_k}{p_k}\left(c \ - \ c\right) \\ & \ = \ 0\,. \end{align*}

A passagem da terceira para a quarta linha vale porque $\frac{v_i}{p_i} \geq \frac{v_k}{p_k}$ para todo $i$ em $Y$ e $\frac{v_i}{p_i} \leq \frac{v_k}{p_k}$ para todo $i$ no complemento de $Y\text{.}$ Já a passagem da sexta para a sétima linha vale porque $p(Y) > c$ e $p(M) \leq c\text{.}$

Se juntarmos os dois passos da prova, veremos que $\max\,(v(X), v_k) \geq \mbox{}$ $\frac{1}{2}(v(X) + v_k) = \mbox{}$ $\frac{1}{2} Y > \frac{1}{2} M\text{,}$ como desejado.

Desempenho.  Adote $n$ como medida do tamanho da instância $(p, v, n, c)$ do problema. (Poderia igualmente bem ter adotado $2n+2$ como medida do tamanho.)  Uma execução de qualquer das linhas do algoritmo consome uma quantidade de tempo que não depende de $n\text{.}$ O bloco de linhas 3-5 é executado no máximo $n$ vezes. Assim, o algoritmo todo consome

$\Theta(n)$

unidades de tempo, tanto no pior quanto no melhor caso. O pré-processamento necessário para fazer valer \eqref{value-per-weight} pode ser feito em tempo $\Theta(n \lg n)\text{.}$ Portanto, o consumo de tempo do processo todo é \[ \Theta(n \lg n)\,. \]

Exercícios 1

  1. Construa uma instância do problema da mochila para a qual o algoritmo Mochila-Quase-Ótima devolve $\conj{k}$ na linha 9.

Apêndice: algoritmos de aproximação

Imagine um problema qualquer de maximização, como é o problema da mochila. Um algoritmo rápido cujo resultado é uma fração pré-determinada da solução ótima é conhecido como algoritmo de aproximação (= approximation algorithm). Por exemplo, o algoritmo Mochila-Quase-Ótima, junto com o pré-processamento, é linearítmico e produz um resultado de valor pelo menos 50% do ótimo. Esse fator de aproximação pode parecer pouco satisfatório, mas é suficiente para algumas aplicações que precisam de um algoritmo muito rápido.

Outros algoritmos de aproximação para o problema da mochila booleana têm fator de aproximação bem maior que 50%. O algoritmo de Ibarra e Kim (veja o livro Uma Introdução Sucinta a Algoritmos Aproximação, por exemplo) garante um fator de aproximação tão próximo de 100% quanto o usuário desejar!  Como seria de se esperar, o consumo de tempo do algoritmo é tanto maior quanto maior o fator de aproximação solicitado. O algoritmo de Ibarra e Kim é baseado numa variante do algoritmo Mochila-Prog-Din em que os papéis de $p$ e $v$ são trocados.

Exercícios 2

  1. Complete os detalhes da prova da correção do algoritmo Mochila-Quase-Ótima.