从向量中找到数字的所有可能组合以达到给定的总和(无重复)

时间:2019-04-23 18:23:06

标签: r combinations

我有一个向量“数字”,我想找出1,2或3个数字的所有可能组合,其总和在90到110之间。

我知道有很多方法可以解决问题,但是没有一个可以满足我的需要。 至少不在R#中

numbers <- c(40,60,20,65,45,30,5,70,100,85,75,10);
names(numbers) <- c("A","B","C","D","E","F","G","H","I","J","K","L")

结果应如下所示:

A + B
A + D
A + H
I
B + E
B + F
C + H
C + J
C + K
D + E
D + F
F + H
F + K
J + L

5 个答案:

答案 0 :(得分:6)

我编写了一个名为RcppAlgos的软件包,用于这些任务。有一个comboGeneral函数可以在特定约束下找到所有组合。观察:

comboBetween <- function(v, m, constraint) {
    do.call(c, lapply(1:m, function(x) {
        nums <- comboGeneral(v, x, constraintFun = "sum", 
                                   comparisonFun = c(">=", "<="), 
                                   limitConstraints = constraint)
        apply(matrix(myNames[match(nums, numbers)], 
                     ncol = x), 1, paste, collapse = " + ")
    }))
}

这里是一个例子:

numbers <- c(40,60,20,65,45,30,5,70,100,85,75,10);
myNames <- c("A","B","C","D","E","F","G","H","I","J","K","L")

comboBetween(numbers, 4, c(90, 110))
 [1] "I"             "G + J"         "G + I"         "L + J"         "L + I"         "C + H"        
 [7] "C + K"         "C + J"         "F + B"         "F + D"         "F + H"         "F + K"        
[13] "A + B"         "A + D"         "A + H"         "E + B"         "E + D"         "G + L + K"    
[19] "G + L + J"     "G + C + D"     "G + C + H"     "G + C + K"     "G + C + J"     "G + F + B"    
[25] "G + F + D"     "G + F + H"     "G + F + K"     "G + A + E"     "G + A + B"     "G + A + D"    
[31] "G + E + B"     "L + C + B"     "L + C + D"     "L + C + H"     "L + C + K"     "L + F + B"    
[37] "L + F + D"     "L + F + H"     "L + A + E"     "L + A + B"     "C + F + A"     "C + F + E"    
[43] "C + F + B"     "C + A + E"     "G + L + C + B" "G + L + C + D" "G + L + C + H" "G + L + C + K"
[49] "G + L + F + E" "G + L + F + B" "G + L + F + D" "G + L + A + E" "G + C + F + A" "G + C + F + E"
[55] "G + C + A + E" "L + C + F + A" "L + C + F + E"

它非常高效,并且用C++编写,以实现最佳性能。

答案 1 :(得分:4)

在下面的代码中,我们确定值的组合以及期望范围内的和,然后获取这些组合的索引。然后,我们使用索引来获取要加在一起的元素的名称。 lapply负责迭代我们要求和的每个数量的值。

这被打包到一个函数中,其中x是数字的命名向量,n是要求和的值的最大数目,而interval是总和应该是。

请注意,如您的问题所述,下面的函数返回格式为"A + B"的字符串。如果您的目标是对字母的组合进行其他处理,则最好直接使用combn()返回的组合的矩阵(或列表)。

numbers <- c(40,60,20,65,45,30,5,70,100,85,75,10);
names(numbers) <- LETTERS[1:length(numbers)]

library(dplyr) # For between function

combos = function(x, n, interval) {

  res = lapply(2:n, function(i) {
    cc = combn(x, i)
    idx = which(between(colSums(cc), interval[1], interval[2]))
    apply(combn(names(x), i)[ , idx], 2, paste, collapse=" + ")
  })

  cbind(unlist(c(names(x)[between(x, interval[1], interval[2])], res)))
}

combos(numbers, 3, c(90, 110))
     [,1]       
 [1,] "I"        
 [2,] "A + B"    
 [3,] "A + D"    
 [4,] "A + H"    
 [5,] "B + E"    
 [6,] "B + F"    
 [7,] "C + H"    
 [8,] "C + J"    
 [9,] "C + K"    
[10,] "D + E"    
[11,] "D + F"    
[12,] "F + H"    
[13,] "F + K"    
[14,] "G + I"    
[15,] "G + J"    
[16,] "I + L"    
[17,] "J + L"    
[18,] "A + B + G"
[19,] "A + B + L"
[20,] "A + C + E"
[21,] "A + C + F"
[22,] "A + D + G"
[23,] "A + E + G"
[24,] "A + E + L"
[25,] "B + C + F"
[26,] "B + C + L"
[27,] "B + E + G"
[28,] "B + F + G"
[29,] "B + F + L"
[30,] "C + D + G"
[31,] "C + D + L"
[32,] "C + E + F"
[33,] "C + G + H"
[34,] "C + G + J"
[35,] "C + G + K"
[36,] "C + H + L"
[37,] "C + K + L"
[38,] "D + F + G"
[39,] "D + F + L"
[40,] "F + G + H"
[41,] "F + G + K"
[42,] "F + H + L"
[43,] "G + J + L"
[44,] "G + K + L"
set.seed(2)
nn = rnorm(10)
names(nn) = LETTERS[1:length(nn)]

