[英]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.