【问题标题】:Determining all possible combinations from a list of values that sum to desired total从总和为所需总数的值列表中确定所有可能的组合
【发布时间】:2017-08-29 16:17:05
【问题描述】:

一位朋友向我提出了一个编程问题,关于如何确定一组值的所有可能组合可以相加以获得所需的总数。我有一个解决方案,但它不够优雅(它基本上只是一系列 for 循环和 if 语句)。我确信 dplyr 有一个我想不出的解决方案,因为我知道它有多么有用,但我还没有很擅长它。我将在下面发布问题和我的脚本。

问题: 有一个目标,上面有六个环,每个环的价值不同。这些值是 1、2、3、4、5 或 6。您可以使用多少种不同的环组合来获得正好 9 分?

所以要考虑: 顺序不重要 您可以根据需要使用尽可能少或尽可能多的值 您可以多次获得相同的值(因此 9 1 是一个完全有效的选项)

我曾考虑首先使用 combinat 包中的 combn(),但 combn() 不会替换值。

然后我决定使用一系列嵌套的 for 循环和 if 语句(我将其截断为您最多只能使用 6 个值,因为虽然我可能有空闲时间,但我不是一个即将编写一个允许最多 9 个值的循环)。所以本质上,它运行了 6 个可能值的 for 循环。当我只需要 2 个值而不是 6 时,我将数字 0 包含到可能值列表中表示不尝试(因此 4+5+0+0+0+0 是此循环中的有效输出,但它不会能够做 4+5,因为它总是会尝试添加更多的非零值)。

## Create a vector x with possible values
x = c(1,2,3,4,5,6)  

## Add in value 0 because I need to be able to write this dumb loop that allows many terms to be used, but also allows smaller amounts of terms to be used

x = c(x,0);x

## Creating empty data.frame to input solutions to so that I can check for uniqueness of solution
df = data.frame("a" = as.numeric(),
            "b" = as.numeric(),
            "c" = as.numeric(),
            "d" = as.numeric(),
            "e" = as.numeric(),
            "f" = as.numeric())

for (a in x){
  for (b in x){
    for (c in x){
      for (d in x){
        for (e in x){
          for (f in x){
            m = sum(a,b,c,d,e,f)
            if(m == 9) {
              p = 0
              n = c(a,b,c,d,e,f)
              if (nrow(df) == 0){
                df[1,] = n
              }
              if (nrow(df) >= 1){
                for (i in (1:nrow(df))){
                  if(setequal(n,df[i,]) == TRUE){
                    p = p+1
                    }}
                if(p == 0){
                  df = rbind(df,n)
                }
              }
            }  
          } 
        }
      }
    }
  }
}

## Convert any 0 values to NA
df[df==0] = NA

## Check Solutions
df

我创建了一个空的 data.frame 来存储解决方案,然后在循环中,我创建了一个测试,以查看循环中的新解决方案是否与先前找到的值的组合匹配,如果是,它不会 rbind( ) 到 data.frame。

我确信有一种更好的方法可以做到这一点,它允许动态最大数量的值(因此在这种情况下可以软编码将每个解决方案中的最大值数量更改为 9 而不是我的硬编码 6,或者如果我想要的总数是 5 而不是 9,则将其降为 5)。如果您有任何建议可以减少这种笨重、充满循环的混乱,我们将不胜感激!

【问题讨论】:

标签: r for-loop combinations


【解决方案1】:

你可以试试这个:

    library(modelr)
    library(dplyr)
    range = 1:6
    df = data.frame("a" = range,
              "b" =  range,
              "c" =  range,
              "d" =  range,
              "e" =  range,
              "f" =  range)
    data_grid(df,a,b,c,d,e,f) %>% 
      mutate(sum = a+b+c+d+e+f) %>% 
      filter(sum == 9) %>% nrow

这是函数:

foo <- function(sum_needed, max_value){
  range <- 1:max_value
  df = data.frame("a" = range,
                "b" =  range,
                "c" =  range,
                "d" =  range,
                "e" =  range,
                "f" =  range)
  result <- data_grid(df,a,b,c,d,e,f) %>% 
    mutate(sum = a+b+c+d+e+f) %>% 
    filter(sum == sum_needed) %>% nrow
  return(result)
}
foo(9,6)
#[1] 56

【讨论】:

  • 谢谢!我喜欢 dplyr 的管道,它只是我没有花时间真正习惯使用的东西。值得注意的是,您和下面的 d.b 提出了不同的数字,我认为您的数字是错误的。我认为手头的问题是排列与组合。这里将 1+1+1+1+1+4 识别为不同于 4+1+1+1+1+1。我认为如果管道在过滤器之后结束,那么您将获得所有可行解决方案的 data.frame,然后您找到一种检查唯一组合的方法,应该这样做,而不是直接在非- 独特的组合。
【解决方案2】:
x = 1:6
mysum = 9

#Repeat each element of x as long the sum of repetitions does not exceed mysum
temp = rep(x, floor(mysum/x))

#Calculate total unique combinations of temp that sum up to mysum
sum(sapply(1:max(floor(mysum/x)),
           function(i) sum(rowSums(unique(t(combn(temp, i)))) == mysum)))
#[1] 26

以下应列出所有组合

sapply(1:max(floor(mysum/x)), function(i){
    temp2 = unique(t(combn(temp, i)))
    temp2[rowSums(temp2) == mysum,]
    })

【讨论】:

  • 这看起来很棒!谢谢!我也将尝试使用其他一些数字。最初的问题有值 (16,17,23,24,39,40) 和期望的总数为 100,我只选择了我所做的这些值,因为那个特定的数字集无论如何只有一个答案,所以它不是很有趣(16+16+17+17+17+17)。从外观上看,这个解决方案也应该适用于这组数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多