我已经看到了一些解决类似问题的方法,但是它们都需要对要添加到一起的项目数进行迭代。
这是我的目标:从数字列表中找到所有加起来等于一定总数的组合(不替换)。例如,如果我有数字1,1,2,3,5
和总数5
,它应该返回5
,2,3
和1,1,3
。
我试图使用combn
,但它要求您指定每个组合中的项目数。有没有一种方法可以允许任何大小的解决方案集?
答案 0 :(得分:7)
这正是combo/permuteGeneral
(我是作者)中的RcppAlgos
所为的。由于我们在样本向量中重复了特定元素,因此我们将找到符合我们标准的multisets组合。请注意,这与生成具有重复的组合的更常见的情况不同,在重复的组合中,每个元素被允许重复 m 次。对于许多组合生成函数,多集会带来问题,因为引入了重复项,必须加以处理。如果数据量很大,这可能成为代码中的瓶颈。 RcppAlgos
中的函数可以有效地处理这些情况,而不会产生任何重复的结果。我应该提到,还有其他一些很好的库可以很好地处理多集:multicool
和arrangements
。
继续执行当前的任务,我们可以利用comboGeneral
的约束参数来找到满足特定条件的向量的所有组合:
vec <- c(1,1,2,3,5) ## using variables from @r2evans
uni <- unique(vec)
myRep <- rle(vec)$lengths
ans <- 5
library(RcppAlgos)
lapply(seq_along(uni), function(x) {
comboGeneral(uni, x, freqs = myRep,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = ans)
})
[[1]]
[,1]
[1,] 5
[[2]]
[,1] [,2]
[1,] 2 3
[[3]]
[,1] [,2] [,3]
[1,] 1 1 3
[[4]]
[,1] [,2] [,3] [,4] ## no solutions of length 4
这些功能经过高度优化,可以很好地扩展到较大的情况。例如,考虑以下示例,该示例将产生超过3000万个组合:
set.seed(42)
bigVec <- sort(sample(1:30, 40, TRUE))
rle(bigVec)
Run Length Encoding
lengths: int [1:22] 2 1 1 2 1 1 1 2 3 1 ...
values : int [1:22] 1 3 4 5 7 8 9 12 14 15 ...
bigUni <- unique(bigVec)
bigRep <- rle(bigVec)$lengths
bigAns <- 199
len <- 12
comboCount(bigUni, len, freqs = bigRep)
[1] 30904021
所有300000+个结果很快就会返回:
system.time(bigTest <- comboGeneral(bigUni, len, freqs = bigRep,
constraintFun = "sum",
comparisonFun = "==",
limitConstraints = bigAns))
user system elapsed
0.383 0.008 0.390
head(bigTest)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
[1,] 1 1 3 4 5 9 29 29 29 29 30 30
[2,] 1 1 3 4 5 12 26 29 29 29 30 30
[3,] 1 1 3 4 5 12 28 28 28 29 30 30
[4,] 1 1 3 4 5 12 28 28 29 29 29 30
[5,] 1 1 3 4 5 14 25 28 29 29 30 30
[6,] 1 1 3 4 5 14 25 29 29 29 29 30
nrow(bigTest)
[1] 370646
all(rowSums(bigTest) == bigAns)
[1] TRUE
我必须提一提的是,当我遇到如下问题:“找到所有合计为特定数字的组合” 时,我的第一个念头是integer partitions。例如,在相关问题Getting all combinations which sum up to 100 in R中,我们可以使用partitions
库轻松解决。但是,这种方法不能扩展到一般情况(如此处所示),在这种情况下,向量包含特定的重复,或者我们的向量包含不容易转换为等价整数的值(例如,向量(0.1, 0.2, 0.3, 0.4)
可以轻松地视为1:4
,但是将c(3.98486 7.84692 0.0038937 7.4879)
视为整数并随后应用整数分区方法将需要大量的计算能力,因此该方法无用)。
答案 1 :(得分:6)
我接受了您的combn
的想法,并讨论了可能的尺寸。
func = function(x, total){
M = length(x)
y = NULL
total = 15
for (m in 1:M){
tmp = combn(x, m)
ind = which(colSums(tmp) == total)
if (length(ind) > 0){
for (j in 1:length(ind))
y = c(y, list(tmp[,ind[j]]))
}
}
return (unique(lapply(y, sort)))
}
x = c(1,1,2,3,5,8,13)
> func(x, 15)
[[1]]
[1] 2 13
[[2]]
[1] 1 1 13
[[3]]
[1] 2 5 8
[[4]]
[1] 1 1 5 8
[[5]]
[1] 1 1 2 3 8
很显然,随着M
的增长,这会出现问题,因为tmp
会很快变得很大,并且y
的长度无法(也许是?)预先确定。>
答案 2 :(得分:5)
类似于米奇的答案,我们可以在另一种循环机制中使用combn
。我将使用lapply
:
vec <- c(1,1,2,3,5)
ans <- 5
Filter(length, lapply(seq_len(length(vec)),
function(i) {
v <- combn(vec, i)
v[, colSums(v) == ans, drop = FALSE]
}))
# [[1]]
# [,1]
# [1,] 5
# [[2]]
# [,1]
# [1,] 2
# [2,] 3
# [[3]]
# [,1]
# [1,] 1
# [2,] 1
# [3,] 3
您可以省略Filter(length,
部分,尽管它可能返回许多空矩阵。它们很容易处理和忽略,我只是认为从美学角度考虑,将它们删除是可以的。
此方法为您提供一个在每一列中都有多个候选对象的矩阵,所以
ans <- 4
Filter(length, lapply(seq_len(length(vec)),
function(i) {
v <- combn(vec, i)
v[, colSums(v) == ans, drop = FALSE]
}))
# [[1]]
# [,1] [,2]
# [1,] 1 1
# [2,] 3 3
# [[2]]
# [,1]
# [1,] 1
# [2,] 1
# [3,] 2
如果重复出现问题,您可以随时这样做:
Filter(length, lapply(seq_len(length(vec)),
function(i) {
v <- combn(vec, i)
v <- v[, colSums(v) == ans, drop = FALSE]
v[,!duplicated(t(v)),drop = FALSE]
}))
# [[1]]
# [,1]
# [1,] 1
# [2,] 3
# [[2]]
# [,1]
# [1,] 1
# [2,] 1
# [3,] 2
答案 3 :(得分:5)
现在这是一个涉及gtools
的解决方案:
# Creating lists of all permutations of the vector x
df1 <- gtools::permutations(n=length(x),r=length(x),v=1:length(x),repeats.allowed=FALSE)
ls1 <- list()
for(j in 1:nrow(df1)) ls1[[j]] <- x[df1[j,1:ncol(df1)]]
# Taking all cumulative sums and filtering entries equaling our magic number
sumsCum <- t(vapply(1:length(ls1), function(j) cumsum(ls1[[j]]), numeric(length(x))))
indexMN <- which(sumsCum == magicNumber, arr.ind = T)
finalList <- list()
for(j in 1:nrow(indexMN)){
magicRow <- indexMN[j,1]
magicCol <- 1:indexMN[j,2]
finalList[[j]] <- ls1[[magicRow]][magicCol]
}
finalList <- unique(finalList)
其中x = c(1,1,2,3,5)
和magicNumber = 5
。这是初稿,我相信可以在这里和那里进行改进。
答案 4 :(得分:3)
到目前为止,这不是最高效的,但也是最紧凑的:
Uri.fromFile.