i'm looking for a method to select the best couple of numeric values from a list to obtain a target mean.
For example if i have a serie like that: c(12,6,8,75,6,2,69,42,13)
How to find the best 2 values to obtain a mean closest to 20.
Here is some code that I think does what you want. There's probably a faster way to do it, but for a moderate length list this should be fine. (The time is order n^2/2, where n is the length of the list.)
x <- c(12,6,8,75,6,2,69,42,13)
target <- 20
n <- length(x)
oldMiss <- Inf
besti <- NA
bestj <- NA
for (i in 1:(n-1))
for(j in (i+1):n){
newMiss <- abs(mean(c(x[i], x[j])) - target)
if (newMiss < oldMiss){
besti <- i
bestj <- j
oldMiss <- newMiss
}
}
cat("Best pair is",x[besti],x[bestj],"\n")
Here is another solution, similar to that of @startz . It has the same complexity, but swaps explicit loops for the outer function, so it might be a wee bit faster.
x <- c(12,6,8,75,6,2,69,42,13)
target <- 20
a <- outer(x, x, FUN = function(i, j) {abs(i + j - 2*target)})
b <- which(a == min(a), arr.ind = TRUE)
besti <- b[1, 1]
bestj <- b[1, 2]
cat("Best pair is",x[besti],x[bestj],"\n")
i and j are going to be the values of x[i] and x[j]. The mean will be (x[i]+x[j])/2.
So abs((i + j)/2 - target) will be the distance between the mean and the target. Then @prubin has multiplied through by 2 inside the absolute value. That doubles all the distances, but doesn't change which is the smallest distance.
That's a good catch about "edge" conditions. @prubin's code includes all possible pairs of x. In this case, that includes 2.3 as both the i and j value--which isn't what you want. Note that my code made sure i and j were different.
My bad for missing that. The fix is to insert the following line after the assignment to a:
diag(a) <- Inf
That will prevent the subsequent code from using the same observation twice.
The tweak suggested by @Ajackson, if I'm reading it correctly, won't work due to the absolute value function.
The expression abs(i + j - 2*target) creates a score that is the absolute difference between the sum of a pair of entries and double the target. That's exactly twice the difference between the mean of the two entries and the target, so the winner is the same. If you prefer, you can use abs((i + j)/2 - target).
The outer() function takes two arguments (usually vectors, maybe arrays) and applies the function specified in the third argument to every pair of entries from the first two arguments.