Implementação de problemas de mochila em Java

Implementação de problemas de mochila em Java

1. Introdução

O problema da mochila é um problema de otimização combinatória que tem muitas aplicações. Neste tutorial, vamos resolver esse problema em Java.

2. O Problema da Mochila

No problema da mochila, temos um conjunto de itens. Cada item tem um valor de peso e valor:

image

Queremos colocar esses itens em uma mochila. No entanto, ele tem um limite de peso:

image

Therefore, we need to choose the items whose total weight does not exceed the weight limit, and their total value is as high as possible. Por exemplo, a melhor solução para o exemplo acima é escolher o item de 5kg e o item de 6kg, o que dá um valor máximo de $ 40 dentro do limite de peso.

O problema da mochila tem várias variações. Neste tutorial, focaremos no problema da mochila 0-1. In the 0-1 knapsack problem, each item must either be chosen or left behind. Não podemos obter uma quantidade parcial de um item. Além disso, não podemos levar um item várias vezes.

3. Definição matemática

Vamos agora formalizar o problema da mochila 0-1 em notação matemática. Dado um conjunto de itensn e o limite de pesoW, podemos definir o problema de otimização como:

image

This problem is NP-hard. Portanto, não há algoritmo de tempo polinomial para resolvê-lo atualmente. No entanto, existe umpseudo-polynomial time algorithm usando programação dinâmica para este problema.

4. Solução Recursiva

Podemos usar uma fórmula de recursão para resolver esse problema:

image

Nesta fórmula,M(n,w) é a solução ótima paran itens com um limite de pesow. É o máximo dos dois valores a seguir:

  • A solução ótima de(n-1) itens com o limite de pesow (excluindo on-ésimo item)

  • Valor don-ésimo item mais a solução ótima dos itens(n-1) ew menos o peso don-ésimo item (incluindo on-ésimo item)

Se o peso don-ésimo item for maior do que o limite de peso atual, não o incluímos. Portanto, está na primeira categoria dos dois casos acima.

Podemos implementar esta fórmula de recursão em Java:

int knapsackRec(int[] w, int[] v, int n, int W) {
    if (n <= 0) {
        return 0;
    } else if (w[n - 1] > W) {
        return knapsackRec(w, v, n - 1, W);
    } else {
        return Math.max(knapsackRec(w, v, n - 1, W), v[n - 1]
          + knapsackRec(w, v, n - 1, W - w[n - 1]));
    }
}

Em cada etapa da recursão, precisamos avaliar duas soluções abaixo do ideal. Portanto, o tempo de execução desta solução recursiva éO(2n).

5. Solução de programação dinâmica

A programação dinâmica é uma estratégia para linearizar problemas de programação exponencialmente difíceis. A idéia é armazenar os resultados dos subproblemas para que não tenhamos que recalculá-los posteriormente.

Também podemos resolver o problema da mochila 0-1 com programação dinâmica. Para usar a programação dinâmica, primeiro criamos uma tabela bidimensional com dimensões de 0 ane 0 aW. Em seguida, usamos uma abordagem de baixo para cima para calcular a solução ideal com esta tabela:

int knapsackDP(int[] w, int[] v, int n, int W) {
    if (n <= 0 || W <= 0) {
        return 0;
    }

    int[][] m = new int[n + 1][W + 1];
    for (int j = 0; j <= W; j++) {
        m[0][j] = 0;
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= W; j++) {
            if (w[i - 1] > j) {
                m[i][j] = m[i - 1][j];
            } else {
                m[i][j] = Math.max(
                  m[i - 1][j],
                  m[i - 1][j - w[i - 1]] + v[i - 1]);
            }
        }
    }
    return m[n][W];
}

Nesta solução, temos um loop aninhado sobre o número do itemne o limite de pesoW. Portanto, seu tempo de execução éO(nW).

6. Conclusão

Neste tutorial, mostramos uma definição matemática do problema da mochila 0-1. Em seguida, fornecemos uma solução recursiva para esse problema com a implementação do Java. Por fim, usamos a programação dinâmica para resolver esse problema.

Como sempre, o código-fonte do artigo está disponívelover on GitHub.