简体   繁体   English

从值列表中近似两个目标值

[英]Approximate two target values from a list of values

I have a list of values (these refer to weights) and 2 target values (which are the sum of a selection of these weights).我有一个值列表(这些是指权重)和 2 个目标值(它们是这些权重选择的总和)。 The weights should be assigned to one of the two target values, so that the sum of the weights approximates its target value.应将权重分配给两个目标值之一,以便权重之和接近其目标值。 The approximation should be maximised for both target values simultaneously.应该同时最大化两个目标值的近似值。

For example, this is a list of weights例如,这是一个权重列表

Weight重量
4.528 4.528
4.773 4.773
4.253 4.253
4.688 4.688
4.21 4.21
3.841 3.841
4.005 4.005
4.545 4.545
3.825 3.825
5.123 5.123
4.757 4.757

and there are two target values:并且有两个目标值:

Values价值观
22.08 22.08
21.37 21.37

The sum of a selection of weights will probably not be exactly equal to the target value, so I need an approximation.权重选择的总和可能不会完全等于目标值,所以我需要一个近似值。

Excel Solver can do this for one target value at a time, as far as I know, but I need it to handle multiple target values at a time.据我所知,Excel Solver 可以一次处理一个目标值,但我需要它一次处理多个目标值。 Does anyone have any idea how to tackle this?有谁知道如何解决这个问题? Preferably in R but Python or Excel are also fine.最好在 R 但 Python 或 Excel 也可以。

Here is an alternative:这是一个替代方案:

 n= length(Weight)
 
 # all combinations 2 to n-1
 cms = lapply(2:(n-1), function(x) combn(n,x))
 # sum weights selected and not selected and compute square errors
 res = lapply(cms, function(cm)
+ apply(cm,2,function(sel){
+   sum1 = sum(Weight[sel])
+   sum2 = sum(Weight[-sel])
+   sum((Values-c(sum1,sum2))^2)
+ }))
 
 # find the min square error 
 mins = sapply(res,function(re){
+   wm = which.min(re)
+   minv= re[wm]
+   c(wm=wm,minv=minv)})
 mins
#         [,1]    [,2]      [,3]     [,4]      [,5]      [,6]     [,7]     [,8]    [,9]
#wm    18.0000  81.000 169.00000 300.0000 163.00000 162.00000  85.0000  38.0000   2.000
#minv 447.1174 212.036  68.92069  14.0989  12.99698  54.91097 184.7123 406.2839 719.574
 which.min(mins[2,])
#[1] 5
 mins[,which.min(mins[2,])]
#       wm      minv 
#163.00000  12.99698 
 (sel = cms[[5]][,163]) # group 1
#[1] 1 3 5 6 7 9
 (1:n)[-sel] # group 2
#[1]  2  4  8 10 11
 c(sum(Weight[sel]), sum(Weight[-sel])) # sums
#[1] 24.662 23.886
 Values
#[1] 22.08 21.37
library(tidyverse)

weight<-c(4.528
,4.773
,4.253
,4.688
,4.21
,3.841
,4.005
,4.545
,3.825
,5.123
,4.757)



values <- c(
22.08,21.37)

# a heuristic that says I'm not going to add more than "these" number of entries to hit a target
(most_to_sum <- ceiling(max(values)/min(weight)))

#make combs
combs_to_do <- expand_grid(
  set_1 = seq_len(most_to_sum),
  set_2 = seq_len(most_to_sum)
) |> rowwise() |> mutate(rsum=sum(set_1,set_2)) |> filter(rsum<=length(weight)) |> ungroup()

unique_sets <- map(seq_len(most_to_sum),
                   ~combn(weight,.x,simplify=FALSE)) |> flatten()

unique_sets_evals <- map_dbl(unique_sets,
        ~abs(values[[1]] - sum(.x)))

# opportunity here to trade accuracy for speed/memoirt
(set1_reduced <- quantile(unique_sets_evals,1)) # use 1 for exhaustive search; though I got the correct result reducing to 0.01  and even good approximations with less  

set1_reduced_sets <- unique_sets[which(unique_sets_evals<=set1_reduced)]

do_second_set <- function(first_sets,size_of_second_set,weight,values){

  second_sets <- map(first_sets,~{
    available <- setdiff(weight,.x)
    if(length(available) < size_of_second_set) return(Inf)
    combn(available,size_of_second_set,simplify = FALSE)
  })
  coeval <- map2(first_sets,second_sets,
                 ~{
                   x <- .x
                   y <- .y 
                   xsum <- sum(x)
                   ysums <- map(y,sum)
                   evals <- map(ysums,
                                  ~sqrt((values[[1]]-xsum)^2+(values[[2]]-.x)^2))
                   best_y <- y[which.min(evals)]
                  list(best_second = best_y,
                       eval=evals[[which.min(evals)]])
                 })
  map2(first_sets,coeval,~c(list(set1=.x),.y))
}

almost_ <- map(seq_len(most_to_sum),
    ~do_second_set(set1_reduced_sets,.x,weight=weight,values=values)) |> flatten()

all_evals  <- map_dbl(almost_,~.x$eval)
(best_eval_num <- which.min(all_evals))

almost_[[best_eval_num]]
> almost_[[best_eval_num]]
$set1
[1] 4.528 4.773 4.253 4.688 3.825

$best_second
$best_second[[1]]
[1] 4.210 3.841 4.005 4.545 4.757


$eval
[1] 0.01769181

> sum( 4.528, 4.773, 4.253,4.688 ,3.825)
[1] 22.067
> sum(4.210, 3.841, 4.005,4.545, 4.757)
[1] 21.358

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM