Search code examples
rmathematical-optimizationknapsack-problem

knapsack() vector length issues


When I run

weights <- 1:50
profits <- 1:50
library(adagio)
knapsack(w = weights, p = profits, cap = 30)

I get the error

Error in F[, k] <- G : 
  number of items to replace is not a multiple of replacement length
In addition: Warning message:
In pmax(G, H) : an argument will be fractionally recycled

but when I run smaller sized vectors, like

weights <- 1:20
profits <- 1:20
knapsack(w = weights, p = profits, cap = 30)

it runs fine. Does knapsack() just slow down (and prevent running) for larger sets? I'm looking to use lengths in the thousands eventually.


Solution

  • This is an issue with passing elements with weight exceeding the total capacity. To see the issue, let's look at the first few lines of the knapsack function:

    function (w, p, cap) 
    {
        n <- length(w)
        x <- logical(n)
        F <- matrix(0, nrow = cap + 1, ncol = n)
        G <- matrix(0, nrow = cap + 1, ncol = 1)
        for (k in 1:n) {
            F[, k] <- G
            H <- c(numeric(w[k]), G[1:(cap + 1 - w[k]), 1] + p[k])
            G <- pmax(G, H)
        }
    

    When iteratively filling the F matrix one column at a time, the algorithm creates a vector H with the following command (and then immediately computing pmax(G, H)):

    H <- c(numeric(w[k]), G[1:(cap + 1 - w[k]), 1] + p[k])
    

    numeric(w[k]) has length w[k], and when w[k] <= cap, G[1:(cap + 1 - w[k]), 1] + p[k] has length cap + 1 - w[k], meaning the entire vector H has length cap+1, matching the size of G. On the other hand, when w[k] == cap + 1 we will end up with an H vector of size cap+2, which doesn't match the size of G and gives us trouble, and with w[k] > cap + 1 we will get an error for mixing positive and negative indices.

    Getting back to your example function call, you have weights up to 50 but only a capacity of 30, yielding an error:

    weights <- 1:50
    profits <- 1:50
    knapsack(w = weights, p = profits, cap = 30)
    # Error in F[, k] <- G : 
    #   number of items to replace is not a multiple of replacement length
    # In addition: Warning message:
    # In pmax(G, H) : an argument will be fractionally recycled
    

    However when you limit to elements with weight not exceeding the capacity, you get no errors:

    knapsack(w = weights[weights <= 30], p = profits[weights <= 30], cap = 30)
    # $capacity
    # [1] 30
    # 
    # $profit
    # [1] 30
    # 
    # $indices
    # [1] 1 2 3 4 5 7 8
    

    It would be most ideal if the knapsack function gracefully removed any object with weight exceeding the capacity (since no such elements could ever be used in a feasible solution) and gave you a solution for the code you posted, but as a workaround you could simply remove them yourself from the input to the knapsack function.