[R] The 'subset matching' challenge

Ben Bolker bolker at ufl.edu
Thu Oct 29 16:02:05 CET 2009




yvonnick noel wrote:
> 
> Dear all,
> 
> The following problem just has been submitted to me by an accountant.
> 
> In his new job, he has to close some old accounts. He has yearly 
> amounts, and a list of products that have been bought over the years, at 
> certain prices for which he has an exhaustive record. The problem is: He 
> does not know what product was bought this or that year (don't ask). He 
> does not want to find back the real story, but just write realistic 
> accounts, for which the sum of a subset of product prices will give the 
> exact yearly amount.
> 
> Here is a real example from his data:
> 
> # A list of 64 product prices
> products = 
> c(30500,30500,30500,30500,42000,42000,42000,42000,42000,42000,42000,42000,42000,42000,71040,90900,76950,35100,71190,
> 53730,456000,70740,70740,533600,83800,59500,27465,28000,28000,28000,28000,28000,26140,49600,77000,123289,27000,27000,27000,
> 27000,27000,27000,80000,33000,33000,55000,77382,48048,51186,40000,35000,21716,63051,15025,15025,15025,15025,800000,1110000,
> 59700,25908,829350,1198000,1031655)
> 
> # Global amount
> amount = 4748652
> 
> Now he wants to find all subsets of the 'product' vector which sums to 
> 'amount'.
> 
> I wrote the following code, which is clearly not optimal:
> 
> # Create a matrix of subsets of size r among the integer set 1:n
> subsets <- function(n, r, v = 1:n) {
>   if(r <= 0) vector(mode(v), 0)
>   else if(r >= n) v[1:n]
>   else rbind(cbind(v[1], Recall(n-1, r-1, v[-1])),Recall(n-1, r, v[-1]))
> }
> 
> # Main function
> find.amount = function(amount,products) {
>  
>   if(sum(products)<amount) {
>     cat("There is no solution.\n")
>     return()
>   }
>  
>   l = length(products)
>   cat("\nThere are",l,"product prices\n\n")
>   names(products) = paste("Product",1:l,sep="")
>   products = sort(products)
> 
>   for(i in 2:l) {
> 
>     # If the sum of the i smallest prices is greater than amount, then
> stop
>     if(sum(products[1:i])>amount) break
> 
>     # Look for matching subsets only in the case when the sum of i 
> largest prices is greater than amount
>     if(sum(rev(products)[1:i])>=amount) {
>       # Generates all subsets of i indicies in 1:l
>       subs = subsets(l,i)
>       nl = nrow(subs)
>       nc = ncol(subs)
> 
>       # Compute sums of corresponding price subsets
>       sums = rowSums(matrix(products[subs],nl,nc))
> 
>       # Which ones match the global amount ?
>       w = which(sums == amount)
>       how.many = length(w)
>       if(how.many>0) {
>         cat("\n-->> There are",how.many,"solutions with",nc,"products
> :\n")
>         for(j in 1:how.many) {
>           print(products[subs[w[j],]])
>         }
>       }
>       else cat("\n-->> There is no solution with",nc,"products.\n")
>     }
>     else cat("\n-->> There is no solution with",i,"products.\n")
>   }
> }
> 
> 
> Then I can use these functions on a smaller example:
> 
>  > find.amount(4,c(1,1,1,1,2,2))
> 
> and a number of matching subsets are provided. The problem is: This 
> approach creates a whole matrix of subsets of r integers among 1:n, 
> which rapidly gives huge matrices, and this is clearly not optimal for 
> the real data provided above.
> 
> Would anyone have a suggestion as to an alternative and more efficient 
> strategy?
> 
> Good luck,
> 
> Yvonnick Noel
> University of Brittany, Rennes 2
> France
> 
> 

  I believe this is the "knapsack problem".
RSiteSearch("knapsack") might help a little bit.

http://xkcd.com/287/

-- 
View this message in context: http://www.nabble.com/The-%27subset-matching%27-challenge-tp26114520p26114758.html
Sent from the R help mailing list archive at Nabble.com.




More information about the R-help mailing list