combos(nn, 3, c(2,2.5))
      [,1]       
 [1,] "B + I"    
 [2,] "C + G"    
 [3,] "F + I"    
 [4,] "B + C + G"
 [5,] "B + E + I"
 [6,] "B + F + I"
 [7,] "B + I + J"
 [8,] "C + D + I"
 [9,] "C + E + G"
[10,] "C + F + G"
[11,] "C + G + H"
[12,] "C + G + J"
[13,] "E + F + I"
[14,] "G + H + I"

答案 2 :(得分:2)

这里是两个和三个数字组合的解决方案。我将它留给读者处理1个数字组合的情况:

numbers <- c(40,60,20,65,45,30,5,70,100,85,75,10)
numnames<- c("A","B","C","D","E","F","G","H","I","J","K","L")

#Take 2 at a time
#Find all combinations of 2 each (output is a matrix)
c2<-combn(numbers, 2)
#find column positions which match the critera
mymatch<-which(colSums(c2)>=90 & colSums(c2)<=110)

#get results of letter comibnations
#combn(numnames, 2) will generate the same squence order as combn(numbers, 2)
answer2<-apply(combn(numnames, 2)[,mymatch], 2, function(t){print(t)
  paste(t, collapse = "+" )})

#Repeat taking 3 at a time
c3<-combn(numbers, 3)
mymatch<-which(colSums(c3)>=90 & colSums(c3)<=110)

answer3<-apply(combn(numnames, 3)[,mymatch], 2, function(t){print(t)
  paste(t, collapse = "+" )})

print(c(answer2, answer3))

[1] "A+B"   "A+D"   "A+H"   "B+E"   "B+F"   "C+H"   "C+J"   "C+K"   "D+E"   "D+F"   "F+H"   "F+K"   "G+I"    
[14] "G+J"   "I+L"   "J+L"   "A+B+G" "A+B+L" "A+C+E" "A+C+F" "A+D+G" "A+E+G" "A+E+L" "B+C+F" "B+C+L" "B+E+G"
[27] "B+F+G" "B+F+L" "C+D+G" "C+D+L" "C+E+F" "C+G+H" "C+G+J" "C+G+K" "C+H+L" "C+K+L" "D+F+G" "D+F+L" "F+G+H"
[40] "F+G+K" "F+H+L" "G+J+L" "G+K+L"

答案 3 :(得分:2)

不生成所有组合的递归选项。可能会提高内存效率,但肯定不会很快。

gen <- function(chosen, rest, lb, ub) {

    new_ub <- ub - sum(chosen)

    rest <- rest[-match(chosen[1L], rest)]

    if (new_ub < 0 || length(chosen) > 2L || !any(rest <= new_ub)) {
        if (sum(chosen) >= lb && sum(chosen) <= ub) {
            return(list(chosen[order(names(chosen))]))
        }
        return(NULL)
    }

    ret <- c()
    for (x in names(rest[rest <= new_ub])) {
        ret <- c(ret, gen(c(rest[x], chosen), rest, lb, ub))
        if (sum(chosen) >= lb && sum(chosen) <= ub) {
            ret <- c(list(chosen[order(names(chosen))]), ret)
        }
    }
    ret
}

ans <- unique(unlist(
    lapply(names(numbers), function(x) gen(numbers[x], rest=numbers, lb=90, ub=110)),
    recursive=FALSE))
unique(sapply(ans, function(x) paste(sort(names(x)), collapse=" + ")))

输出:

 [1] "A + B"     "A + B + G" "A + B + L" "A + C + E" "A + C + F" "A + D"     "A + D + G"
 [8] "A + E + G" "A + E + L" "A + H"     "B + C + F" "B + C + L" "B + E"     "B + E + G"
[15] "B + F"     "B + F + G" "B + F + L" "C + D + G" "C + D + L" "C + E + F" "C + G + H"
[22] "C + G + J" "C + G + K" "C + H"     "C + H + L" "C + J"     "C + K"     "C + K + L"
[29] "D + E"     "D + F"     "D + F + G" "D + F + L" "F + G + H" "F + G + K" "F + H"    
[36] "F + H + L" "F + K"     "G + I"     "G + J"     "G + J + L" "G + K + L" "I"        
[43] "I + L"     "J + L"    

答案 4 :(得分:1)

我将使用combn创建n个元素的所有可能m个组合的列表(m = 1,2,3)。然后,您可以对每个元组中的元素求和,并按所需范围对其进行过滤。 很抱歉,我目前无法使用PC,非常抱歉。